diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-02-25 18:23:36 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-02-25 18:23:36 -0800 |
| commit | 4aeae430b41ceab422afcfc3f55a07c354f2ed85 (patch) | |
| tree | 0a218ad5644b9528b270c921465c63ed03e30339 | |
| parent | c16cfec2c31119d00086f570cca36227e8b8850e (diff) | |
| parent | 90d2aee3a04cb13dfcab7d37624ffa32025eda1c (diff) | |
Merge bk://gkernel.bkbits.net/misc-2.5
into ppc970.osdl.org:/home/torvalds/v2.5/linux
295 files changed, 26715 insertions, 20294 deletions
diff --git a/Documentation/dvb/avermedia.txt b/Documentation/dvb/avermedia.txt new file mode 100644 index 000000000000..5d8dbdada393 --- /dev/null +++ b/Documentation/dvb/avermedia.txt @@ -0,0 +1,324 @@ + +HOWTO: Get An Avermedia DVB-T working under Linux + ______________________________________________ + + Table of Contents + Assumptions and Introduction + The Avermedia DVB-T + Getting the card going + Getting the Firmware + Receiving DVB-T in Australia + Known Limitations + Further Update + +Assumptions and Introduction + + It is assumed that the reader understands the basic structure + of the Linux Kernel DVB drivers and the general principles of + Digital TV. + + One significant difference between Digital TV and Analogue TV + that the unwary (like myself) should consider is that, + although the component structure of budget DVB-T cards are + substantially similar to Analogue TV cards, they function in + substantially different ways. + + The purpose of an Analogue TV is to receive and display an + Analogue Television signal. An Analogue TV signal (otherwise + known as composite video) is an analogue encoding of a + sequence of image frames (25 per second) rasterised using an + interlacing technique. Interlacing takes two fields to + represent one frame. Computers today are at their best when + dealing with digital signals, not analogue signals and a + composite video signal is about as far removed from a digital + data stream as you can get. Therefore, an Analogue TV card for + a PC has the following purpose: + + * Tune the receiver to receive a broadcast signal + * demodulate the broadcast signal + * demultiplex the analogue video signal and analogue audio + signal (note some countries employ a digital audio signal + embedded within the modulated composite analogue signal - + NICAM.) + * digitize the analogue video signal and make the resulting + datastream available to the data bus. + + The digital datastream from an Analogue TV card is generated + by circuitry on the card and is often presented uncompressed. + For a PAL TV signal encoded at a resolution of 768x576 24-bit + color pixels over 25 frames per second - a fair amount of data + is generated and must be proceesed by the PC before it can be + displayed on the video monitor screen. Some Analogue TV cards + for PC's have onboard MPEG2 encoders which permit the raw + digital data stream to be presented to the PC in an encoded + and compressed form - similar to the form that is used in + Digital TV. + + The purpose of a simple budget digital TV card (DVB-T,C or S) + is to simply: + + * Tune the received to receive a broadcast signal. + * Extract the encoded digital datastream from the broadcast + signal. + * Make the encoded digital datastream (MPEG2) available to + the data bus. + + The significant difference between the two is that the tuner + on the analogue TV card spits out an Analogue signal, whereas + the tuner on the digital TV card spits out a compressed + encoded digital datastream. As the signal is already + digitised, it is trivial to pass this datastream to the PC + databus with minimal additional processing and then extract + the digital video and audio datastreams passing them to the + appropriate software or hardware for decoding and viewing. + _________________________________________________________ + +The Avermedia DVB-T + + The Avermedia DVB-T is a budget PCI DVB card. It has 3 inputs: + + * RF Tuner Input + * Composite Video Input (RCA Jack) + * SVIDEO Input (Mini-DIN) + + The RF Tuner Input is the input to the tuner module of the + card. The Tuner is otherwise known as the "Frontend" . The + Frontend of the Avermedia DVB-T is a Microtune 7202D. A timely + post to the linux-dvb mailing list ascertained that the + Microtune 7202D is supported by the sp887x driver which is + found in the dvb-hw CVS module. + + The DVB-T card is based around the BT878 chip which is a very + common multimedia bridge and often found on Analogue TV cards. + There is no on-board MPEG2 decoder, which means that all MPEG2 + decoding must be done in software, or if you have one, on an + MPEG2 hardware decoding card or chipset. + _________________________________________________________ + +Getting the card going + + In order to fire up the card, it is necessary to load a number + of modules from the DVB driver set. Prior to this it will have + been necessary to download these drivers from the linuxtv CVS + server and compile them successfully. + + Depending on the card's feature set, the Device Driver API for + DVB under Linux will expose some of the following device files + in the /dev tree: + + * /dev/dvb/adapter0/audio0 + * /dev/dvb/adapter0/ca0 + * /dev/dvb/adapter0/demux0 + * /dev/dvb/adapter0/dvr0 + * /dev/dvb/adapter0/frontend0 + * /dev/dvb/adapter0/net0 + * /dev/dvb/adapter0/osd0 + * /dev/dvb/adapter0/video0 + + The primary device nodes that we are interested in (at this + stage) for the Avermedia DVB-T are: + + * /dev/dvb/adapter0/dvr0 + * /dev/dvb/adapter0/frontend0 + + The dvr0 device node is used to read the MPEG2 Data Stream and + the frontend0 node is used to tune the frontend tuner module. + + At this stage, it has not been able to ascertain the + functionality of the remaining device nodes in respect of the + Avermedia DVBT. However, full functionality in respect of + tuning, receiving and supplying the MPEG2 data stream is + possible with the currently available versions of the driver. + It may be possible that additional functionality is available + from the card (i.e. viewing the additional analogue inputs + that the card presents), but this has not been tested yet. If + I get around to this, I'll update the document with whatever I + find. + + To power up the card, load the following modules in the + following order: + + * insmod dvb-core.o + * modprobe bttv.o + * insmod bt878.o + * insmod dvb-bt8xx.o + * insmod sp887x.o + + Insertion of these modules into the running kernel will + activate the appropriate DVB device nodes. It is then possible + to start accessing the card with utilities such as scan, tzap, + dvbstream etc. + + The current version of the frontend module sp887x.o, contains + no firmware drivers?, so the first time you open it with a DVB + utility the driver will try to download some initial firmware + to the card. You will need to download this firmware from the + web, or copy it from an installation of the Windows drivers + that probably came with your card, before you can use it. + + The default Linux filesystem location for this firmware is + /usr/lib/hotplug/firmware/sc_main.mc . + _________________________________________________________ + +Getting the Firmware + + As the firmware for the card is no longer contained within the + driver, it is necessary to extract it from the windows + drivers. + + The Windows drivers for the Avermedia DVB-T can be obtained + from: http://babyurl.com/H3U970 and you can get an application + to extract the firmware from: + http://www.kyz.uklinux.net/cabextract.php. + _________________________________________________________ + +Receiving DVB-T in Australia + + I have no experience of DVB-T in other countries other than + Australia, so I will attempt to explain how it works here in + Melbourne and how this affects the configuration of the DVB-T + card. + + The Digital Broadcasting Australia website has a Reception + locatortool which provides information on transponder channels + and frequencies. My local transmitter happens to be Mount + Dandenong. + + The frequencies broadcast by Mount Dandenong are: + + Table 1. Transponder Frequencies Mount Dandenong, Vic, Aus. + Broadcaster Channel Frequency + ABC VHF 12 226.5 MHz + TEN VHF 11 219.5 MHz + NINE VHF 8 191.625 MHz + SEVEN VHF 6 177.5 MHz + SBS UHF 29 536.5 MHz + + The Scan utility has a set of compiled-in defaults for various + countries and regions, but if they do not suit, or if you have + a pre-compiled scan binary, you can specify a data file on the + command line which contains the transponder frequencies. Here + is a sample file for the above channel transponders: +# Data file for DVB scan program +# +# C Frequency SymbolRate FEC QAM +# S Frequency Polarisation SymbolRate FEC +# T Frequency Bandwidth FEC FEC2 QAM Mode Guard Hier +T 226500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE +T 191625000 7MHz 2/3 NONE QAM64 8k 1/8 NONE +T 219500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE +T 177500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE +T 536500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE + + The defaults for the transponder frequency and other + modulation parameters were obtained from www.dba.org.au. + + When Scan runs, it will output channels.conf information for + any channel's transponders which the card's frontend can lock + onto. (i.e. any whose signal is strong enough at your + antenna). + + Here's my channels.conf file for anyone who's interested: +ABC HDTV:226500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_3_4:QAM_64 +:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:2307:0:560 +ABC TV Melbourne:226500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_3_ +4:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:65 +0:561 +ABC TV 2:226500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_3_4:QAM_64 +:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:562 +ABC TV 3:226500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_3_4:QAM_64 +:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:563 +ABC TV 4:226500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_3_4:QAM_64 +:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:564 +ABC DiG Radio:226500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_3_4:Q +AM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:0:2311:56 +6 +TEN Digital:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:158 +5 +TEN Digital 1:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:Q +AM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:1 +586 +TEN Digital 2:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:Q +AM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:1 +587 +TEN Digital 3:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:Q +AM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:1 +588 +TEN Digital:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:158 +9 +TEN Digital 4:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:Q +AM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:1 +590 +TEN Digital:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:159 +1 +TEN HD:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:QAM_64:T +RANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:514:0:1592 +TEN Digital:219500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:650:159 +3 +Nine Digital:191625000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:QA +M_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:513:660:10 +72 +Nine Digital HD:191625000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2 +:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:512:0:1 +073 +Nine Guide:191625000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_3_4:FEC_1_2:QAM_ +64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_16:HIERARCHY_NONE:514:670:1074 +7 Digital:177500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QAM_6 +4:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:769:770:1328 +7 Digital 1:177500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:769:770:1329 +7 Digital 2:177500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:769:770:1330 +7 Digital 3:177500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:769:770:1331 +7 HD Digital:177500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QA +M_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:833:834:133 +2 +7 Program Guide:177500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3 +:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:865:866: +1334 +SBS HD:536500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QAM_64:T +RANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:102:103:784 +SBS DIGITAL 1:536500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:Q +AM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:161:81:785 +SBS DIGITAL 2:536500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:Q +AM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:162:83:786 +SBS EPG:536500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QAM_64: +TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:163:85:787 +SBS RADIO 1:536500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:0:201:798 +SBS RADIO 2:536500000:INVERSION_OFF:BANDWIDTH_7_MHZ:FEC_2_3:FEC_2_3:QAM +_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_8:HIERARCHY_NONE:0:202:799 + _________________________________________________________ + +Known Limitations + + At present I can say with confidence that the frontend tunes + via /dev/dvb/adapter{x}/frontend0 and supplies an MPEG2 stream + via /dev/dvb/adapter{x}/dvr0. I have not tested the + functionality of any other part of the card yet. I will do so + over time and update this document. + + There are some limitations in the i2c layer due to a returned + error message inconsistency. Although this generates errors in + dmesg and the system logs, it does not appear to affect the + ability of the frontend to function correctly. + _________________________________________________________ + +Further Update + + dvbstream and VideoLAN Client on windows works a treat with + DVB, in fact this is currently serving as my main way of + viewing DVB-T at the moment. Additionally, VLC is happily + decoding HDTV signals, although the PC is dropping the odd + frame here and there - I assume due to processing capability - + as all the decoding is being done under windows in software. + + Many thanks to Nigel Pearson for the updates to this document + since the recent revision of the driver. + + January 29th 2004 diff --git a/Documentation/dvb/cards.txt b/Documentation/dvb/cards.txt index 3742638900cc..5e07f18308b4 100644 --- a/Documentation/dvb/cards.txt +++ b/Documentation/dvb/cards.txt @@ -8,7 +8,7 @@ Hardware supported by the linuxtv.org DVB drivers DVB-S/DVB-C/DVB-T. Thus the frontend drivers are listed seperately. Note 1: There is no guarantee that every frontend driver works - out-of-the box with every card, because of different wiring. + out of the box with every card, because of different wiring. Note 2: The demodulator chips can be used with a variety of tuner/PLL chips, and not all combinations are supported. Often @@ -19,13 +19,13 @@ Hardware supported by the linuxtv.org DVB drivers o Frontends drivers: - dvb_dummy_fe: for testing... DVB-S: - - alps_bsrv2 : Alps BSRV2 (ves1893 demodulator) + - ves1x93 : Alps BSRV2 (ves1893 demodulator) and dbox2 (ves1993) - cx24110 : Conexant HM1221/HM1811 (cx24110 or cx24106 demod, cx24108 PLL) - grundig_29504-491 : Grundig 29504-491 (Philips TDA8083 demodulator), tsa5522 PLL - mt312 : Zarlink mt312 or Mitel vp310 demodulator, sl1935 or tsa5059 PLL - stv0299 : Alps BSRU6 (tsa5059 PLL), LG TDQB-S00x (tsa5059 PLL), LG TDQF-S001F (sl1935 PLL), Philips SU1278 (tua6100 PLL), - Philips SU1278SH (tsa5059 PLL) + Philips SU1278SH (tsa5059 PLL), Samsung TBMU24112IMB DVB-C: - ves1820 : various (ves1820 demodulator, sp5659c or spXXXX PLL) - at76c651 : Atmel AT76c651(B) with DAT7021 PLL @@ -37,6 +37,9 @@ o Frontends drivers: - nxt6000 : Alps TDME7 (MITEL SP5659 PLL), Alps TDED4 (TI ALP510 PLL), Comtech DVBT-6k07 (SP5730 PLL) (NxtWave Communications NXT6000 demodulator) + - sp887x : Microtune 7202D + DVB-S/C/T: + - dst : TwinHan DST Frontend o Cards based on the Phillips saa7146 multimedia PCI bridge chip: @@ -48,16 +51,17 @@ o Cards based on the Phillips saa7146 multimedia PCI bridge chip: - SATELCO Multimedia PCI - KNC1 DVB-S -o Cards based on the B2C2 Inc. FlexCopII: - - Technisat SkyStar2 PCI DVB +o Cards based on the B2C2 Inc. FlexCopII/IIb/III: + - Technisat SkyStar2 PCI DVB card revision 2.3, 2.6B, 2.6C o Cards based on the Conexant Bt8xx PCI bridge: - Pinnacle PCTV Sat DVB - Nebula Electronics DigiTV + - TwinHan DST + - Avermedia DVB-T o Technotrend / Hauppauge DVB USB devices: - Nova USB - - DEC 2000-T - -o Preliminary support for the analog module of the Siemens DVB-C PCI card + - DEC 2000-T, 3000-S, 2540-T +o Experimental support for the analog module of the Siemens DVB-C PCI card diff --git a/Documentation/dvb/faq.txt b/Documentation/dvb/faq.txt index 28e63ec30318..d3457495b541 100644 --- a/Documentation/dvb/faq.txt +++ b/Documentation/dvb/faq.txt @@ -99,11 +99,57 @@ Some very frequently asked questions about linuxtv-dvb If you are using a Technotrend/Hauppauge DVB-C card *without* analog module, you might have to use module parameter adac=-1 (dvb-ttpci.o). -5. The dvb_net device doesn't give me any multicast packets +5. The dvb_net device doesn't give me any packets at all + + Run tcpdump on the dvb0_0 interface. This sets the interface + into promiscous mode so it accepts any packets from the PID + you have configured with the dvbnet utility. Check if there + are any packets with the IP addr and MAC addr you have + configured with ifconfig. + + If tcpdump doesn't give you any output, check the statistics + which ifconfig outputs. (Note: If the MAC address is wrong, + dvb_net won't get any input; thus you have to run tcpdump + before checking the statistics.) If there are no packets at + all then maybe the PID is wrong. If there are error packets, + then either the PID is wrong or the stream does not conform to + the MPE standard (EN 301 192, http://www.etsi.org/). You can + use e.g. dvbsnoop for debugging. + +6. The dvb_net device doesn't give me any multicast packets Check your routes if they include the multicast address range. Additionally make sure that "source validation by reversed path lookup" is disabled: $ "echo 0 > /proc/sys/net/ipv4/conf/dvb0/rp_filter" +7. What the hell are all those modules that need to be loaded? + + For a dvb-ttpci av7110 based full-featured card the following + modules are loaded: + + - videodev: Video4Linux core module. This is the base module that + gives you access to the "analog" tv picture of the av7110 mpeg2 + decoder. + + - v4l2-common: common functions for Video4Linux-2 drivers + + - v4l1-compat: backward compatiblity layer for Video4Linux-1 legacy + applications + + - dvb-core: DVB core module. This provides you with the + /dev/dvb/adapter entries + + - saa7146: SAA7146 core driver. This is need to access any SAA7146 + based card in your system. + + - saa7146_vv: SAA7146 video and vbi functions. These are only needed + for full-featured cards. + + - video-buf: capture helper module for the saa7146_vv driver. This + one is responsible to handle capture buffers. + + - dvb-ttpci: The main driver for AV7110 based, full-featued + DVB-S/C/T cards + eof diff --git a/Documentation/dvb/firmware.txt b/Documentation/dvb/firmware.txt index 3a85c0b5bb1d..0991c0c1f933 100644 --- a/Documentation/dvb/firmware.txt +++ b/Documentation/dvb/firmware.txt @@ -20,7 +20,7 @@ current state: extracted from the Windows driver (Sc_main.mc). - tda1004x: firmware is loaded from path specified in DVB_TDA1004X_FIRMWARE_FILE kernel config - variable (default /etc/dvb/tda1004x.bin); the + variable (default /usr/lib/hotplug/firmware/tda1004x.bin); the firmware binary must be extracted from the windows driver - ttusb-dec: see "ttusb-dec.txt" for details @@ -76,11 +76,15 @@ you want to upload the firmware by hand, however, this might be too fast. Step c) Getting a usable firmware file for the dvb-ttpci driver/av7110 card. You can download the firmware files from -http://www.linuxtv.org/download/dvb/ +http://linuxtv.org/download/dvb/ Please note that in case of the dvb-ttpci driver this is *not* the "Root" file you probably know from the 2.4 DVB releases driver. +The ttpci-firmware utility from linuxtv.org CVS can be used to +convert Dpram and Root files into a usable firmware image. +See dvb-kerrnel/scripts/ in http://linuxtv.org/cvs/. + > wget http://www.linuxtv.org/download/dvb/dvb-ttpci-01.fw gets you the version 01 of the firmware fot the ttpci driver. diff --git a/Documentation/filesystems/hfs.txt b/Documentation/filesystems/hfs.txt new file mode 100644 index 000000000000..bd0fa7704035 --- /dev/null +++ b/Documentation/filesystems/hfs.txt @@ -0,0 +1,83 @@ + +Macintosh HFS Filesystem for Linux +================================== + +HFS stands for ``Hierarchical File System'' and is the filesystem used +by the Mac Plus and all later Macintosh models. Earlier Macintosh +models used MFS (``Macintosh File System''), which is not supported, +MacOS 8.1 and newer support a filesystem called HFS+ that's similar to +HFS but is extended in various areas. Use the hfsplus filesystem driver +to access such filesystems from Linux. + + +Mount options +============= + +When mounting an HFS filesystem, the following options are accepted: + + creator=cccc, type=cccc + Specifies the creator/type values as shown by the MacOS finder + used for creating new files. Default values: '????'. + + uid=n, gid=n + Specifies the user/group that owns all files on the filesystems. + Default: user/group id of the mounting process. + + dir_umask=n, file_umask=n, umask=n + Specifies the umask used for all files , all directories or all + files and directories. Defaults to the umask of the mounting process. + + session=n + Select the CDROM session to mount as HFS filesystem. Defaults to + leaving that decision to the CDROM driver. This option will fail + with anything but a CDROM as underlying devices. + + part=n + Select partition number n from the devices. Does only makes + sense for CDROMS because they can't be partitioned under Linux. + For disk devices the generic partition parsing code does this + for us. Defaults to not parsing the partition table at all. + + quiet + Ignore invalid mount options instead of complaining. + + +Writing to HFS Filesystems +========================== + +HFS is not a UNIX filesystem, thus it does not have the usual features you'd +expect: + + o You can't modify the set-uid, set-gid, sticky or executable bits or the uid + and gid of files. + o You can't create hard- or symlinks, device files, sockets or FIFOs. + +HFS does on the other have the concepts of multiple forks per file. These +non-standard forks are represented as hidden additional files in the normal +filesystems namespace which is kind of a cludge and makes the semantics for +the a little strange: + + o You can't create, delete or rename resource forks of files or the + Finder's metadata. + o They are however created (with default values), deleted and renamed + along with the corresponding data fork or directory. + o Copying files to a different filesystem will loose those attributes + that are essential for MacOS to work. + + +Creating HFS filesystems +=================================== + +The hfsutils package from Robert Leslie contains a program called +hformat that can be used to create HFS filesystem. See +<http://www.mars.org/home/rob/proj/hfs/> for details. + + +Credits +======= + +The HFS drivers was written by Paul H. Hargrovea (hargrove@sccm.Stanford.EDU) +and is now maintained by Roman Zippel (roman@ardistech.com) at Ardis +Technologies. +Roman rewrote large parts of the code and brought in btree routines derived +from Brad Boyer's hfsplus driver (also maintained by Roman now). diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index 01c0d047cd53..1af5712b8f9a 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -187,3 +187,5 @@ Code Seq# Include File Comments 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> 0xCB 00-1F CBM serial IEC bus in development: <mailto:michael.klein@puffin.lb.shuttle.de> +0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ + <mailto:aherrman@de.ibm.com> diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 3be0399d3dc3..9165445a19ec 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -314,8 +314,8 @@ running once the system is up. dtc3181e= [HW,SCSI] earlyprintk= [x86, x86_64] - early_printk=vga - early_printk=serial[,ttySn[,baudrate]] + earlyprintk=vga + earlyprintk=serial[,ttySn[,baudrate]] Append ,keep to not disable it when the real console takes over. diff --git a/Documentation/s390/CommonIO b/Documentation/s390/CommonIO index 575e918bb91a..ce1aac6e1978 100644 --- a/Documentation/s390/CommonIO +++ b/Documentation/s390/CommonIO @@ -8,23 +8,26 @@ Command line parameters Determines whether information on found devices and sensed device characteristics should be shown during startup, i. e. messages of the types - "Detected device 4711 on subchannel 42" and "SenseID: Device 4711 reports: ...". + "Detected device 0.0.4711 on subchannel 0.0.0042" and "SenseID: Device + 0.0.4711 reports: ...". Default is off. * cio_notoper_msg = yes | no - Determines whether messages of the type "Device 4711 became 'not operational'" - should be shown during startup; after startup, they will always be shown. + Determines whether messages of the type "Device 0.0.4711 became 'not + operational'" should be shown during startup; after startup, they will always + be shown. Default is on. -* cio_ignore = <device number> | <range of device numbers>, - <device number> | <range of device numbers>, ... +* cio_ignore = {all} | + {<device> | <range of devices>} | + {!<device> | !<range of devices>} - The given device numbers will be ignored by the common I/O-layer; no detection + The given devices will be ignored by the common I/O-layer; no detection and device sensing will be done on any of those devices. The subchannel to which the device in question is attached will be treated as if no device was attached. @@ -32,12 +35,19 @@ Command line parameters An ignored device can be un-ignored later; see the "/proc entries"-section for details. - The device numbers must be given hexadecimal. + The devices must be given either as bus ids (0.0.abcd) or as hexadecimal + device numbers (0xabcd or abcd, for 2.4 backward compatibility). + You can use the 'all' keyword to ignore all devices. + The '!' operator will cause the I/O-layer to _not_ ignore a device. + The order on the command line is not important. For example, - cio_ignore=0x23-0x42,0x4711 - will ignore all devices with device numbers ranging from 23 to 42 and the - device with device number 4711, if detected. + cio_ignore=0.0.0023-0.0.0042,0.0.4711 + will ignore all devices ranging from 0.0.0023 to 0.0.0042 and the device + 0.0.4711, if detected. + As another example, + cio_ignore=all,!0.0.4711,!0.0.fd00-0.0.fd02 + will ignore all devices but 0.0.4711, 0.0.fd00, 0.0.fd01, 0.0.fd02. By default, no devices are ignored. @@ -47,17 +57,19 @@ Command line parameters * /proc/cio_ignore - Lists the ranges of device numbers which are ignored by common I/O. + Lists the ranges of devices (by bus id) which are ignored by common I/O. You can un-ignore certain or all devices by piping to /proc/cio_ignore. "free all" will un-ignore all ignored devices, - "free <devnorange>, <devnorange>, ..." will un-ignore the specified devices. - - For example, if devices 23 to 42 and 4711 are ignored, - - echo free 0x30-0x32 > /proc/cio_ignore - will un-ignore devices 30 to 32 and will leave devices 23 to 2F, 33 to 42 - and 4711 ignored; - - echo free 0x41 > /proc/cio_ignore will furthermore un-ignore device 41; + "free <device range>, <device range>, ..." will un-ignore the specified + devices. + + For example, if devices 0.0.0023 to 0.0.0042 and 0.0.4711 are ignored, + - echo free 0.0.0030-0.0.0032 > /proc/cio_ignore + will un-ignore devices 0.0.0030 to 0.0.0032 and will leave devices 0.0.0023 + to 0.0.002f, 0.0.0033 to 0.0.0042 and 0.0.4711 ignored; + - echo free 0.0.0041 > /proc/cio_ignore will furthermore un-ignore device + 0.0.0041; - echo free all > /proc/cio_ignore will un-ignore all remaining ignored devices. @@ -66,15 +78,19 @@ Command line parameters available to the system. You can also add ranges of devices to be ignored by piping to - /proc/cio_ignore; "add <devnorange>, <devnorange>, ..." will ignore the + /proc/cio_ignore; "add <device range>, <device range>, ..." will ignore the specified devices. - Note: Already known devices cannot be ignored; this also applies to devices - which are gone after a machine check. + Note: Already known devices cannot be ignored. + + For example, if device 0.0.abcd is already known and all other devices + 0.0.a000-0.0.afff are not known, + "echo add 0.0.a000-0.0.accc, 0.0.af00-0.0.afff > /proc/cio_ignore" + will add 0.0.a000-0.0.abcc, 0.0.abce-0.0.accc and 0.0.af00-0.0.afff to the + list of ignored devices and skip 0.0.abcd. - For example, if device abcd is already known and all other devices a000-afff - are not known, "echo add 0xa000-0xaccc, 0xaf00-0xafff > /proc/cio_ignore" - will add af00-afff to the list of ignored devices and skip a000-accc. + The devices can be specified either by bus id (0.0.abcd) or, for 2.4 backward + compatibilty, by the device number in hexadecimal (0xabcd or abcd). * /proc/s390dbf/cio_*/ (S/390 debug feature) diff --git a/Documentation/s390/driver-model.txt b/Documentation/s390/driver-model.txt index c2517c6a9e52..97f00d47f190 100644 --- a/Documentation/s390/driver-model.txt +++ b/Documentation/s390/driver-model.txt @@ -216,9 +216,17 @@ mind that most drivers will need to implement both a ccwgroup and a ccw driver Channel paths show up, like subchannels, under the channel subsystem root (css0) and are called 'chp0.<chpid>'. They have no driver and do not belong to any bus. +Please note, that unlike /proc/chpids in 2.4, the channel path objects reflect +only the logical state and not the physical state, since we cannot track the +latter consistently due to lacking machine support (we don't need to be aware +of anyway). -status - Can be 'online', 'logically offline' or 'n/a'. +status - Can be 'online' or 'offline'. Piping 'on' or 'off' sets the chpid logically online/offline. + Piping 'on' to an online chpid triggers path reprobing for all devices + the chpid connects to. This can be used to force the kernel to re-use + a channel path the user knows to be online, but the machine hasn't + created a machine check for. 3. System devices diff --git a/MAINTAINERS b/MAINTAINERS index 1ccb1e946719..05bd91451706 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -862,8 +862,8 @@ W: http://www.nyx.net/~arobinso S: Maintained HFS FILESYSTEM -P: Oliver Neukum -M: oliver@neukum.org +P: Roman Zippel +M: zippel@linux-m68k.org L: linux-kernel@vger.kernel.org S: Maintained @@ -889,6 +889,9 @@ rpm: clean spec # Brief documentation of the typical targets used # --------------------------------------------------------------------------- +boards := $(wildcard $(srctree)/arch/$(ARCH)/configs/*_defconfig) +boards := $(notdir $(boards)) + help: @echo 'Cleaning targets:' @echo ' clean - remove most generated files but keep the config' @@ -914,6 +917,11 @@ help: @$(if $(archhelp),$(archhelp),\ echo ' No architecture specific help defined for $(ARCH)') @echo '' + @$(if $(boards), \ + $(foreach b, $(boards), \ + printf " %-24s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));) \ + echo '') + @echo ' make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build' @echo ' make O=dir [targets] Locate all output files in "dir", including .config' @echo ' make C=1 [targets] Check all c source with checker tool' diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d509e59d54fd..cbc453953d47 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -336,14 +336,12 @@ config CPU_FREQ_SA1100 bool depends on CPU_FREQ && SA1100_LART default y - select CPU_FREQ_DEFAULT_GOV_USERSPACE select CPU_FREQ_24_API if SYSCTL config CPU_FREQ_SA1110 bool depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3) default y - select CPU_FREQ_DEFAULT_GOV_USERSPACE select CPU_FREQ_24_API if SYSCTL config CPU_FREQ_INTEGRATOR diff --git a/arch/i386/kernel/cpu/cpufreq/elanfreq.c b/arch/i386/kernel/cpu/cpufreq/elanfreq.c index a15f1ee967ad..b6e8482a0d06 100644 --- a/arch/i386/kernel/cpu/cpufreq/elanfreq.c +++ b/arch/i386/kernel/cpu/cpufreq/elanfreq.c @@ -199,6 +199,7 @@ static int elanfreq_cpu_init(struct cpufreq_policy *policy) { struct cpuinfo_x86 *c = cpu_data; unsigned int i; + int result; /* capability check */ if ((c->x86_vendor != X86_VENDOR_AMD) || @@ -220,7 +221,20 @@ static int elanfreq_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->cur = elanfreq_get_cpu_frequency(); - return cpufreq_frequency_table_cpuinfo(policy, &elanfreq_table[0]);; + result = cpufreq_frequency_table_cpuinfo(policy, elanfreq_table); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(elanfreq_table, policy->cpu); + + return 0; +} + + +static int elanfreq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; } @@ -245,12 +259,20 @@ __setup("elanfreq=", elanfreq_setup); #endif +static struct freq_attr* elanfreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + + static struct cpufreq_driver elanfreq_driver = { .verify = elanfreq_verify, .target = elanfreq_target, .init = elanfreq_cpu_init, + .exit = elanfreq_cpu_exit, .name = "elanfreq", .owner = THIS_MODULE, + .attr = elanfreq_attr, }; diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index 5a2478dd2b0b..3c1a30c1d918 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c @@ -234,6 +234,8 @@ static int __init longhaul_get_ranges (void) case 2: rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + //TODO: Nehemiah may have borken MaxMHzBR. + // need to extrapolate from FSB. invalue = longhaul.bits.MaxMHzBR; if (longhaul.bits.MaxMHzBR4) invalue += 16; @@ -245,7 +247,16 @@ static int __init longhaul_get_ranges (void) else minmult = multipliers[invalue]; - fsb = guess_fsb(maxmult); + switch (longhaul.bits.MaxMHzFSB) { + case 0x0: fsb=133; + break; + case 0x1: fsb=100; + break; + case 0x2: printk (KERN_INFO PFX "Invalid (reserved) FSB!\n"); + return -EINVAL; + case 0x3: fsb=66; + break; + } break; } @@ -438,15 +449,34 @@ static int __init longhaul_cpu_init (struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->cur = calc_speed (longhaul_get_cpu_mult(), fsb); - return cpufreq_frequency_table_cpuinfo(policy, longhaul_table); + ret = cpufreq_frequency_table_cpuinfo(policy, longhaul_table); + if (ret) + return ret; + + cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); + + return 0; } +static int longhaul_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr* longhaul_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + static struct cpufreq_driver longhaul_driver = { .verify = longhaul_verify, .target = longhaul_target, .init = longhaul_cpu_init, + .exit = longhaul_cpu_exit, .name = "longhaul", .owner = THIS_MODULE, + .attr = longhaul_attr, }; static int __init longhaul_init (void) diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k6.c b/arch/i386/kernel/cpu/cpufreq/powernow-k6.c index bfb2b6b5468c..83b0d2e527fd 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k6.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k6.c @@ -140,6 +140,7 @@ static int powernow_k6_target (struct cpufreq_policy *policy, static int powernow_k6_cpu_init(struct cpufreq_policy *policy) { unsigned int i; + int result; if (policy->cpu != 0) return -ENODEV; @@ -161,7 +162,13 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->cur = busfreq * max_multiplier; - return cpufreq_frequency_table_cpuinfo(policy, &clock_ratio[0]); + result = cpufreq_frequency_table_cpuinfo(policy, clock_ratio); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(clock_ratio, policy->cpu); + + return 0; } @@ -172,9 +179,14 @@ static int powernow_k6_cpu_exit(struct cpufreq_policy *policy) if (i==max_multiplier) powernow_k6_set_state(i); } - return 0; + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; } +static struct freq_attr* powernow_k6_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; static struct cpufreq_driver powernow_k6_driver = { .verify = powernow_k6_verify, @@ -183,6 +195,7 @@ static struct cpufreq_driver powernow_k6_driver = { .exit = powernow_k6_cpu_exit, .name = "powernow-k6", .owner = THIS_MODULE, + .attr = powernow_k6_attr, }; diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c index db830db703fb..c77c9637592d 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c @@ -389,15 +389,29 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy) policy->cur = maximum_speed; + cpufreq_frequency_table_get_attr(powernow_table, policy->cpu); + return cpufreq_frequency_table_cpuinfo(policy, powernow_table); } +static int powernow_cpu_exit (struct cpufreq_policy *policy) { + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr* powernow_table_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + static struct cpufreq_driver powernow_driver = { .verify = powernow_verify, .target = powernow_target, .init = powernow_cpu_init, + .exit = powernow_cpu_exit, .name = "powernow-k7", .owner = THIS_MODULE, + .attr = powernow_table_attr, }; static int __init powernow_init (void) diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c index b84559800166..0fd2bc161bc8 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c @@ -802,6 +802,8 @@ powernowk8_cpu_init(struct cpufreq_policy *pol) return -EINVAL; } + cpufreq_frequency_table_get_attr(powernow_table, pol->cpu); + printk(KERN_INFO PFX "cpu_init done, current fid 0x%x, vid 0x%x\n", currfid, currvid); @@ -813,12 +815,19 @@ static int __exit powernowk8_cpu_exit (struct cpufreq_policy *pol) if (pol->cpu != 0) return -EINVAL; + cpufreq_frequency_table_put_attr(pol->cpu); + if (powernow_table) kfree(powernow_table); return 0; } +static struct freq_attr* powernow_k8_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + static struct cpufreq_driver cpufreq_amd64_driver = { .verify = powernowk8_verify, .target = powernowk8_target, @@ -826,6 +835,7 @@ static struct cpufreq_driver cpufreq_amd64_driver = { .exit = powernowk8_cpu_exit, .name = "powernow-k8", .owner = THIS_MODULE, + .attr = powernow_k8_attr, }; diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c index aeeaefd33789..4e5a253176d0 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c @@ -303,16 +303,37 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->cur = speed; - return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]); + result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu); + + return 0; +} + + +static int speedstep_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; } +static struct freq_attr* speedstep_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + + static struct cpufreq_driver speedstep_driver = { .name = "speedstep-ich", .verify = speedstep_verify, .target = speedstep_target, .init = speedstep_cpu_init, + .exit = speedstep_cpu_exit, .owner = THIS_MODULE, + .attr = speedstep_attr, }; diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c b/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c index 157899d60d7a..50e41112b3e6 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c @@ -286,7 +286,20 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->cur = speed; - return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]); + result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu); + + return 0; +} + + +static int speedstep_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; } @@ -300,14 +313,20 @@ static int speedstep_resume(struct cpufreq_policy *policy) return result; } +static struct freq_attr* speedstep_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; static struct cpufreq_driver speedstep_driver = { .name = "speedstep-smi", .verify = speedstep_verify, .target = speedstep_target, .init = speedstep_cpu_init, + .exit = speedstep_cpu_exit, .resume = speedstep_resume, .owner = THIS_MODULE, + .attr = speedstep_attr, }; /** diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 20dd1731eee0..7aea2c8f5a44 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -11,7 +11,6 @@ * This file handles the architecture-dependent parts of process handling.. */ -#define __KERNEL_SYSCALLS__ #include <stdarg.h> #include <linux/errno.h> @@ -23,7 +22,6 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/stddef.h> -#include <linux/unistd.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/user.h> diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile index a174cc5c6272..2675ab3fbe8a 100644 --- a/arch/ia64/Makefile +++ b/arch/ia64/Makefile @@ -104,9 +104,12 @@ include/asm-ia64/.offsets.h.stamp: boot: lib/lib.a vmlinux $(Q)$(MAKE) $(build)=$(boot) $@ +install: vmlinux.gz + sh $(srctree)/arch/ia64/install.sh $(KERNELRELEASE) $< System.map "$(INSTALL_PATH)" define archhelp echo '* compressed - Build compressed kernel image' + echo ' install - Install compressed kernel image' echo ' boot - Build vmlinux and bootloader for Ski simulator' echo '* unwcheck - Check vmlinux for invalid unwind info' endef diff --git a/arch/ia64/defconfig b/arch/ia64/defconfig index 9846f6822ae6..246e3f393e64 100644 --- a/arch/ia64/defconfig +++ b/arch/ia64/defconfig @@ -68,6 +68,7 @@ CONFIG_IA64_L1_CACHE_SHIFT=7 # CONFIG_NUMA is not set CONFIG_VIRTUAL_MEM_MAP=y CONFIG_IA64_MCA=y +# CONFIG_IA64_CYCLONE is not set CONFIG_PM=y CONFIG_IOSAPIC=y CONFIG_FORCE_MAX_ZONEORDER=18 @@ -587,7 +588,6 @@ CONFIG_EFI_RTC=y # # CONFIG_FTAPE is not set CONFIG_AGP=m -CONFIG_AGP_I460=m CONFIG_AGP_HP_ZX1=m CONFIG_DRM=y # CONFIG_DRM_TDFX is not set @@ -778,7 +778,9 @@ CONFIG_FB=y # CONFIG_FB_IMSTT is not set CONFIG_FB_RIVA=m # CONFIG_FB_MATROX is not set +# CONFIG_FB_RADEON_OLD is not set CONFIG_FB_RADEON=y +# CONFIG_FB_RADEON_DEBUG is not set # CONFIG_FB_ATY128 is not set # CONFIG_FB_ATY is not set # CONFIG_FB_SIS is not set diff --git a/arch/ia64/install.sh b/arch/ia64/install.sh new file mode 100644 index 000000000000..929e780026d1 --- /dev/null +++ b/arch/ia64/install.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# +# arch/ia64/install.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install" script for ia64 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install path (blank if root directory) +# + +# User may have a custom install script + +if [ -x ~/bin/installkernel ]; then exec ~/bin/installkernel "$@"; fi +if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi + +# Default install - same as make zlilo + +if [ -f $4/vmlinuz ]; then + mv $4/vmlinuz $4/vmlinuz.old +fi + +if [ -f $4/System.map ]; then + mv $4/System.map $4/System.old +fi + +cat $2 > $4/vmlinuz +cp $3 $4/System.map + +test -x /usr/sbin/elilo && /usr/sbin/elilo diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index 5ff97681c95c..3af08d2e7d48 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -23,15 +23,15 @@ obj-$(CONFIG_IA64_CYCLONE) += cyclone.o # The gate DSO image is built using a special linker script. targets += gate.so gate-syms.o +extra-y += gate.so gate-syms.o gate.lds.s gate.o + AFLAGS_gate.lds.o += -P -C -U$(ARCH) -arch/ia64/kernel/gate.lds.s: %.s: %.S scripts FORCE - $(call if_changed_dep,as_s_S) quiet_cmd_gate = GATE $@ cmd_gate = $(CC) -nostdlib $(GATECFLAGS_$(@F)) -Wl,-T,$(filter-out FORCE,$^) -o $@ GATECFLAGS_gate.so = -shared -s -Wl,-soname=linux-gate.so.1 -$(obj)/gate.so: $(src)/gate.lds.s $(obj)/gate.o FORCE +$(obj)/gate.so: $(obj)/gate.lds.s $(obj)/gate.o FORCE $(call if_changed,gate) $(obj)/built-in.o: $(obj)/gate-syms.o diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index cbcb2b24fe8f..0432abe563c3 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -75,12 +75,25 @@ ia64_get_scratch_nat_bits (struct pt_regs *pt, unsigned long scratch_unat) ({ \ unsigned long bit = ia64_unat_pos(&pt->r##first); \ unsigned long mask = ((1UL << (last - first + 1)) - 1) << first; \ - (ia64_rotl(unat, first) >> bit) & mask; \ + unsigned long dist; \ + if (bit < first) \ + dist = 64 + bit - first; \ + else \ + dist = bit - first; \ + ia64_rotr(unat, dist) & mask; \ }) unsigned long val; - val = GET_BITS( 1, 3, scratch_unat); - val |= GET_BITS(12, 15, scratch_unat); + /* + * Registers that are stored consecutively in struct pt_regs can be handled in + * parallel. If the register order in struct_pt_regs changes, this code MUST be + * updated. + */ + val = GET_BITS( 1, 1, scratch_unat); + val |= GET_BITS( 2, 3, scratch_unat); + val |= GET_BITS(12, 13, scratch_unat); + val |= GET_BITS(14, 14, scratch_unat); + val |= GET_BITS(15, 15, scratch_unat); val |= GET_BITS( 8, 11, scratch_unat); val |= GET_BITS(16, 31, scratch_unat); return val; @@ -96,16 +109,29 @@ ia64_get_scratch_nat_bits (struct pt_regs *pt, unsigned long scratch_unat) unsigned long ia64_put_scratch_nat_bits (struct pt_regs *pt, unsigned long nat) { +# define PUT_BITS(first, last, nat) \ + ({ \ + unsigned long bit = ia64_unat_pos(&pt->r##first); \ + unsigned long mask = ((1UL << (last - first + 1)) - 1) << first; \ + long dist; \ + if (bit < first) \ + dist = 64 + bit - first; \ + else \ + dist = bit - first; \ + ia64_rotl(nat & mask, dist); \ + }) unsigned long scratch_unat; -# define PUT_BITS(first, last, nat) \ - ({ \ - unsigned long bit = ia64_unat_pos(&pt->r##first); \ - unsigned long mask = ((1UL << (last - first + 1)) - 1) << bit; \ - (ia64_rotr(nat, first) << bit) & mask; \ - }) - scratch_unat = PUT_BITS( 1, 3, nat); - scratch_unat |= PUT_BITS(12, 15, nat); + /* + * Registers that are stored consecutively in struct pt_regs can be handled in + * parallel. If the register order in struct_pt_regs changes, this code MUST be + * updated. + */ + scratch_unat = PUT_BITS( 1, 1, nat); + scratch_unat |= PUT_BITS( 2, 3, nat); + scratch_unat |= PUT_BITS(12, 13, nat); + scratch_unat |= PUT_BITS(14, 14, nat); + scratch_unat |= PUT_BITS(15, 15, nat); scratch_unat |= PUT_BITS( 8, 11, nat); scratch_unat |= PUT_BITS(16, 31, nat); diff --git a/arch/ia64/kernel/sal.c b/arch/ia64/kernel/sal.c index 7298e9a4863f..3ba6856c3ac7 100644 --- a/arch/ia64/kernel/sal.c +++ b/arch/ia64/kernel/sal.c @@ -106,7 +106,7 @@ ia64_sal_init (struct ia64_sal_systab *systab) /* * revisions are coded in BCD, so %x does the job for us */ - printk(KERN_INFO "SAL v%x.%02x: oem=%.32s, product=%.32s\n", + printk(KERN_INFO "SAL v%x.%x: oem=%.32s, product=%.32s\n", systab->sal_rev_major, systab->sal_rev_minor, systab->oem_id, systab->product_id); diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c index 0055272e0163..76da58c7c820 100644 --- a/arch/ia64/kernel/signal.c +++ b/arch/ia64/kernel/signal.c @@ -347,12 +347,6 @@ setup_sigcontext (struct sigcontext *sc, sigset_t *mask, struct sigscratch *scr) __copy_to_user(&sc->sc_fr[32], current->thread.fph, 96*16); } - /* - * Note: sw->ar_unat is UNDEFINED unless the process is being - * PTRACED. However, this is OK because the NaT bits of the - * preserved registers (r4-r7) are never being looked at by - * the signal handler (registers r4-r7 are used instead). - */ nat = ia64_get_scratch_nat_bits(&scr->pt, scr->scratch_unat); err = __put_user(flags, &sc->sc_flags); diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index d1306576f8d8..1f9a01c304dd 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -40,6 +40,125 @@ struct early_node_data { static struct early_node_data mem_data[NR_NODES] __initdata; +/** + * reassign_cpu_only_nodes - called from find_memory to move CPU-only nodes to a memory node + * + * This function will move nodes with only CPUs (no memory) + * to a node with memory which is at the minimum numa_slit distance. + * Any reassigments will result in the compression of the nodes + * and renumbering the nid values where appropriate. + * The static declarations below are to avoid large stack size which + * makes the code not re-entrant. + */ +static void __init reassign_cpu_only_nodes(void) +{ + struct node_memblk_s *p; + int i, j, k, nnode, nid, cpu, cpunid; + u8 cslit, slit; + static DECLARE_BITMAP(nodes_with_mem, NR_NODES) __initdata; + static u8 numa_slit_fix[MAX_NUMNODES * MAX_NUMNODES] __initdata; + static int node_flip[NR_NODES] __initdata; + + for (nnode = 0, p = &node_memblk[0]; p < &node_memblk[num_node_memblks]; p++) + if (!test_bit(p->nid, (void *) nodes_with_mem)) { + set_bit(p->nid, (void *) nodes_with_mem); + nnode++; + } + + /* + * All nids with memory. + */ + if (nnode == numnodes) + return; + + /* + * Change nids and attempt to migrate CPU-only nodes + * to the best numa_slit (closest neighbor) possible. + * For reassigned CPU nodes a nid can't be arrived at + * until after this loop because the target nid's new + * identity might not have been established yet. So + * new nid values are fabricated above numnodes and + * mapped back later to their true value. + */ + for (nid = 0, i = 0; i < numnodes; i++) { + if (test_bit(i, (void *) nodes_with_mem)) { + /* + * Save original nid value for numa_slit + * fixup and node_cpuid reassignments. + */ + node_flip[nid] = i; + + if (i == nid) { + nid++; + continue; + } + + for (p = &node_memblk[0]; p < &node_memblk[num_node_memblks]; p++) + if (p->nid == i) + p->nid = nid; + + cpunid = nid; + nid++; + } else + cpunid = numnodes; + + for (cpu = 0; cpu < NR_CPUS; cpu++) + if (node_cpuid[cpu].nid == i) { + /* For nodes not being reassigned just fix the cpu's nid. */ + if (cpunid < numnodes) { + node_cpuid[cpu].nid = cpunid; + continue; + } + + /* + * For nodes being reassigned, find best node by + * numa_slit information and then make a temporary + * nid value based on current nid and numnodes. + */ + for (slit = 0xff, k = numnodes + numnodes, j = 0; j < numnodes; j++) + if (i == j) + continue; + else if (test_bit(j, (void *) nodes_with_mem)) { + cslit = numa_slit[i * numnodes + j]; + if (cslit < slit) { + k = numnodes + j; + slit = cslit; + } + } + + node_cpuid[cpu].nid = k; + } + } + + /* + * Fixup temporary nid values for CPU-only nodes. + */ + for (cpu = 0; cpu < NR_CPUS; cpu++) + if (node_cpuid[cpu].nid == (numnodes + numnodes)) + node_cpuid[cpu].nid = nnode - 1; + else + for (i = 0; i < nnode; i++) + if (node_flip[i] == (node_cpuid[cpu].nid - numnodes)) { + node_cpuid[cpu].nid = i; + break; + } + + /* + * Fix numa_slit by compressing from larger + * nid array to reduced nid array. + */ + for (i = 0; i < nnode; i++) + for (j = 0; j < nnode; j++) + numa_slit_fix[i * nnode + j] = + numa_slit[node_flip[i] * numnodes + node_flip[j]]; + + memcpy(numa_slit, numa_slit_fix, sizeof (numa_slit)); + + numnodes = nnode; + + return; +} + /* * To prevent cache aliasing effects, align per-node structures so that they * start at addresses that are strided by node number. @@ -301,6 +420,9 @@ void __init find_memory(void) min_low_pfn = -1; max_low_pfn = 0; + if (numnodes > 1) + reassign_cpu_only_nodes(); + /* These actually end up getting called by call_pernode_memory() */ efi_memmap_walk(filter_rsvd_memory, build_node_maps); efi_memmap_walk(filter_rsvd_memory, find_pernode_space); diff --git a/arch/ia64/sn/io/machvec/pci_bus_cvlink.c b/arch/ia64/sn/io/machvec/pci_bus_cvlink.c index b045a3ca147a..6b70818a6726 100644 --- a/arch/ia64/sn/io/machvec/pci_bus_cvlink.c +++ b/arch/ia64/sn/io/machvec/pci_bus_cvlink.c @@ -384,6 +384,80 @@ sn_pci_fixup_slot(struct pci_dev *dev) return 0; } +#ifdef CONFIG_HOTPLUG_PCI_SGI + +void +sn_dma_flush_clear(struct sn_flush_device_list *dma_flush_list, + unsigned long start, unsigned long end) +{ + + int i; + + dma_flush_list->pin = -1; + dma_flush_list->bus = -1; + dma_flush_list->slot = -1; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if ((dma_flush_list->bar_list[i].start == start) && + (dma_flush_list->bar_list[i].end == end)) { + dma_flush_list->bar_list[i].start = 0; + dma_flush_list->bar_list[i].end = 0; + break; + } + +} + +/* + * sn_pci_unfixup_slot() - This routine frees a slot's resources + * consistent with the Linux PCI abstraction layer. Resources released + * back to our PCI provider include PIO maps to BAR space and interrupt + * objects. + */ +void +sn_pci_unfixup_slot(struct pci_dev *dev) +{ + struct sn_device_sysdata *device_sysdata; + vertex_hdl_t vhdl; + pciio_intr_t intr_handle; + unsigned int irq; + unsigned long size; + int idx; + + device_sysdata = SN_DEVICE_SYSDATA(dev); + + vhdl = device_sysdata->vhdl; + + if (device_sysdata->dma_flush_list) + for (idx = 0; idx < PCI_ROM_RESOURCE; idx++) { + size = dev->resource[idx].end - + dev->resource[idx].start; + if (size == 0) continue; + + sn_dma_flush_clear(device_sysdata->dma_flush_list, + dev->resource[idx].start, + dev->resource[idx].end); + } + + intr_handle = device_sysdata->intr_handle; + if (intr_handle) { + extern void unregister_pcibr_intr(int, pcibr_intr_t); + irq = intr_handle->pi_irq; + irqpdaindr->device_dev[irq] = NULL; + unregister_pcibr_intr(irq, (pcibr_intr_t) intr_handle); + pciio_intr_disconnect(intr_handle); + pciio_intr_free(intr_handle); + } + + for (idx = 0; idx < PCI_ROM_RESOURCE; idx++) { + if (device_sysdata->pio_map[idx]) { + pciio_piomap_done (device_sysdata->pio_map[idx]); + pciio_piomap_free (device_sysdata->pio_map[idx]); + } + } + +} +#endif /* CONFIG_HOTPLUG_PCI_SGI */ + struct sn_flush_nasid_entry flush_nasid_list[MAX_NASIDS]; /* Initialize the data structures for flushing write buffers after a PIO read. @@ -534,6 +608,7 @@ sn_dma_flush_init(unsigned long start, unsigned long end, int idx, int pin, int return p; } + /* * linux_bus_cvlink() Creates a link between the Linux PCI Bus number * to the actual hardware component that it represents: @@ -774,7 +849,7 @@ sn_pci_init (void) printk(KERN_WARNING "sn_pci_fixup: sn_pci_fixup_bus fails : error %d\n", ret); - return; + return 0; } } @@ -805,7 +880,7 @@ sn_pci_init (void) printk(KERN_WARNING "sn_pci_fixup: sn_pci_fixup_slot fails : error %d\n", ret); - return; + return 0; } } diff --git a/arch/ia64/sn/io/sn2/pcibr/pcibr_dvr.c b/arch/ia64/sn/io/sn2/pcibr/pcibr_dvr.c index 3571229b5a96..4dc0ba4ecf9d 100644 --- a/arch/ia64/sn/io/sn2/pcibr/pcibr_dvr.c +++ b/arch/ia64/sn/io/sn2/pcibr/pcibr_dvr.c @@ -629,6 +629,8 @@ pcibr_driver_reg_callback(vertex_hdl_t pconn_vhdl, pcibr_soft = pcibr_soft_get(pcibr_vhdl); pcibr_info->f_att_det_error = error; + +#ifdef CONFIG_HOTPLUG_PCI_SGI pcibr_soft->bs_slot[slot].slot_status &= ~SLOT_STATUS_MASK; if (error) { @@ -636,6 +638,7 @@ pcibr_driver_reg_callback(vertex_hdl_t pconn_vhdl, } else { pcibr_soft->bs_slot[slot].slot_status |= SLOT_STARTUP_CMPLT; } +#endif /* CONFIG_HOTPLUG_PCI_SGI */ } /* @@ -668,6 +671,7 @@ pcibr_driver_unreg_callback(vertex_hdl_t pconn_vhdl, pcibr_soft = pcibr_soft_get(pcibr_vhdl); pcibr_info->f_att_det_error = error; +#ifdef CONFIG_HOTPLUG_PCI_SGI pcibr_soft->bs_slot[slot].slot_status &= ~SLOT_STATUS_MASK; if (error) { @@ -675,6 +679,7 @@ pcibr_driver_unreg_callback(vertex_hdl_t pconn_vhdl, } else { pcibr_soft->bs_slot[slot].slot_status |= SLOT_SHUTDOWN_CMPLT; } +#endif /* CONFIG_HOTPLUG_PCI_SGI */ } /* diff --git a/arch/ia64/sn/io/sn2/pcibr/pcibr_slot.c b/arch/ia64/sn/io/sn2/pcibr/pcibr_slot.c index 0e317b52af82..7537175d12f4 100644 --- a/arch/ia64/sn/io/sn2/pcibr/pcibr_slot.c +++ b/arch/ia64/sn/io/sn2/pcibr/pcibr_slot.c @@ -16,6 +16,7 @@ #include <asm/sn/pci/pcibr_private.h> #include <asm/sn/pci/pci_defs.h> #include <asm/sn/sn_private.h> +#include <asm/sn/sn_sal.h> extern pcibr_info_t pcibr_info_get(vertex_hdl_t); extern int pcibr_widget_to_bus(vertex_hdl_t pcibr_vhdl); @@ -58,6 +59,411 @@ void do_pcibr_config_set(cfg_p, unsigned, unsigned, uint64_t); int max_splittrans_to_numbuf[MAX_SPLIT_TABLE] = {1, 2, 3, 4, 8, 12, 16, 32}; int max_readcount_to_bufsize[MAX_READCNT_TABLE] = {512, 1024, 2048, 4096 }; +#ifdef CONFIG_HOTPLUG_PCI_SGI + +/* + * PCI slot manipulation errors from the system controller, and their + * associated descriptions + */ +#define SYSCTL_REQERR_BASE (-106000) +#define SYSCTL_PCI_ERROR_BASE (SYSCTL_REQERR_BASE - 100) +#define SYSCTL_PCIX_ERROR_BASE (SYSCTL_REQERR_BASE - 3000) + +struct sysctl_pci_error_s { + + int error; + char *msg; + +} sysctl_pci_errors[] = { + +#define SYSCTL_PCI_UNINITIALIZED (SYSCTL_PCI_ERROR_BASE - 0) + { SYSCTL_PCI_UNINITIALIZED, "module not initialized" }, + +#define SYSCTL_PCI_UNSUPPORTED_BUS (SYSCTL_PCI_ERROR_BASE - 1) + { SYSCTL_PCI_UNSUPPORTED_BUS, "unsupported bus" }, + +#define SYSCTL_PCI_UNSUPPORTED_SLOT (SYSCTL_PCI_ERROR_BASE - 2) + { SYSCTL_PCI_UNSUPPORTED_SLOT, "unsupported slot" }, + +#define SYSCTL_PCI_POWER_NOT_OKAY (SYSCTL_PCI_ERROR_BASE - 3) + { SYSCTL_PCI_POWER_NOT_OKAY, "slot power not okay" }, + +#define SYSCTL_PCI_CARD_NOT_PRESENT (SYSCTL_PCI_ERROR_BASE - 4) + { SYSCTL_PCI_CARD_NOT_PRESENT, "card not present" }, + +#define SYSCTL_PCI_POWER_LIMIT (SYSCTL_PCI_ERROR_BASE - 5) + { SYSCTL_PCI_POWER_LIMIT, "power limit reached - some cards not powered up" }, + +#define SYSCTL_PCI_33MHZ_ON_66MHZ (SYSCTL_PCI_ERROR_BASE - 6) + { SYSCTL_PCI_33MHZ_ON_66MHZ, "cannot add a 33 MHz card to an active 66 MHz bus" }, + +#define SYSCTL_PCI_INVALID_ORDER (SYSCTL_PCI_ERROR_BASE - 7) + { SYSCTL_PCI_INVALID_ORDER, "invalid reset order" }, + +#define SYSCTL_PCI_DOWN_33MHZ (SYSCTL_PCI_ERROR_BASE - 8) + { SYSCTL_PCI_DOWN_33MHZ, "cannot power down a 33 MHz card on an active bus" }, + +#define SYSCTL_PCI_RESET_33MHZ (SYSCTL_PCI_ERROR_BASE - 9) + { SYSCTL_PCI_RESET_33MHZ, "cannot reset a 33 MHz card on an active bus" }, + +#define SYSCTL_PCI_SLOT_NOT_UP (SYSCTL_PCI_ERROR_BASE - 10) + { SYSCTL_PCI_SLOT_NOT_UP, "cannot reset a slot that is not powered up" }, + +#define SYSCTL_PCIX_UNINITIALIZED (SYSCTL_PCIX_ERROR_BASE - 0) + { SYSCTL_PCIX_UNINITIALIZED, "module not initialized" }, + +#define SYSCTL_PCIX_UNSUPPORTED_BUS (SYSCTL_PCIX_ERROR_BASE - 1) + { SYSCTL_PCIX_UNSUPPORTED_BUS, "unsupported bus" }, + +#define SYSCTL_PCIX_UNSUPPORTED_SLOT (SYSCTL_PCIX_ERROR_BASE - 2) + { SYSCTL_PCIX_UNSUPPORTED_SLOT, "unsupported slot" }, + +#define SYSCTL_PCIX_POWER_NOT_OKAY (SYSCTL_PCIX_ERROR_BASE - 3) + { SYSCTL_PCIX_POWER_NOT_OKAY, "slot power not okay" }, + +#define SYSCTL_PCIX_CARD_NOT_PRESENT (SYSCTL_PCIX_ERROR_BASE - 4) + { SYSCTL_PCIX_CARD_NOT_PRESENT, "card not present" }, + +#define SYSCTL_PCIX_POWER_LIMIT (SYSCTL_PCIX_ERROR_BASE - 5) + { SYSCTL_PCIX_POWER_LIMIT, "power limit reached - some cards not powered up" }, + +#define SYSCTL_PCIX_33MHZ_ON_66MHZ (SYSCTL_PCIX_ERROR_BASE - 6) + { SYSCTL_PCIX_33MHZ_ON_66MHZ, "cannot add a 33 MHz card to an active 66 MHz bus" }, + +#define SYSCTL_PCIX_PCI_ON_PCIX (SYSCTL_PCIX_ERROR_BASE - 7) + { SYSCTL_PCIX_PCI_ON_PCIX, "cannot add a PCI card to an active PCIX bus" }, + +#define SYSCTL_PCIX_ANYTHING_ON_133MHZ (SYSCTL_PCIX_ERROR_BASE - 8) + { SYSCTL_PCIX_ANYTHING_ON_133MHZ, "cannot add any card to an active 133MHz PCIX bus" }, + +#define SYSCTL_PCIX_X66MHZ_ON_X100MHZ (SYSCTL_PCIX_ERROR_BASE - 9) + { SYSCTL_PCIX_X66MHZ_ON_X100MHZ, "cannot add a PCIX 66MHz card to an active 100MHz PCIX bus" }, + +#define SYSCTL_PCIX_INVALID_ORDER (SYSCTL_PCIX_ERROR_BASE - 10) + { SYSCTL_PCIX_INVALID_ORDER, "invalid reset order" }, + +#define SYSCTL_PCIX_DOWN_33MHZ (SYSCTL_PCIX_ERROR_BASE - 11) + { SYSCTL_PCIX_DOWN_33MHZ, "cannot power down a 33 MHz card on an active bus" }, + +#define SYSCTL_PCIX_RESET_33MHZ (SYSCTL_PCIX_ERROR_BASE - 12) + { SYSCTL_PCIX_RESET_33MHZ, "cannot reset a 33 MHz card on an active bus" }, + +#define SYSCTL_PCIX_SLOT_NOT_UP (SYSCTL_PCIX_ERROR_BASE - 13) + { SYSCTL_PCIX_SLOT_NOT_UP, "cannot reset a slot that is not powered up" }, + +#define SYSCTL_PCIX_INVALID_BUS_SETTING (SYSCTL_PCIX_ERROR_BASE - 14) + { SYSCTL_PCIX_INVALID_BUS_SETTING, "invalid bus type/speed selection (PCIX<66MHz, PCI>66MHz)" }, + +#define SYSCTL_PCIX_INVALID_DEPENDENT_SLOT (SYSCTL_PCIX_ERROR_BASE - 15) + { SYSCTL_PCIX_INVALID_DEPENDENT_SLOT, "invalid dependent slot in PCI slot configuration" }, + +#define SYSCTL_PCIX_SHARED_IDSELECT (SYSCTL_PCIX_ERROR_BASE - 16) + { SYSCTL_PCIX_SHARED_IDSELECT, "cannot enable two slots sharing the same IDSELECT" }, + +#define SYSCTL_PCIX_SLOT_DISABLED (SYSCTL_PCIX_ERROR_BASE - 17) + { SYSCTL_PCIX_SLOT_DISABLED, "slot is disabled" }, + +}; /* end sysctl_pci_errors[] */ + +/* + * look up an error message for PCI operations that fail + */ +static void +sysctl_pci_error_lookup(int error, char *err_msg) +{ + int i; + struct sysctl_pci_error_s *e = sysctl_pci_errors; + + for (i = 0; + i < (sizeof(sysctl_pci_errors) / sizeof(*e)); + i++, e++ ) + { + if (e->error == error) + { + strcpy(err_msg, e->msg); + return; + } + } + + sprintf(err_msg, "unrecognized PCI error type"); +} + +/* + * pcibr_slot_attach + * This is a place holder routine to keep track of all the + * slot-specific initialization that needs to be done. + * This is usually called when we want to initialize a new + * PCI card on the bus. + */ +int +pcibr_slot_attach(vertex_hdl_t pcibr_vhdl, + pciio_slot_t slot, + int drv_flags, + char *l1_msg, + int *sub_errorp) +{ + pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl); + int error; + + if (!(pcibr_soft->bs_slot[slot].slot_status & PCI_SLOT_POWER_ON)) { + uint64_t speed; + uint64_t mode; + + /* Power-up the slot */ + error = pcibr_slot_pwr(pcibr_vhdl, slot, PCI_REQ_SLOT_POWER_ON, l1_msg); + + if (error) { + if (sub_errorp) + *sub_errorp = error; + return(PCI_L1_ERR); + } else { + pcibr_soft->bs_slot[slot].slot_status &= ~PCI_SLOT_POWER_MASK; + pcibr_soft->bs_slot[slot].slot_status |= PCI_SLOT_POWER_ON; + } + + /* The speed/mode of the bus may have changed due to the hotplug */ + speed = pcireg_speed_get(pcibr_soft); + mode = pcireg_mode_get(pcibr_soft); + pcibr_soft->bs_bridge_mode = ((speed << 1) | mode); + + /* + * Allow cards like the Alteon Gigabit Ethernet Adapter to complete + * on-card initialization following the slot reset + */ + set_current_state (TASK_INTERRUPTIBLE); + schedule_timeout (HZ); + + /* Find out what is out there */ + error = pcibr_slot_info_init(pcibr_vhdl, slot); + + if (error) { + if (sub_errorp) + *sub_errorp = error; + return(PCI_SLOT_INFO_INIT_ERR); + } + + /* Set up the address space for this slot in the PCI land */ + + error = pcibr_slot_addr_space_init(pcibr_vhdl, slot); + + if (error) { + if (sub_errorp) + *sub_errorp = error; + return(PCI_SLOT_ADDR_INIT_ERR); + } + + /* Allocate the PCI-X Read Buffer Attribute Registers (RBARs)*/ + if (IS_PCIX(pcibr_soft)) { + int tmp_slot; + + /* Recalculate the RBARs for all the devices on the bus. Only + * return an error if we error for the given 'slot' + */ + pcibr_soft->bs_pcix_rbar_inuse = 0; + pcibr_soft->bs_pcix_rbar_avail = NUM_RBAR; + pcibr_soft->bs_pcix_rbar_percent_allowed = + pcibr_pcix_rbars_calc(pcibr_soft); + for (tmp_slot = pcibr_soft->bs_min_slot; + tmp_slot < PCIBR_NUM_SLOTS(pcibr_soft); ++tmp_slot) { + if (tmp_slot == slot) + continue; /* skip this 'slot', we do it below */ + (void)pcibr_slot_pcix_rbar_init(pcibr_soft, tmp_slot); + } + + error = pcibr_slot_pcix_rbar_init(pcibr_soft, slot); + if (error) { + if (sub_errorp) + *sub_errorp = error; + return(PCI_SLOT_RBAR_ALLOC_ERR); + } + } + + /* Setup the device register */ + error = pcibr_slot_device_init(pcibr_vhdl, slot); + + if (error) { + if (sub_errorp) + *sub_errorp = error; + return(PCI_SLOT_DEV_INIT_ERR); + } + + /* Setup host/guest relations */ + error = pcibr_slot_guest_info_init(pcibr_vhdl, slot); + + if (error) { + if (sub_errorp) + *sub_errorp = error; + return(PCI_SLOT_GUEST_INIT_ERR); + } + + /* Initial RRB management */ + error = pcibr_slot_initial_rrb_alloc(pcibr_vhdl, slot); + + if (error) { + if (sub_errorp) + *sub_errorp = error; + return(PCI_SLOT_RRB_ALLOC_ERR); + } + + } + + /* Call the device attach */ + error = pcibr_slot_call_device_attach(pcibr_vhdl, slot, drv_flags); + + if (error) { + if (sub_errorp) + *sub_errorp = error; + if (error == EUNATCH) + return(PCI_NO_DRIVER); + else + return(PCI_SLOT_DRV_ATTACH_ERR); + } + + return(0); +} + +/* + * pcibr_slot_enable + * Enable the PCI slot for a hot-plug insert. + */ +int +pcibr_slot_enable(vertex_hdl_t pcibr_vhdl, struct pcibr_slot_enable_req_s *req_p) +{ + pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl); + pciio_slot_t slot = req_p->req_device; + int error = 0; + + /* Make sure that we are dealing with a bridge device vertex */ + if (!pcibr_soft) { + return(PCI_NOT_A_BRIDGE); + } + + PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_HOTPLUG, pcibr_vhdl, + "pcibr_slot_enable: pcibr_soft=0x%lx, slot=%d, req_p=0x%lx\n", + pcibr_soft, slot, req_p)); + + /* Check for the valid slot */ + if (!PCIBR_VALID_SLOT(pcibr_soft, slot)) + return(PCI_NOT_A_SLOT); + + if (pcibr_soft->bs_slot[slot].slot_status & PCI_SLOT_ENABLE_CMPLT) { + error = PCI_SLOT_ALREADY_UP; + goto enable_unlock; + } + + error = pcibr_slot_attach(pcibr_vhdl, slot, NULL, + req_p->req_resp.resp_l1_msg, + &req_p->req_resp.resp_sub_errno); + + req_p->req_resp.resp_l1_msg[PCI_L1_QSIZE] = '\0'; + + enable_unlock: + + return(error); +} + +/* + * pcibr_slot_disable + * Disable the PCI slot for a hot-plug removal. + */ +int +pcibr_slot_disable(vertex_hdl_t pcibr_vhdl, struct pcibr_slot_disable_req_s *req_p) +{ + pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl); + pciio_slot_t slot = req_p->req_device; + int error = 0; + pciio_slot_t tmp_slot; + + /* Make sure that we are dealing with a bridge device vertex */ + if (!pcibr_soft) { + return(PCI_NOT_A_BRIDGE); + } + + PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_HOTPLUG, pcibr_vhdl, + "pcibr_slot_disable: pcibr_soft=0x%lx, slot=%d, req_p=0x%lx\n", + pcibr_soft, slot, req_p)); + + /* Check for valid slot */ + if (!PCIBR_VALID_SLOT(pcibr_soft, slot)) + return(PCI_NOT_A_SLOT); + + if ((pcibr_soft->bs_slot[slot].slot_status & PCI_SLOT_DISABLE_CMPLT) || + ((pcibr_soft->bs_slot[slot].slot_status & PCI_SLOT_STATUS_MASK) == 0)) { + error = PCI_SLOT_ALREADY_DOWN; + /* + * RJR - Should we invoke an L1 slot power-down command just in case + * a previous shut-down failed to power-down the slot? + */ + goto disable_unlock; + } + + /* Do not allow the last 33 MHz card to be removed */ + if (IS_33MHZ(pcibr_soft)) { + for (tmp_slot = pcibr_soft->bs_first_slot; + tmp_slot <= pcibr_soft->bs_last_slot; tmp_slot++) + if (tmp_slot != slot) + if (pcibr_soft->bs_slot[tmp_slot].slot_status & PCI_SLOT_POWER_ON) { + error++; + break; + } + if (!error) { + error = PCI_EMPTY_33MHZ; + goto disable_unlock; + } + } + + if (req_p->req_action == PCI_REQ_SLOT_ELIGIBLE) + return(0); + + error = pcibr_slot_detach(pcibr_vhdl, slot, 1, + req_p->req_resp.resp_l1_msg, + &req_p->req_resp.resp_sub_errno); + + req_p->req_resp.resp_l1_msg[PCI_L1_QSIZE] = '\0'; + + disable_unlock: + + return(error); +} + +/* + * pcibr_slot_pwr + * Power-up or power-down a PCI slot. This routines makes calls to + * the L1 system controller driver which requires "external" slot#. + */ +int +pcibr_slot_pwr(vertex_hdl_t pcibr_vhdl, + pciio_slot_t slot, + int up, + char *err_msg) +{ + pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl); + nasid_t nasid; + u64 connection_type; + int rv; + + nasid = NASID_GET(pcibr_soft->bs_base); + connection_type = SAL_SYSCTL_IO_XTALK; + + rv = (int) ia64_sn_sysctl_iobrick_pci_op + (nasid, + connection_type, + (u64) pcibr_widget_to_bus(pcibr_vhdl), + PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), + (up ? SAL_SYSCTL_PCI_POWER_UP : SAL_SYSCTL_PCI_POWER_DOWN)); + + if (!rv) { + /* everything's okay; no error message */ + *err_msg = '\0'; + } + else { + /* there was a problem; look up an appropriate error message */ + sysctl_pci_error_lookup(rv, err_msg); + } + return rv; +} + +#endif /* CONFIG_HOTPLUG_PCI_SGI */ /* * pcibr_slot_info_init @@ -114,7 +520,9 @@ pcibr_slot_info_init(vertex_hdl_t pcibr_vhdl, return -ENODEV; slotp = &pcibr_soft->bs_slot[slot]; +#ifdef CONFIG_HOTPLUG_PCI_SGI slotp->slot_status |= SLOT_POWER_UP; +#endif vendor = 0xFFFF & idword; device = 0xFFFF & (idword >> 16); @@ -1019,6 +1427,7 @@ pcibr_slot_call_device_attach(vertex_hdl_t pcibr_vhdl, } /* next func */ +#ifdef CONFIG_HOTPLUG_PCI_SGI if (error) { if ((error != ENODEV) && (error != EUNATCH) && (error != EPERM)) { pcibr_soft->bs_slot[slot].slot_status &= ~SLOT_STATUS_MASK; @@ -1028,7 +1437,7 @@ pcibr_slot_call_device_attach(vertex_hdl_t pcibr_vhdl, pcibr_soft->bs_slot[slot].slot_status &= ~SLOT_STATUS_MASK; pcibr_soft->bs_slot[slot].slot_status |= SLOT_STARTUP_CMPLT; } - +#endif /* CONFIG_HOTPLUG_PCI_SGI */ return error; } @@ -1103,7 +1512,7 @@ pcibr_slot_call_device_detach(vertex_hdl_t pcibr_vhdl, } /* next func */ - +#ifdef CONFIG_HOTPLUG_PCI_SGI if (error) { if ((error != ENODEV) && (error != EUNATCH) && (error != EPERM)) { pcibr_soft->bs_slot[slot].slot_status &= ~SLOT_STATUS_MASK; @@ -1115,10 +1524,12 @@ pcibr_slot_call_device_detach(vertex_hdl_t pcibr_vhdl, pcibr_soft->bs_slot[slot].slot_status &= ~SLOT_STATUS_MASK; pcibr_soft->bs_slot[slot].slot_status |= SLOT_SHUTDOWN_CMPLT; } - +#endif /* CONFIG_HOTPLUG_PCI_SGI */ return error; } + + /* * pcibr_slot_detach * This is a place holder routine to keep track of all the diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c index 37a19527a1cc..3786a53db928 100644 --- a/arch/ia64/sn/kernel/irq.c +++ b/arch/ia64/sn/kernel/irq.c @@ -212,6 +212,49 @@ register_pcibr_intr(int irq, pcibr_intr_t intr) } void +unregister_pcibr_intr(int irq, pcibr_intr_t intr) +{ + + struct sn_intr_list_t **prev, *curr; + int cpu = intr->bi_cpu; + int i; + + if (sn_intr_list[irq] == NULL) + return; + + prev = &sn_intr_list[irq]; + curr = sn_intr_list[irq]; + while (curr) { + if (curr->intr == intr) { + *prev = curr->next; + break; + } + prev = &curr->next; + curr = curr->next; + } + + if (curr) + kfree(curr); + + if (!sn_intr_list[irq]) { + if (pdacpu(cpu)->sn_last_irq == irq) { + for (i = pdacpu(cpu)->sn_last_irq - 1; i; i--) + if (sn_intr_list[i]) + break; + pdacpu(cpu)->sn_last_irq = i; + } + + if (pdacpu(cpu)->sn_first_irq == irq) { + pdacpu(cpu)->sn_first_irq = 0; + for (i = pdacpu(cpu)->sn_first_irq + 1; i < NR_IRQS; i++) + if (sn_intr_list[i]) + pdacpu(cpu)->sn_first_irq = i; + } + } + +} + +void force_polled_int(void) { int i; diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 992d1ffe4460..503a32d232ee 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -553,80 +553,6 @@ config MAC_SCC tristate "Macintosh serial support" depends on MAC -config ADB - bool "Apple Desktop Bus (ADB) support" - depends on MAC - help - Apple Desktop Bus (ADB) support is for support of devices which - are connected to an ADB port. ADB devices tend to have 4 pins. - If you have an Apple Macintosh prior to the iMac, or a - "Blue and White G3", you probably want to say Y here. Otherwise - say N. - -config ADB_MACII - bool "Include Mac II ADB driver" - depends on ADB - help - Say Y here if want your kernel to support Macintosh systems that use - the Mac II style ADB. This includes the II, IIx, IIcx, SE/30, IIci, - Quadra 610, Quadra 650, Quadra 700, Quadra 800, Centris 610 and - Centris 650. - -config ADB_MACIISI - bool "Include Mac IIsi ADB driver" - depends on ADB - help - Say Y here if want your kernel to support Macintosh systems that use - the Mac IIsi style ADB. This includes the IIsi, IIvi, IIvx, Classic - II, LC, LC II, LC III, Performa 460, and the Performa 600. - -config ADB_CUDA - bool "Include CUDA ADB driver" - depends on ADB - help - This provides support for CUDA based Power Macintosh systems. This - includes most OldWorld PowerMacs, the first generation iMacs, the - Blue&White G3 and the Yikes G4 (PCI Graphics). All later models - should use CONFIG_ADB_PMU instead. - - If unsure say Y. - -config ADB_IOP - bool "Include IOP (IIfx/Quadra 9x0) ADB driver" - depends on ADB - help - The I/O Processor (IOP) is an Apple custom IC designed to provide - intelligent support for I/O controllers. It is described at - <http://www.angelfire.com/ca2/dev68k/iopdesc.html> to enable direct - support for it, say 'Y' here. - -config ADB_PMU68K - bool "Include PMU (Powerbook) ADB driver" - depends on ADB - help - Say Y here if want your kernel to support the m68k based Powerbooks. - This includes the PowerBook 140, PowerBook 145, PowerBook 150, - PowerBook 160, PowerBook 165, PowerBook 165c, PowerBook 170, - PowerBook 180, PowerBook, 180c, PowerBook 190cs, PowerBook 520, - PowerBook Duo 210, PowerBook Duo 230, PowerBook Duo 250, - PowerBook Duo 270c, PowerBook Duo 280 and PowerBook Duo 280c. - -config INPUT_ADBHID - bool "Use input layer for ADB devices" - depends on MAC && INPUT=y - ---help--- - Say Y here if you want to have ADB (Apple Desktop Bus) HID devices - such as keyboards, mice, joysticks, or graphic tablets handled by - the input layer. If you say Y here, make sure to say Y to the - corresponding drivers "Keyboard support" (CONFIG_INPUT_KEYBDEV), - "Mouse Support" (CONFIG_INPUT_MOUSEDEV) and "Event interface - support" (CONFIG_INPUT_EVDEV) as well. - - If you say N here, you still have the option of using the old ADB - keyboard and mouse drivers. - - If unsure, say Y. - config MAC_HID bool depends on INPUT_ADBHID @@ -646,18 +572,6 @@ config MAC_ADBKEYCODES If unsure, say Y here. -config MAC_EMUMOUSEBTN - bool "Support for mouse button 2+3 emulation" - depends on INPUT_ADBHID - help - This provides generic support for emulating the 2nd and 3rd mouse - button with keypresses. If you say Y here, the emulation is still - disabled by default. The emulation is controlled by these sysctl - entries: - /proc/sys/dev/mac_hid/mouse_button_emulation - /proc/sys/dev/mac_hid/mouse_button2_keycode - /proc/sys/dev/mac_hid/mouse_button3_keycode - config ADB_KEYBOARD bool "Support for ADB keyboard (old driver)" depends on MAC && !INPUT_ADBHID diff --git a/arch/m68knommu/defconfig b/arch/m68knommu/defconfig index 857c86b29c0d..153bbfecc085 100644 --- a/arch/m68knommu/defconfig +++ b/arch/m68knommu/defconfig @@ -355,7 +355,6 @@ CONFIG_MOUSE_PS2=y # # CONFIG_VT is not set # CONFIG_SERIAL_NONSTANDARD is not set -# CONFIG_LEDMAN is not set # CONFIG_RESETSWITCH is not set # diff --git a/arch/m68knommu/kernel/signal.c b/arch/m68knommu/kernel/signal.c index 82161f3560d8..3fb0d78f43b1 100644 --- a/arch/m68knommu/kernel/signal.c +++ b/arch/m68knommu/kernel/signal.c @@ -604,7 +604,7 @@ adjust_stack: struct pt_regs *tregs = (struct pt_regs *)((ulong)regs + regs->stkadj); #if DEBUG - printk("Performing stackadjust=%04x\n", regs->stkadj); + printk(KERN_DEBUG "Performing stackadjust=%04x\n", regs->stkadj); #endif /* This must be copied with decreasing addresses to handle overlaps. */ @@ -673,7 +673,7 @@ adjust_stack: struct pt_regs *tregs = (struct pt_regs *)((ulong)regs + regs->stkadj); #if DEBUG - printk("Performing stackadjust=%04x\n", regs->stkadj); + printk(KERN_DEBUG "Performing stackadjust=%04x\n", regs->stkadj); #endif /* This must be copied with decreasing addresses to handle overlaps. */ diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index a0f338a91eca..02b03fdfea9b 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -250,6 +250,98 @@ config SHARED_KERNEL You should only select this option if you know what you are doing and want to exploit this feature. +config CMM + tristate "Cooperative memory management" + help + Select this option, if you want to enable the kernel interface + to reduce the memory size of the system. This is accomplished + by allocating pages of memory and put them "on hold". This only + makes sense for a system running under VM where the unused pages + will be reused by VM for other guest systems. The interface + allows an external monitor to balance memory of many systems. + Everybody who wants to run Linux under VM should select this + option. + +config CMM_PROC + bool "/proc interface to cooperative memory management" + depends on CMM + help + Select this option to enable the /proc interface to the + cooperative memory management. + +config CMM_IUCV + bool "IUCV special message interface to cooperative memory management" + depends on CMM && (SMSGIUCV=y || CMM=SMSGIUCV) + help + Select this option to enable the special message interface to + the cooperative memory management. + +config VIRT_TIMER + bool "Virtual CPU timer support" + help + This provides a kernel interface for virtual CPU timers. + Default is disabled. + +config APPLDATA_BASE + bool "Linux - VM Monitor Stream, base infrastructure" + depends on PROC_FS && VIRT_TIMER=y + help + This provides a kernel interface for creating and updating z/VM APPLDATA + monitor records. The monitor records are updated at certain time + intervals, once the timer is started. + Writing 1 or 0 to /proc/appldata/timer starts(1) or stops(0) the timer, + i.e. enables or disables monitoring on the Linux side. + A custom interval value (in seconds) can be written to + /proc/appldata/interval. + + Defaults are 60 seconds interval and timer off. + The /proc entries can also be read from, showing the current settings. + +config APPLDATA_MEM + tristate "Monitor memory management statistics" + depends on APPLDATA_BASE + help + This provides memory management related data to the Linux - VM Monitor + Stream, like paging/swapping rate, memory utilisation, etc. + Writing 1 or 0 to /proc/appldata/memory creates(1) or removes(0) a z/VM + APPLDATA monitor record, i.e. enables or disables monitoring this record + on the z/VM side. + + Default is disabled. + The /proc entry can also be read from, showing the current settings. + + This can also be compiled as a module, which will be called + appldata_mem.o. + +config APPLDATA_OS + tristate "Monitor OS statistics" + depends on APPLDATA_BASE + help + This provides OS related data to the Linux - VM Monitor Stream, like + CPU utilisation, etc. + Writing 1 or 0 to /proc/appldata/os creates(1) or removes(0) a z/VM + APPLDATA monitor record, i.e. enables or disables monitoring this record + on the z/VM side. + + Default is disabled. + This can also be compiled as a module, which will be called + appldata_os.o. + +config APPLDATA_NET_SUM + tristate "Monitor overall network statistics" + depends on APPLDATA_BASE + help + This provides network related data to the Linux - VM Monitor Stream, + currently there is only a total sum of network I/O statistics, no + per-interface data. + Writing 1 or 0 to /proc/appldata/net_sum creates(1) or removes(0) a z/VM + APPLDATA monitor record, i.e. enables or disables monitoring this record + on the z/VM side. + + Default is disabled. + This can also be compiled as a module, which will be called + appldata_net_sum.o. + endmenu config PCMCIA diff --git a/arch/s390/Makefile b/arch/s390/Makefile index d18a5731e31f..9525ee20aa0e 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -35,6 +35,7 @@ cflags-$(CONFIG_MARCH_Z990) += $(call check_gcc,-march=z990,) CFLAGS += $(cflags-y) CFLAGS += $(call check_gcc,-finline-limit=10000,) CFLAGS += -pipe -fno-strength-reduce -Wno-sign-compare +CFLAGS += -mbackchain OBJCOPYFLAGS := -O binary LDFLAGS_vmlinux := -e start @@ -43,7 +44,8 @@ head-$(CONFIG_ARCH_S390_31) += arch/$(ARCH)/kernel/head.o head-$(CONFIG_ARCH_S390X) += arch/$(ARCH)/kernel/head64.o head-y += arch/$(ARCH)/kernel/init_task.o -core-y += arch/$(ARCH)/mm/ arch/$(ARCH)/kernel/ +core-y += arch/$(ARCH)/mm/ arch/$(ARCH)/kernel/ \ + arch/$(ARCH)/appldata/ libs-y += arch/$(ARCH)/lib/ drivers-y += drivers/s390/ drivers-$(CONFIG_MATHEMU) += arch/$(ARCH)/math-emu/ diff --git a/arch/s390/appldata/Makefile b/arch/s390/appldata/Makefile new file mode 100644 index 000000000000..99f1cf071304 --- /dev/null +++ b/arch/s390/appldata/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Linux - z/VM Monitor Stream. +# + +obj-$(CONFIG_APPLDATA_BASE) += appldata_base.o +obj-$(CONFIG_APPLDATA_MEM) += appldata_mem.o +obj-$(CONFIG_APPLDATA_OS) += appldata_os.o +obj-$(CONFIG_APPLDATA_NET_SUM) += appldata_net_sum.o diff --git a/arch/s390/appldata/appldata.h b/arch/s390/appldata/appldata.h new file mode 100644 index 000000000000..e806a8922bbb --- /dev/null +++ b/arch/s390/appldata/appldata.h @@ -0,0 +1,59 @@ +/* + * arch/s390/appldata/appldata.h + * + * Definitions and interface for Linux - z/VM Monitor Stream. + * + * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH. + * + * Author: Gerald Schaefer <geraldsc@de.ibm.com> + */ + +//#define APPLDATA_DEBUG /* Debug messages on/off */ + +#define APPLDATA_MAX_REC_SIZE 4024 /* Maximum size of the */ + /* data buffer */ +#define APPLDATA_MAX_PROCS 100 + +#define APPLDATA_PROC_NAME_LENGTH 16 /* Max. length of /proc name */ + +#define APPLDATA_RECORD_MEM_ID 0x01 /* IDs to identify the */ +#define APPLDATA_RECORD_OS_ID 0x02 /* individual records, */ +#define APPLDATA_RECORD_NET_SUM_ID 0x03 /* must be < 256 ! */ +#define APPLDATA_RECORD_PROC_ID 0x04 + +#define CTL_APPLDATA 2120 /* sysctl IDs, must be unique */ +#define CTL_APPLDATA_TIMER 2121 +#define CTL_APPLDATA_INTERVAL 2122 +#define CTL_APPLDATA_MEM 2123 +#define CTL_APPLDATA_OS 2124 +#define CTL_APPLDATA_NET_SUM 2125 +#define CTL_APPLDATA_PROC 2126 + +#define P_INFO(x...) printk(KERN_INFO MY_PRINT_NAME " info: " x) +#define P_ERROR(x...) printk(KERN_ERR MY_PRINT_NAME " error: " x) +#define P_WARNING(x...) printk(KERN_WARNING MY_PRINT_NAME " status: " x) + +#ifdef APPLDATA_DEBUG +#define P_DEBUG(x...) printk(KERN_DEBUG MY_PRINT_NAME " debug: " x) +#else +#define P_DEBUG(x...) do {} while (0) +#endif + +struct appldata_ops { + struct list_head list; + struct ctl_table_header *sysctl_header; + struct ctl_table *ctl_table; + int active; /* monitoring status */ + + /* fill in from here */ + unsigned int ctl_nr; /* sysctl ID */ + char name[APPLDATA_PROC_NAME_LENGTH]; /* name of /proc fs node */ + unsigned char record_nr; /* Record Nr. for Product ID */ + void (*callback)(void *data); /* callback function */ + void *data; /* record data */ + unsigned int size; /* size of record */ + struct module *owner; /* THIS_MODULE */ +}; + +extern int appldata_register_ops(struct appldata_ops *ops); +extern void appldata_unregister_ops(struct appldata_ops *ops); diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c new file mode 100644 index 000000000000..377f78e7c9fa --- /dev/null +++ b/arch/s390/appldata/appldata_base.c @@ -0,0 +1,677 @@ +/* + * arch/s390/appldata/appldata_base.c + * + * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1. + * Exports appldata_register_ops() and appldata_unregister_ops() for the + * data gathering modules. + * + * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH. + * + * Author: Gerald Schaefer <geraldsc@de.ibm.com> + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <linux/interrupt.h> +#include <linux/proc_fs.h> +#include <linux/page-flags.h> +#include <linux/swap.h> +#include <linux/pagemap.h> +#include <linux/sysctl.h> +#include <asm/timer.h> +//#include <linux/kernel_stat.h> + +#include "appldata.h" + + +#define MY_PRINT_NAME "appldata" /* for debug messages, etc. */ +#define APPLDATA_CPU_INTERVAL 10000 /* default (CPU) time for + sampling interval in + milliseconds */ + +#define TOD_MICRO 0x01000 /* nr. of TOD clock units + for 1 microsecond */ +#ifndef CONFIG_ARCH_S390X + +#define APPLDATA_START_INTERVAL_REC 0x00 /* Function codes for */ +#define APPLDATA_STOP_REC 0x01 /* DIAG 0xDC */ +#define APPLDATA_GEN_EVENT_RECORD 0x02 +#define APPLDATA_START_CONFIG_REC 0x03 + +#else + +#define APPLDATA_START_INTERVAL_REC 0x80 +#define APPLDATA_STOP_REC 0x81 +#define APPLDATA_GEN_EVENT_RECORD 0x82 +#define APPLDATA_START_CONFIG_REC 0x83 + +#endif /* CONFIG_ARCH_S390X */ + + +/* + * Parameter list for DIAGNOSE X'DC' + */ +#ifndef CONFIG_ARCH_S390X +struct appldata_parameter_list { + u16 diag; /* The DIAGNOSE code X'00DC' */ + u8 function; /* The function code for the DIAGNOSE */ + u8 parlist_length; /* Length of the parameter list */ + u32 product_id_addr; /* Address of the 16-byte product ID */ + u16 reserved; + u16 buffer_length; /* Length of the application data buffer */ + u32 buffer_addr; /* Address of the application data buffer */ +}; +#else +struct appldata_parameter_list { + u16 diag; + u8 function; + u8 parlist_length; + u32 unused01; + u16 reserved; + u16 buffer_length; + u32 unused02; + u64 product_id_addr; + u64 buffer_addr; +}; +#endif /* CONFIG_ARCH_S390X */ + +/* + * /proc entries (sysctl) + */ +static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata"; +static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp); +static int appldata_interval_handler(ctl_table *ctl, int write, + struct file *filp, void *buffer, + size_t *lenp); + +static struct ctl_table_header *appldata_sysctl_header; +static struct ctl_table appldata_table[] = { + { + .ctl_name = CTL_APPLDATA_TIMER, + .procname = "timer", + .mode = S_IRUGO | S_IWUSR, + .proc_handler = &appldata_timer_handler, + }, + { + .ctl_name = CTL_APPLDATA_INTERVAL, + .procname = "interval", + .mode = S_IRUGO | S_IWUSR, + .proc_handler = &appldata_interval_handler, + }, + { .ctl_name = 0 } +}; + +static struct ctl_table appldata_dir_table[] = { + { + .ctl_name = CTL_APPLDATA, + .procname = appldata_proc_name, + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = appldata_table, + }, + { .ctl_name = 0 } +}; + +/* + * Timer + */ +DEFINE_PER_CPU(struct vtimer_list, appldata_timer); +static atomic_t appldata_expire_count = ATOMIC_INIT(0); +static struct appldata_mod_vtimer_args { + struct vtimer_list *timer; + u64 expires; +} appldata_mod_vtimer_args; + +static spinlock_t appldata_timer_lock = SPIN_LOCK_UNLOCKED; +static int appldata_interval = APPLDATA_CPU_INTERVAL; +static int appldata_timer_active; + +/* + * Tasklet + */ +static struct tasklet_struct appldata_tasklet_struct; + +/* + * Ops list + */ +static spinlock_t appldata_ops_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(appldata_ops_list); + + +/************************* timer, tasklet, DIAG ******************************/ +/* + * appldata_timer_function() + * + * schedule tasklet and reschedule timer + */ +static void appldata_timer_function(unsigned long data, struct pt_regs *regs) +{ + P_DEBUG(" -= Timer =-\n"); + P_DEBUG("CPU: %i, expire: %i\n", smp_processor_id(), + atomic_read(&appldata_expire_count)); + if (atomic_dec_and_test(&appldata_expire_count)) { + atomic_set(&appldata_expire_count, num_online_cpus()); + tasklet_schedule((struct tasklet_struct *) data); + } +} + +/* + * appldata_tasklet_function() + * + * call data gathering function for each (active) module + */ +static void appldata_tasklet_function(unsigned long data) +{ + struct list_head *lh; + struct appldata_ops *ops; + int i; + + P_DEBUG(" -= Tasklet =-\n"); + i = 0; + spin_lock(&appldata_ops_lock); + list_for_each(lh, &appldata_ops_list) { + ops = list_entry(lh, struct appldata_ops, list); + P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n", + ++i, ops->active, ops->name); + if (ops->active == 1) { + ops->callback(ops->data); + } + } + spin_unlock(&appldata_ops_lock); +} + +/* + * appldata_mod_vtimer_wrap() + * + * wrapper function for mod_virt_timer(), because smp_call_function_on() + * accepts only one parameter. + */ +static void appldata_mod_vtimer_wrap(struct appldata_mod_vtimer_args *args) { + mod_virt_timer(args->timer, args->expires); +} + +/* + * appldata_diag() + * + * prepare parameter list, issue DIAG 0xDC + */ +static int appldata_diag(char record_nr, u16 function, unsigned long buffer, + u16 length) +{ + unsigned long ry; + struct appldata_product_id { + char prod_nr[7]; /* product nr. */ + char prod_fn[2]; /* product function */ + char record_nr; /* record nr. */ + char version_nr[2]; /* version */ + char release_nr[2]; /* release */ + char mod_lvl[2]; /* modification lvl. */ + } appldata_product_id = { + /* all strings are EBCDIC, record_nr is byte */ + .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4, + 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */ + .prod_fn = {0xD5, 0xD3}, /* "NL" */ + .record_nr = record_nr, + .version_nr = {0xF2, 0xF6}, /* "26" */ + .release_nr = {0xF0, 0xF1}, /* "01" */ + .mod_lvl = {0xF0, 0xF0}, /* "00" */ + }; + struct appldata_parameter_list appldata_parameter_list = { + .diag = 0xDC, + .function = function, + .parlist_length = + sizeof(appldata_parameter_list), + .buffer_length = length, + .product_id_addr = + (unsigned long) &appldata_product_id, + .buffer_addr = virt_to_phys((void *) buffer) + }; + + if (!MACHINE_IS_VM) + return -ENOSYS; + ry = -1; + asm volatile( + "diag %1,%0,0xDC\n\t" + : "=d" (ry) : "d" (&(appldata_parameter_list)) : "cc"); + return (int) ry; +} +/********************** timer, tasklet, DIAG <END> ***************************/ + + +/****************************** /proc stuff **********************************/ +/* + * appldata_timer_handler() + * + * Start/Stop timer, show status of timer (0 = not active, 1 = active) + */ +static int +appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int len, i; + char buf[2]; + u64 per_cpu_interval; + + if (!*lenp || filp->f_pos) { + *lenp = 0; + return 0; + } + if (!write) { + len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n"); + if (len > *lenp) + len = *lenp; + if (copy_to_user(buffer, buf, len)) + return -EFAULT; + goto out; + } + per_cpu_interval = (u64) (appldata_interval*1000 / + num_online_cpus()) * TOD_MICRO; + len = *lenp; + if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) + return -EFAULT; + spin_lock(&appldata_timer_lock); + per_cpu_interval = (u64) (appldata_interval*1000 / + num_online_cpus()) * TOD_MICRO; + if ((buf[0] == '1') && (!appldata_timer_active)) { + for (i = 0; i < num_online_cpus(); i++) { + per_cpu(appldata_timer, i).expires = per_cpu_interval; + smp_call_function_on(add_virt_timer_periodic, + &per_cpu(appldata_timer, i), + 0, 1, i); + } + appldata_timer_active = 1; + P_INFO("Monitoring timer started.\n"); + } else if ((buf[0] == '0') && (appldata_timer_active)) { + for (i = 0; i < num_online_cpus(); i++) { + del_virt_timer(&per_cpu(appldata_timer, i)); + } + appldata_timer_active = 0; + P_INFO("Monitoring timer stopped.\n"); + } + spin_unlock(&appldata_timer_lock); +out: + *lenp = len; + filp->f_pos += len; + return 0; +} + +/* + * appldata_interval_handler() + * + * Set (CPU) timer interval for collection of data (in milliseconds), show + * current timer interval. + */ +static int +appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int len, i, interval; + char buf[16]; + u64 per_cpu_interval; + + if (!*lenp || filp->f_pos) { + *lenp = 0; + return 0; + } + if (!write) { + len = sprintf(buf, "%i\n", appldata_interval); + if (len > *lenp) + len = *lenp; + if (copy_to_user(buffer, buf, len)) + return -EFAULT; + goto out; + } + len = *lenp; + if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) { + return -EFAULT; + } + sscanf(buf, "%i", &interval); + if (interval <= 0) { + P_ERROR("Timer CPU interval has to be > 0!\n"); + return -EINVAL; + } + per_cpu_interval = (u64) (interval*1000 / num_online_cpus()) * TOD_MICRO; + + spin_lock(&appldata_timer_lock); + appldata_interval = interval; + if (appldata_timer_active) { + for (i = 0; i < num_online_cpus(); i++) { + appldata_mod_vtimer_args.timer = + &per_cpu(appldata_timer, i); + appldata_mod_vtimer_args.expires = + per_cpu_interval; + smp_call_function_on( + (void *) appldata_mod_vtimer_wrap, + &appldata_mod_vtimer_args, + 0, 1, i); + } + } + spin_unlock(&appldata_timer_lock); + + P_INFO("Monitoring CPU interval set to %u milliseconds.\n", + interval); +out: + *lenp = len; + filp->f_pos += len; + return 0; +} + +/* + * appldata_generic_handler() + * + * Generic start/stop monitoring and DIAG, show status of + * monitoring (0 = not in process, 1 = in process) + */ +static int +appldata_generic_handler(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + struct appldata_ops *ops; + int rc, len; + char buf[2]; + + ops = ctl->data; + if (!*lenp || filp->f_pos) { + *lenp = 0; + return 0; + } + if (!write) { + len = sprintf(buf, ops->active ? "1\n" : "0\n"); + if (len > *lenp) + len = *lenp; + if (copy_to_user(buffer, buf, len)) + return -EFAULT; + goto out; + } + len = *lenp; + if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) + return -EFAULT; + + spin_lock_bh(&appldata_ops_lock); + if ((buf[0] == '1') && (ops->active == 0)) { + ops->active = 1; + ops->callback(ops->data); // init record + rc = appldata_diag(ops->record_nr, + APPLDATA_START_INTERVAL_REC, + (unsigned long) ops->data, ops->size); + if (rc != 0) { + P_ERROR("START DIAG 0xDC for %s failed, " + "return code: %d\n", ops->name, rc); + ops->active = 0; + } else { + P_INFO("Monitoring %s data enabled, " + "DIAG 0xDC started.\n", ops->name); + } + } else if ((buf[0] == '0') && (ops->active == 1)) { + ops->active = 0; + rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, + (unsigned long) ops->data, ops->size); + if (rc != 0) { + P_ERROR("STOP DIAG 0xDC for %s failed, " + "return code: %d\n", ops->name, rc); + } else { + P_INFO("Monitoring %s data disabled, " + "DIAG 0xDC stopped.\n", ops->name); + } + } + spin_unlock_bh(&appldata_ops_lock); +out: + *lenp = len; + filp->f_pos += len; + return 0; +} + +/*************************** /proc stuff <END> *******************************/ + + +/************************* module-ops management *****************************/ +/* + * appldata_register_ops() + * + * update ops list, register /proc/sys entries + */ +int appldata_register_ops(struct appldata_ops *ops) +{ + struct list_head *lh; + struct appldata_ops *tmp_ops; + int rc, i; + + rc = 0; + i = 0; + + if ((ops->size > APPLDATA_MAX_REC_SIZE) || + (ops->size < 0)){ + P_ERROR("Invalid size of %s record = %i, maximum = %i!\n", + ops->name, ops->size, APPLDATA_MAX_REC_SIZE); + rc = -ENOMEM; + goto out; + } + if ((ops->ctl_nr == CTL_APPLDATA) || + (ops->ctl_nr == CTL_APPLDATA_TIMER) || + (ops->ctl_nr == CTL_APPLDATA_INTERVAL)) { + P_ERROR("ctl_nr %i already in use!\n", ops->ctl_nr); + rc = -EBUSY; + goto out; + } + ops->ctl_table = kmalloc(4*sizeof(struct ctl_table), GFP_KERNEL); + if (ops->ctl_table == NULL) { + P_ERROR("Not enough memory for %s ctl_table!\n", ops->name); + rc = -ENOMEM; + goto out; + } + memset(ops->ctl_table, 0, 4*sizeof(struct ctl_table)); + + spin_lock_bh(&appldata_ops_lock); + list_for_each(lh, &appldata_ops_list) { + tmp_ops = list_entry(lh, struct appldata_ops, list); + P_DEBUG("register_ops loop: %i) name = %s, ctl = %i\n", + ++i, tmp_ops->name, tmp_ops->ctl_nr); + P_DEBUG("Comparing %s (ctl %i) with %s (ctl %i)\n", + tmp_ops->name, tmp_ops->ctl_nr, ops->name, + ops->ctl_nr); + if (strncmp(tmp_ops->name, ops->name, + APPLDATA_PROC_NAME_LENGTH) == 0) { + spin_unlock_bh(&appldata_ops_lock); + P_ERROR("Name \"%s\" already registered!\n", ops->name); + kfree(ops->ctl_table); + rc = -EBUSY; + goto out; + } + if (tmp_ops->ctl_nr == ops->ctl_nr) { + spin_unlock_bh(&appldata_ops_lock); + P_ERROR("ctl_nr %i already registered!\n", ops->ctl_nr); + kfree(ops->ctl_table); + rc = -EBUSY; + goto out; + } + } + list_add(&ops->list, &appldata_ops_list); + spin_unlock_bh(&appldata_ops_lock); + + ops->ctl_table[0].ctl_name = CTL_APPLDATA; + ops->ctl_table[0].procname = appldata_proc_name; + ops->ctl_table[0].maxlen = 0; + ops->ctl_table[0].mode = S_IRUGO | S_IXUGO; + ops->ctl_table[0].child = &ops->ctl_table[2]; + + ops->ctl_table[1].ctl_name = 0; + + ops->ctl_table[2].ctl_name = ops->ctl_nr; + ops->ctl_table[2].procname = ops->name; + ops->ctl_table[2].mode = S_IRUGO | S_IWUSR; + ops->ctl_table[2].proc_handler = appldata_generic_handler; + ops->ctl_table[2].data = ops; + + ops->ctl_table[3].ctl_name = 0; + + ops->sysctl_header = register_sysctl_table(ops->ctl_table,1); + ops->ctl_table[2].de->owner = ops->owner; + P_INFO("%s-ops registered!\n", ops->name); +out: + return rc; +} + +/* + * appldata_unregister_ops() + * + * update ops list, unregister /proc entries, stop DIAG if necessary + */ +void appldata_unregister_ops(struct appldata_ops *ops) +{ + int rc; + + unregister_sysctl_table(ops->sysctl_header); + kfree(ops->ctl_table); + if (ops->active == 1) { + ops->active = 0; + rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, + (unsigned long) ops->data, ops->size); + if (rc != 0) { + P_ERROR("STOP DIAG 0xDC for %s failed, " + "return code: %d\n", ops->name, rc); + } else { + P_INFO("Monitoring %s data disabled, " + "DIAG 0xDC stopped.\n", ops->name); + } + + } + spin_lock_bh(&appldata_ops_lock); + list_del(&ops->list); + spin_unlock_bh(&appldata_ops_lock); + P_INFO("%s-ops unregistered!\n", ops->name); +} +/********************** module-ops management <END> **************************/ + + +/******************************* init / exit *********************************/ +/* + * appldata_init() + * + * init timer and tasklet, register /proc entries + */ +static int __init appldata_init(void) +{ + int i; + + P_DEBUG("sizeof(parameter_list) = %lu\n", + sizeof(struct appldata_parameter_list)); + + for (i = 0; i < num_online_cpus(); i++) { + init_virt_timer(&per_cpu(appldata_timer, i)); + per_cpu(appldata_timer, i).function = appldata_timer_function; + per_cpu(appldata_timer, i).data = (unsigned long) + &appldata_tasklet_struct; + } + atomic_set(&appldata_expire_count, num_online_cpus()); + + appldata_sysctl_header = register_sysctl_table(appldata_dir_table, 1); +#ifdef MODULE + appldata_dir_table[0].de->owner = THIS_MODULE; + appldata_table[0].de->owner = THIS_MODULE; + appldata_table[1].de->owner = THIS_MODULE; +#endif + + tasklet_init(&appldata_tasklet_struct, appldata_tasklet_function, 0); + P_DEBUG("Base interface initialized.\n"); + return 0; +} + +/* + * appldata_exit() + * + * stop timer and tasklet, unregister /proc entries + */ +static void __exit appldata_exit(void) +{ + struct list_head *lh; + struct appldata_ops *ops; + int rc, i; + + P_DEBUG("Unloading module ...\n"); + /* + * ops list should be empty, but just in case something went wrong... + */ + spin_lock_bh(&appldata_ops_lock); + list_for_each(lh, &appldata_ops_list) { + ops = list_entry(lh, struct appldata_ops, list); + rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, + (unsigned long) ops->data, ops->size); + if (rc != 0) { + P_ERROR("STOP DIAG 0xDC for %s failed, " + "return code: %d\n", ops->name, rc); + } + } + spin_unlock_bh(&appldata_ops_lock); + + for (i = 0; i < num_online_cpus(); i++) { + del_virt_timer(&per_cpu(appldata_timer, i)); + } + appldata_timer_active = 0; + + unregister_sysctl_table(appldata_sysctl_header); + + tasklet_kill(&appldata_tasklet_struct); + P_DEBUG("... module unloaded!\n"); +} +/**************************** init / exit <END> ******************************/ + + +module_init(appldata_init); +module_exit(appldata_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gerald Schaefer"); +MODULE_DESCRIPTION("Linux-VM Monitor Stream, base infrastructure"); + +EXPORT_SYMBOL_GPL(appldata_register_ops); +EXPORT_SYMBOL_GPL(appldata_unregister_ops); + +#ifdef MODULE +/* + * Kernel symbols needed by appldata_mem and appldata_os modules. + * However, if this file is compiled as a module (for testing only), these + * symbols are not exported. In this case, we define them locally and export + * those. + */ +void si_swapinfo(struct sysinfo *val) +{ + val->freeswap = -1ul; + val->totalswap = -1ul; +} + +unsigned long avenrun[3] = {-1 - FIXED_1/200, -1 - FIXED_1/200, + -1 - FIXED_1/200}; +int nr_threads = -1; + +void get_full_page_state(struct page_state *ps) +{ + memset(ps, -1, sizeof(struct page_state)); +} + +unsigned long nr_running(void) +{ + return -1; +} + +unsigned long nr_iowait(void) +{ + return -1; +} + +/*unsigned long nr_context_switches(void) +{ + return -1; +}*/ +#endif /* MODULE */ +EXPORT_SYMBOL_GPL(si_swapinfo); +EXPORT_SYMBOL_GPL(nr_threads); +EXPORT_SYMBOL_GPL(avenrun); +EXPORT_SYMBOL_GPL(get_full_page_state); +EXPORT_SYMBOL_GPL(nr_running); +EXPORT_SYMBOL_GPL(nr_iowait); +//EXPORT_SYMBOL_GPL(nr_context_switches); diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c new file mode 100644 index 000000000000..fdf29bfbad3c --- /dev/null +++ b/arch/s390/appldata/appldata_mem.c @@ -0,0 +1,183 @@ +/* + * arch/s390/appldata/appldata_mem.c + * + * Data gathering module for Linux-VM Monitor Stream, Stage 1. + * Collects data related to memory management. + * + * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH. + * + * Author: Gerald Schaefer <geraldsc@de.ibm.com> + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <asm/io.h> +#include <linux/pagemap.h> +#include <linux/swap.h> + +#include "appldata.h" + + +#define MY_PRINT_NAME "appldata_mem" /* for debug messages, etc. */ +#define P2K(x) ((x) << (PAGE_SHIFT - 10)) /* Converts #Pages to KB */ + +/* + * Memory data + */ +struct appldata_mem_data { + u64 timestamp; + u32 sync_count_1; /* after VM collected the record data, */ + u32 sync_count_2; /* sync_count_1 and sync_count_2 should be the + same. If not, the record has been updated on + the Linux side while VM was collecting the + (possibly corrupt) data */ + + u64 pgpgin; /* data read from disk */ + u64 pgpgout; /* data written to disk */ + u64 pswpin; /* pages swapped in */ + u64 pswpout; /* pages swapped out */ + + u64 sharedram; /* sharedram is currently set to 0 */ + + u64 totalram; /* total main memory size */ + u64 freeram; /* free main memory size */ + u64 totalhigh; /* total high memory size */ + u64 freehigh; /* free high memory size */ + + u64 bufferram; /* memory reserved for buffers, free cache */ + u64 cached; /* size of (used) cache, w/o buffers */ + u64 totalswap; /* total swap space size */ + u64 freeswap; /* free swap space */ + +// New in 2.6 --> + u64 pgalloc; /* page allocations */ + u64 pgfault; /* page faults (major+minor) */ + u64 pgmajfault; /* page faults (major only) */ +// <-- New in 2.6 + +} appldata_mem_data; + + +static inline void appldata_debug_print(struct appldata_mem_data *mem_data) +{ + P_DEBUG("--- MEM - RECORD ---\n"); + P_DEBUG("pgpgin = %8lu KB\n", mem_data->pgpgin); + P_DEBUG("pgpgout = %8lu KB\n", mem_data->pgpgout); + P_DEBUG("pswpin = %8lu Pages\n", mem_data->pswpin); + P_DEBUG("pswpout = %8lu Pages\n", mem_data->pswpout); + P_DEBUG("pgalloc = %8lu \n", mem_data->pgalloc); + P_DEBUG("pgfault = %8lu \n", mem_data->pgfault); + P_DEBUG("pgmajfault = %8lu \n", mem_data->pgmajfault); + P_DEBUG("sharedram = %8lu KB\n", mem_data->sharedram); + P_DEBUG("totalram = %8lu KB\n", mem_data->totalram); + P_DEBUG("freeram = %8lu KB\n", mem_data->freeram); + P_DEBUG("totalhigh = %8lu KB\n", mem_data->totalhigh); + P_DEBUG("freehigh = %8lu KB\n", mem_data->freehigh); + P_DEBUG("bufferram = %8lu KB\n", mem_data->bufferram); + P_DEBUG("cached = %8lu KB\n", mem_data->cached); + P_DEBUG("totalswap = %8lu KB\n", mem_data->totalswap); + P_DEBUG("freeswap = %8lu KB\n", mem_data->freeswap); + P_DEBUG("sync_count_1 = %u\n", mem_data->sync_count_1); + P_DEBUG("sync_count_2 = %u\n", mem_data->sync_count_2); + P_DEBUG("timestamp = %lX\n", mem_data->timestamp); +} + +/* + * appldata_get_mem_data() + * + * gather memory data + */ +static void appldata_get_mem_data(void *data) +{ + struct sysinfo val; + struct page_state ps; + struct appldata_mem_data *mem_data; + + mem_data = data; + mem_data->sync_count_1++; + + get_full_page_state(&ps); + mem_data->pgpgin = ps.pgpgin >> 1; + mem_data->pgpgout = ps.pgpgout >> 1; + mem_data->pswpin = ps.pswpin; + mem_data->pswpout = ps.pswpout; + mem_data->pgalloc = ps.pgalloc; + mem_data->pgfault = ps.pgfault; + mem_data->pgmajfault = ps.pgmajfault; + +P_DEBUG("pgalloc = %lu, pgfree = %lu\n", ps.pgalloc, ps.pgfree); + + si_meminfo(&val); + mem_data->sharedram = val.sharedram; + mem_data->totalram = P2K(val.totalram); + mem_data->freeram = P2K(val.freeram); + mem_data->totalhigh = P2K(val.totalhigh); + mem_data->freehigh = P2K(val.freehigh); + mem_data->bufferram = P2K(val.bufferram); + mem_data->cached = P2K(atomic_read(&nr_pagecache) - val.bufferram); + + si_swapinfo(&val); + mem_data->totalswap = P2K(val.totalswap); + mem_data->freeswap = P2K(val.freeswap); + + mem_data->timestamp = get_clock(); + mem_data->sync_count_2++; +#ifdef APPLDATA_DEBUG + appldata_debug_print(mem_data); +#endif +} + + +static struct appldata_ops ops = { + .ctl_nr = CTL_APPLDATA_MEM, + .name = "mem", + .record_nr = APPLDATA_RECORD_MEM_ID, + .size = sizeof(struct appldata_mem_data), + .callback = &appldata_get_mem_data, + .data = &appldata_mem_data, + .owner = THIS_MODULE, +}; + + +/* + * appldata_mem_init() + * + * init_data, register ops + */ +static int __init appldata_mem_init(void) +{ + int rc; + + P_DEBUG("sizeof(mem) = %lu\n", sizeof(struct appldata_mem_data)); + + rc = appldata_register_ops(&ops); + if (rc != 0) { + P_ERROR("Error registering ops, rc = %i\n", rc); + } else { + P_DEBUG("%s-ops registered!\n", ops.name); + } + return rc; +} + +/* + * appldata_mem_exit() + * + * unregister ops + */ +static void __exit appldata_mem_exit(void) +{ + appldata_unregister_ops(&ops); + P_DEBUG("%s-ops unregistered!\n", ops.name); +} + + +module_init(appldata_mem_init); +module_exit(appldata_mem_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gerald Schaefer"); +MODULE_DESCRIPTION("Linux-VM Monitor Stream, MEMORY statistics"); diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c new file mode 100644 index 000000000000..87cd7d152847 --- /dev/null +++ b/arch/s390/appldata/appldata_net_sum.c @@ -0,0 +1,187 @@ +/* + * arch/s390/appldata/appldata_net_sum.c + * + * Data gathering module for Linux-VM Monitor Stream, Stage 1. + * Collects accumulated network statistics (Packets received/transmitted, + * dropped, errors, ...). + * + * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH. + * + * Author: Gerald Schaefer <geraldsc@de.ibm.com> + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/netdevice.h> + +#include "appldata.h" + + +#define MY_PRINT_NAME "appldata_net_sum" /* for debug messages, etc. */ + + +/* + * Network data + */ +struct appldata_net_sum_data { + u64 timestamp; + u32 sync_count_1; /* after VM collected the record data, */ + u32 sync_count_2; /* sync_count_1 and sync_count_2 should be the + same. If not, the record has been updated on + the Linux side while VM was collecting the + (possibly corrupt) data */ + + u32 nr_interfaces; /* nr. of network interfaces being monitored */ + + u32 padding; /* next value is 64-bit aligned, so these */ + /* 4 byte would be padded out by compiler */ + + u64 rx_packets; /* total packets received */ + u64 tx_packets; /* total packets transmitted */ + u64 rx_bytes; /* total bytes received */ + u64 tx_bytes; /* total bytes transmitted */ + u64 rx_errors; /* bad packets received */ + u64 tx_errors; /* packet transmit problems */ + u64 rx_dropped; /* no space in linux buffers */ + u64 tx_dropped; /* no space available in linux */ + u64 collisions; /* collisions while transmitting */ +} appldata_net_sum_data; + + +static inline void appldata_print_debug(struct appldata_net_sum_data *net_data) +{ + P_DEBUG("--- NET - RECORD ---\n"); + + P_DEBUG("nr_interfaces = %u\n", net_data->nr_interfaces); + P_DEBUG("rx_packets = %8lu\n", net_data->rx_packets); + P_DEBUG("tx_packets = %8lu\n", net_data->tx_packets); + P_DEBUG("rx_bytes = %8lu\n", net_data->rx_bytes); + P_DEBUG("tx_bytes = %8lu\n", net_data->tx_bytes); + P_DEBUG("rx_errors = %8lu\n", net_data->rx_errors); + P_DEBUG("tx_errors = %8lu\n", net_data->tx_errors); + P_DEBUG("rx_dropped = %8lu\n", net_data->rx_dropped); + P_DEBUG("tx_dropped = %8lu\n", net_data->tx_dropped); + P_DEBUG("collisions = %8lu\n", net_data->collisions); + + P_DEBUG("sync_count_1 = %u\n", net_data->sync_count_1); + P_DEBUG("sync_count_2 = %u\n", net_data->sync_count_2); + P_DEBUG("timestamp = %lX\n", net_data->timestamp); +} + +/* + * appldata_get_net_sum_data() + * + * gather accumulated network statistics + */ +static void appldata_get_net_sum_data(void *data) +{ + int i; + struct appldata_net_sum_data *net_data; + struct net_device *dev; + struct net_device_stats *stats; + unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes, rx_errors, + tx_errors, rx_dropped, tx_dropped, collisions; + + net_data = data; + net_data->sync_count_1++; + + i = 0; + rx_packets = 0; + tx_packets = 0; + rx_bytes = 0; + tx_bytes = 0; + rx_errors = 0; + tx_errors = 0; + rx_dropped = 0; + tx_dropped = 0; + collisions = 0; + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { + if (dev->get_stats == NULL) { + continue; + } + stats = dev->get_stats(dev); + rx_packets += stats->rx_packets; + tx_packets += stats->tx_packets; + rx_bytes += stats->rx_bytes; + tx_bytes += stats->tx_bytes; + rx_errors += stats->rx_errors; + tx_errors += stats->tx_errors; + rx_dropped += stats->rx_dropped; + tx_dropped += stats->tx_dropped; + collisions += stats->collisions; + i++; + } + read_unlock(&dev_base_lock); + net_data->nr_interfaces = i; + net_data->rx_packets = rx_packets; + net_data->tx_packets = tx_packets; + net_data->rx_bytes = rx_bytes; + net_data->tx_bytes = tx_bytes; + net_data->rx_errors = rx_errors; + net_data->tx_errors = tx_errors; + net_data->rx_dropped = rx_dropped; + net_data->tx_dropped = tx_dropped; + net_data->collisions = collisions; + + net_data->timestamp = get_clock(); + net_data->sync_count_2++; +#ifdef APPLDATA_DEBUG + appldata_print_debug(net_data); +#endif +} + + +static struct appldata_ops ops = { + .ctl_nr = CTL_APPLDATA_NET_SUM, + .name = "net_sum", + .record_nr = APPLDATA_RECORD_NET_SUM_ID, + .size = sizeof(struct appldata_net_sum_data), + .callback = &appldata_get_net_sum_data, + .data = &appldata_net_sum_data, + .owner = THIS_MODULE, +}; + + +/* + * appldata_net_init() + * + * init data, register ops + */ +static int __init appldata_net_init(void) +{ + int rc; + + P_DEBUG("sizeof(net) = %lu\n", sizeof(struct appldata_net_sum_data)); + + rc = appldata_register_ops(&ops); + if (rc != 0) { + P_ERROR("Error registering ops, rc = %i\n", rc); + } else { + P_DEBUG("%s-ops registered!\n", ops.name); + } + return rc; +} + +/* + * appldata_net_exit() + * + * unregister ops + */ +static void __exit appldata_net_exit(void) +{ + appldata_unregister_ops(&ops); + P_DEBUG("%s-ops unregistered!\n", ops.name); +} + + +module_init(appldata_net_init); +module_exit(appldata_net_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gerald Schaefer"); +MODULE_DESCRIPTION("Linux-VM Monitor Stream, accumulated network statistics"); diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c new file mode 100644 index 000000000000..5c79ad9ce2f5 --- /dev/null +++ b/arch/s390/appldata/appldata_os.c @@ -0,0 +1,231 @@ +/* + * arch/s390/appldata/appldata_os.c + * + * Data gathering module for Linux-VM Monitor Stream, Stage 1. + * Collects misc. OS related data (CPU utilization, running processes). + * + * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH. + * + * Author: Gerald Schaefer <geraldsc@de.ibm.com> + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/netdevice.h> +#include <linux/sched.h> +#include <asm/smp.h> + +#include "appldata.h" + + +#define MY_PRINT_NAME "appldata_os" /* for debug messages, etc. */ +#define LOAD_INT(x) ((x) >> FSHIFT) +#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) + +/* + * OS data + */ +struct appldata_os_per_cpu { + u32 per_cpu_user; /* timer ticks spent in user mode */ + u32 per_cpu_nice; /* ... spent with modified priority */ + u32 per_cpu_system; /* ... spent in kernel mode */ + u32 per_cpu_idle; /* ... spent in idle mode */ + +// New in 2.6 --> + u32 per_cpu_irq; /* ... spent in interrupts */ + u32 per_cpu_softirq; /* ... spent in softirqs */ + u32 per_cpu_iowait; /* ... spent while waiting for I/O */ +// <-- New in 2.6 +}; + +struct appldata_os_data { + u64 timestamp; + u32 sync_count_1; /* after VM collected the record data, */ + u32 sync_count_2; /* sync_count_1 and sync_count_2 should be the + same. If not, the record has been updated on + the Linux side while VM was collecting the + (possibly corrupt) data */ + + u32 nr_cpus; /* number of (virtual) CPUs */ + u32 per_cpu_size; /* size of the per-cpu data struct */ + u32 cpu_offset; /* offset of the first per-cpu data struct */ + + u32 nr_running; /* number of runnable threads */ + u32 nr_threads; /* number of threads */ + u32 avenrun[3]; /* average nr. of running processes during */ + /* the last 1, 5 and 15 minutes */ + +// New in 2.6 --> + u32 nr_iowait; /* number of blocked threads + (waiting for I/O) */ +// <-- New in 2.6 + + /* per cpu data */ + struct appldata_os_per_cpu os_cpu[0]; +}; + +static struct appldata_os_data *appldata_os_data; + + +static inline void appldata_print_debug(struct appldata_os_data *os_data) +{ + int a0, a1, a2, i; + + P_DEBUG("--- OS - RECORD ---\n"); + P_DEBUG("nr_threads = %u\n", os_data->nr_threads); + P_DEBUG("nr_running = %u\n", os_data->nr_running); + P_DEBUG("nr_iowait = %u\n", os_data->nr_iowait); + P_DEBUG("avenrun(int) = %8x / %8x / %8x\n", os_data->avenrun[0], + os_data->avenrun[1], os_data->avenrun[2]); + a0 = os_data->avenrun[0]; + a1 = os_data->avenrun[1]; + a2 = os_data->avenrun[2]; + P_DEBUG("avenrun(float) = %d.%02d / %d.%02d / %d.%02d\n", + LOAD_INT(a0), LOAD_FRAC(a0), LOAD_INT(a1), LOAD_FRAC(a1), + LOAD_INT(a2), LOAD_FRAC(a2)); + + P_DEBUG("nr_cpus = %u\n", os_data->nr_cpus); + for (i = 0; i < NR_CPUS; i++) { + if (!cpu_online(i)) continue; + P_DEBUG("cpu%u : user = %u, nice = %u, system = %u, " + "idle = %u, irq = %u, softirq = %u, iowait = %u\n", + i, + os_data->os_cpu[i].per_cpu_user, + os_data->os_cpu[i].per_cpu_nice, + os_data->os_cpu[i].per_cpu_system, + os_data->os_cpu[i].per_cpu_idle, + os_data->os_cpu[i].per_cpu_irq, + os_data->os_cpu[i].per_cpu_softirq, + os_data->os_cpu[i].per_cpu_iowait); + } + + P_DEBUG("sync_count_1 = %u\n", os_data->sync_count_1); + P_DEBUG("sync_count_2 = %u\n", os_data->sync_count_2); + P_DEBUG("timestamp = %lX\n", os_data->timestamp); +} + +/* + * appldata_get_os_data() + * + * gather OS data + */ +static void appldata_get_os_data(void *data) +{ + int i; + struct appldata_os_data *os_data; + + os_data = data; + os_data->sync_count_1++; + + os_data->nr_cpus = num_online_cpus(); + + os_data->nr_threads = nr_threads; + os_data->nr_running = nr_running(); + os_data->nr_iowait = nr_iowait(); + os_data->avenrun[0] = avenrun[0] + (FIXED_1/200); + os_data->avenrun[1] = avenrun[1] + (FIXED_1/200); + os_data->avenrun[2] = avenrun[2] + (FIXED_1/200); + + for (i = 0; i < num_online_cpus(); i++) { + os_data->os_cpu[i].per_cpu_user = + kstat_cpu(i).cpustat.user; + os_data->os_cpu[i].per_cpu_nice = + kstat_cpu(i).cpustat.nice; + os_data->os_cpu[i].per_cpu_system = + kstat_cpu(i).cpustat.system; + os_data->os_cpu[i].per_cpu_idle = + kstat_cpu(i).cpustat.idle; + os_data->os_cpu[i].per_cpu_irq = + kstat_cpu(i).cpustat.irq; + os_data->os_cpu[i].per_cpu_softirq = + kstat_cpu(i).cpustat.softirq; + os_data->os_cpu[i].per_cpu_iowait = + kstat_cpu(i).cpustat.iowait; + } + + os_data->timestamp = get_clock(); + os_data->sync_count_2++; +#ifdef APPLDATA_DEBUG + appldata_print_debug(os_data); +#endif +} + + +static struct appldata_ops ops = { + .ctl_nr = CTL_APPLDATA_OS, + .name = "os", + .record_nr = APPLDATA_RECORD_OS_ID, + .callback = &appldata_get_os_data, + .owner = THIS_MODULE, +}; + + +/* + * appldata_os_init() + * + * init data, register ops + */ +static int __init appldata_os_init(void) +{ + int rc, size; + + size = sizeof(struct appldata_os_data) + + (NR_CPUS * sizeof(struct appldata_os_per_cpu)); + if (size > APPLDATA_MAX_REC_SIZE) { + P_ERROR("Size of record = %i, bigger than maximum (%i)!\n", + size, APPLDATA_MAX_REC_SIZE); + rc = -ENOMEM; + goto out; + } + P_DEBUG("sizeof(os) = %i, sizeof(os_cpu) = %lu\n", size, + sizeof(struct appldata_os_per_cpu)); + + appldata_os_data = kmalloc(size, GFP_DMA); + if (appldata_os_data == NULL) { + P_ERROR("No memory for %s!\n", ops.name); + rc = -ENOMEM; + goto out; + } + memset(appldata_os_data, 0, size); + + appldata_os_data->per_cpu_size = sizeof(struct appldata_os_per_cpu); + appldata_os_data->cpu_offset = offsetof(struct appldata_os_data, + os_cpu); + P_DEBUG("cpu offset = %u\n", appldata_os_data->cpu_offset); + + ops.data = appldata_os_data; + ops.size = size; + rc = appldata_register_ops(&ops); + if (rc != 0) { + P_ERROR("Error registering ops, rc = %i\n", rc); + kfree(appldata_os_data); + } else { + P_DEBUG("%s-ops registered!\n", ops.name); + } +out: + return rc; +} + +/* + * appldata_os_exit() + * + * unregister ops + */ +static void __exit appldata_os_exit(void) +{ + appldata_unregister_ops(&ops); + kfree(appldata_os_data); + P_DEBUG("%s-ops unregistered!\n", ops.name); +} + + +module_init(appldata_os_init); +module_exit(appldata_os_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gerald Schaefer"); +MODULE_DESCRIPTION("Linux-VM Monitor Stream, OS statistics"); diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 948d0185779a..123fcb72fb80 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -76,6 +76,8 @@ CONFIG_BINFMT_MISC=m # CONFIG_PROCESS_DEBUG is not set CONFIG_PFAULT=y # CONFIG_SHARED_KERNEL is not set +# CONFIG_CMM is not set +# CONFIG_VIRT_TIMER is not set # CONFIG_PCMCIA is not set # @@ -132,11 +134,13 @@ CONFIG_BLK_DEV_INITRD=y # S/390 block device drivers # CONFIG_BLK_DEV_XPRAM=m +# CONFIG_DCSSBLK is not set CONFIG_DASD=y # CONFIG_DASD_PROFILE is not set CONFIG_DASD_ECKD=y CONFIG_DASD_FBA=y CONFIG_DASD_DIAG=y +# CONFIG_DASD_CMB is not set # # Multi-device support (RAID and LVM) @@ -311,6 +315,8 @@ CONFIG_NET_ETHERNET=y CONFIG_LCS=m CONFIG_CTC=m CONFIG_IUCV=m +# CONFIG_NETIUCV is not set +# CONFIG_SMSGIUCV is not set CONFIG_QETH=y # diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 88dcd496d27d..9029c0e6ff0c 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -40,6 +40,9 @@ #include <asm/io.h> #include <asm/processor.h> #include <asm/irq.h> +#ifdef CONFIG_VIRT_TIMER +#include <asm/timer.h> +#endif asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); @@ -77,6 +80,14 @@ void default_idle(void) return; } +#ifdef CONFIG_VIRT_TIMER + /* + * hook to stop timers that should not tick while CPU is idle + */ + if (stop_timers()) + return; +#endif + /* * Wait for external, I/O or machine check interrupt and * switch off machine check bit after the wait has ended. diff --git a/arch/s390/kernel/s390_ext.c b/arch/s390/kernel/s390_ext.c index 2337751b3c78..c00e5e328c6c 100644 --- a/arch/s390/kernel/s390_ext.c +++ b/arch/s390/kernel/s390_ext.c @@ -111,6 +111,7 @@ void do_extint(struct pt_regs *regs, unsigned short code) int index; irq_enter(); + asm volatile ("mc 0,0"); if (S390_lowcore.int_clock >= S390_lowcore.jiffy_timer) account_ticks(regs); kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++; diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c index 078b427d8d2d..b51f748069a4 100644 --- a/arch/s390/kernel/s390_ksyms.c +++ b/arch/s390/kernel/s390_ksyms.c @@ -19,6 +19,9 @@ #ifdef CONFIG_IP_MULTICAST #include <net/arp.h> #endif +#ifdef CONFIG_VIRT_TIMER +#include <asm/timer.h> +#endif /* * memory management @@ -29,6 +32,7 @@ EXPORT_SYMBOL_NOVERS(_zb_findmap); EXPORT_SYMBOL_NOVERS(__copy_from_user_asm); EXPORT_SYMBOL_NOVERS(__copy_to_user_asm); EXPORT_SYMBOL_NOVERS(__clear_user_asm); +EXPORT_SYMBOL(diag10); /* * semaphore ops @@ -54,6 +58,7 @@ EXPORT_SYMBOL_NOVERS(strnlen); EXPORT_SYMBOL_NOVERS(strrchr); EXPORT_SYMBOL_NOVERS(strstr); EXPORT_SYMBOL_NOVERS(strpbrk); +EXPORT_SYMBOL_NOVERS(strcpy); /* * binfmt_elf loader @@ -65,6 +70,17 @@ EXPORT_SYMBOL(overflowgid); EXPORT_SYMBOL(empty_zero_page); /* + * virtual CPU timer + */ +#ifdef CONFIG_VIRT_TIMER +EXPORT_SYMBOL(init_virt_timer); +EXPORT_SYMBOL(add_virt_timer); +EXPORT_SYMBOL(add_virt_timer_periodic); +EXPORT_SYMBOL(mod_virt_timer); +EXPORT_SYMBOL(del_virt_timer); +#endif + +/* * misc. */ EXPORT_SYMBOL(machine_flags); @@ -76,5 +92,5 @@ EXPORT_SYMBOL(console_device); EXPORT_SYMBOL_NOVERS(do_call_softirq); EXPORT_SYMBOL(sys_wait4); EXPORT_SYMBOL(cpcmd); +EXPORT_SYMBOL(smp_call_function_on); EXPORT_SYMBOL(sys_ioctl); - diff --git a/arch/s390/kernel/semaphore.c b/arch/s390/kernel/semaphore.c index f6cfdd7c00a8..8203f5e0228d 100644 --- a/arch/s390/kernel/semaphore.c +++ b/arch/s390/kernel/semaphore.c @@ -33,8 +33,9 @@ static inline int __sem_update_count(struct semaphore *sem, int incr) " cs %0,%1,0(%3)\n" " jl 0b\n" : "=&d" (old_val), "=&d" (new_val), - "+m" (sem->count) - : "a" (&sem->count), "d" (incr) : "cc" ); + "=m" (sem->count) + : "a" (&sem->count), "d" (incr), "m" (sem->count) + : "cc" ); return old_val; } diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 14646c5a10b3..c528cb0acfd7 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -30,6 +30,7 @@ #include <linux/delay.h> #include <linux/cache.h> +#include <linux/interrupt.h> #include <asm/sigp.h> #include <asm/pgalloc.h> @@ -65,7 +66,7 @@ extern char vmpoff_cmd[]; extern void do_reipl(unsigned long devno); -static sigp_ccode smp_ext_bitcall(int, ec_bit_sig); +static void smp_ext_bitcall(int, ec_bit_sig); static void smp_ext_bitcall_others(ec_bit_sig); /* @@ -150,6 +151,59 @@ int smp_call_function (void (*func) (void *info), void *info, int nonatomic, return 0; } +/* + * Call a function on one CPU + * cpu : the CPU the function should be executed on + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler. You may call it from a bottom half. + * + * It is guaranteed that the called function runs on the specified CPU, + * preemption is disabled. + */ +int smp_call_function_on(void (*func) (void *info), void *info, + int nonatomic, int wait, int cpu) +{ + struct call_data_struct data; + int curr_cpu; + + if (!cpu_online(cpu)) + return -EINVAL; + + /* disable preemption for local function call */ + curr_cpu = get_cpu(); + + if (curr_cpu == cpu) { + /* direct call to function */ + func(info); + put_cpu(); + return 0; + } + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock_bh(&call_lock); + call_data = &data; + smp_ext_bitcall(cpu, ec_call_function); + + /* Wait for response */ + while (atomic_read(&data.started) != 1) + cpu_relax(); + + if (wait) + while (atomic_read(&data.finished) != 1) + cpu_relax(); + + spin_unlock_bh(&call_lock); + put_cpu(); + return 0; +} + static inline void do_send_stop(void) { u32 dummy; @@ -305,16 +359,14 @@ void do_ext_call_interrupt(struct pt_regs *regs, __u16 code) * Send an external call sigp to another cpu and return without waiting * for its completion. */ -static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) +static void smp_ext_bitcall(int cpu, ec_bit_sig sig) { - sigp_ccode ccode; - /* * Set signaling bit in lowcore of target cpu and kick it */ set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast); - ccode = signal_processor(cpu, sigp_external_call); - return ccode; + while(signal_processor(cpu, sigp_external_call) == sigp_busy) + udelay(10); } /* @@ -350,6 +402,7 @@ void smp_ptlb_all(void) { on_each_cpu(smp_ptlb_callback, NULL, 0, 1); } +EXPORT_SYMBOL(smp_ptlb_all); #endif /* ! CONFIG_ARCH_S390X */ /* diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 1f772fe96af5..75b4d0f3babd 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -24,16 +24,17 @@ #include <linux/init.h> #include <linux/smp.h> #include <linux/types.h> +#include <linux/timex.h> +#include <linux/config.h> #include <asm/uaccess.h> #include <asm/delay.h> #include <asm/s390_ext.h> #include <asm/div64.h> - -#include <linux/timex.h> -#include <linux/config.h> - #include <asm/irq.h> +#ifdef CONFIG_VIRT_TIMER +#include <asm/timer.h> +#endif /* change this if you have some constant time drift */ #define USECS_PER_JIFFY ((unsigned long) 1000000/HZ) @@ -51,19 +52,25 @@ u64 jiffies_64 = INITIAL_JIFFIES; EXPORT_SYMBOL(jiffies_64); -static ext_int_info_t ext_int_info_timer; +static ext_int_info_t ext_int_info_cc; static u64 init_timer_cc; static u64 jiffies_timer_cc; static u64 xtime_cc; extern unsigned long wall_jiffies; +#ifdef CONFIG_VIRT_TIMER +#define VTIMER_MAGIC (0x4b87ad6e + 1) +static ext_int_info_t ext_int_info_timer; +DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer); +#endif + /* * Scheduler clock - returns current time in nanosec units. */ unsigned long long sched_clock(void) { - return (get_clock() - jiffies_timer_cc) >> 2; + return ((get_clock() - jiffies_timer_cc) * 1000) >> 12; } void tod_to_timeval(__u64 todval, struct timespec *xtime) @@ -226,13 +233,208 @@ void account_ticks(struct pt_regs *regs) #endif } +#ifdef CONFIG_VIRT_TIMER +void start_cpu_timer(void) +{ + struct vtimer_queue *vt_list; + + vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + set_vtimer(vt_list->idle); +} + +int stop_cpu_timer(void) +{ + __u64 done; + struct vtimer_queue *vt_list; + + vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + + /* nothing to do */ + if (list_empty(&vt_list->list)) { + vt_list->idle = VTIMER_MAX_SLICE; + goto fire; + } + + /* store progress */ + asm volatile ("STPT %0" : "=m" (done)); + + /* + * If done is negative we do not stop the CPU timer + * because we will get instantly an interrupt that + * will start the CPU timer again. + */ + if (done & 1LL<<63) + return 1; + else + vt_list->offset += vt_list->to_expire - done; + + /* save the actual expire value */ + vt_list->idle = done; + + /* + * We cannot halt the CPU timer, we just write a value that + * nearly never expires (only after 71 years) and re-write + * the stored expire value if we continue the timer + */ + fire: + set_vtimer(VTIMER_MAX_SLICE); + return 0; +} + +void do_monitor_call(struct pt_regs *regs, long interruption_code) +{ + /* disable monitor call class 0 */ + __ctl_clear_bit(8, 15); + + start_cpu_timer(); +} + +/* + * called from cpu_idle to stop any timers + * returns 1 if CPU should not be stopped + */ +int stop_timers(void) +{ + if (stop_cpu_timer()) + return 1; + + /* enable monitor call class 0 */ + __ctl_set_bit(8, 15); + + return 0; +} + +void set_vtimer(__u64 expires) +{ + asm volatile ("SPT %0" : : "m" (expires)); + + /* store expire time for this CPU timer */ + per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires; +} + +/* + * Sorted add to a list. List is linear searched until first bigger + * element is found. + */ +void list_add_sorted(struct vtimer_list *timer, struct list_head *head) +{ + struct vtimer_list *event; + + list_for_each_entry(event, head, entry) { + if (event->expires > timer->expires) { + list_add_tail(&timer->entry, &event->entry); + return; + } + } + list_add_tail(&timer->entry, head); +} + +/* + * Do the callback functions of expired vtimer events. + * Called from within the interrupt handler. + */ +static void do_callbacks(struct list_head *cb_list, struct pt_regs *regs) +{ + struct vtimer_queue *vt_list; + struct vtimer_list *event, *tmp; + void (*fn)(unsigned long, struct pt_regs*); + unsigned long data; + + if (list_empty(cb_list)) + return; + + vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + + list_for_each_entry_safe(event, tmp, cb_list, entry) { + fn = event->function; + data = event->data; + fn(data, regs); + + if (!event->interval) + /* delete one shot timer */ + list_del_init(&event->entry); + else { + /* move interval timer back to list */ + spin_lock(&vt_list->lock); + list_del_init(&event->entry); + list_add_sorted(event, &vt_list->list); + spin_unlock(&vt_list->lock); + } + } +} + +/* + * Handler for the virtual CPU timer. + */ +static void do_cpu_timer_interrupt(struct pt_regs *regs, __u16 error_code) +{ + int cpu; + __u64 next, delta; + struct vtimer_queue *vt_list; + struct vtimer_list *event, *tmp; + struct list_head *ptr; + /* the callback queue */ + struct list_head cb_list; + + INIT_LIST_HEAD(&cb_list); + cpu = smp_processor_id(); + vt_list = &per_cpu(virt_cpu_timer, cpu); + + /* walk timer list, fire all expired events */ + spin_lock(&vt_list->lock); + + if (vt_list->to_expire < VTIMER_MAX_SLICE) + vt_list->offset += vt_list->to_expire; + + list_for_each_entry_safe(event, tmp, &vt_list->list, entry) { + if (event->expires > vt_list->offset) + /* found first unexpired event, leave */ + break; + + /* re-charge interval timer, we have to add the offset */ + if (event->interval) + event->expires = event->interval + vt_list->offset; + + /* move expired timer to the callback queue */ + list_move_tail(&event->entry, &cb_list); + } + spin_unlock(&vt_list->lock); + do_callbacks(&cb_list, regs); + + /* next event is first in list */ + spin_lock(&vt_list->lock); + if (!list_empty(&vt_list->list)) { + ptr = vt_list->list.next; + event = list_entry(ptr, struct vtimer_list, entry); + next = event->expires - vt_list->offset; + + /* add the expired time from this interrupt handler + * and the callback functions + */ + asm volatile ("STPT %0" : "=m" (delta)); + delta = 0xffffffffffffffffLL - delta + 1; + vt_list->offset += delta; + next -= delta; + } else { + vt_list->offset = 0; + next = VTIMER_MAX_SLICE; + } + spin_unlock(&vt_list->lock); + set_vtimer(next); +} +#endif + /* - * Start the clock comparator on the current CPU. + * Start the clock comparator and the virtual CPU timer + * on the current CPU. */ void init_cpu_timer(void) { unsigned long cr0; __u64 timer; +#ifdef CONFIG_VIRT_TIMER + struct vtimer_queue *vt_list; +#endif timer = jiffies_timer_cc + jiffies_64 * CLK_TICKS_PER_JIFFY; S390_lowcore.jiffy_timer = timer + CLK_TICKS_PER_JIFFY; @@ -242,6 +444,22 @@ void init_cpu_timer(void) __ctl_store(cr0, 0, 0); cr0 |= 0x800; __ctl_load(cr0, 0, 0); + +#ifdef CONFIG_VIRT_TIMER + /* kick the virtual timer */ + timer = VTIMER_MAX_SLICE; + asm volatile ("SPT %0" : : "m" (timer)); + __ctl_store(cr0, 0, 0); + cr0 |= 0x400; + __ctl_load(cr0, 0, 0); + + vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + INIT_LIST_HEAD(&vt_list->list); + spin_lock_init(&vt_list->lock); + vt_list->to_expire = 0; + vt_list->offset = 0; + vt_list->idle = 0; +#endif } /* @@ -281,11 +499,252 @@ void __init time_init(void) set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); - /* request the 0x1004 external interrupt */ + /* request the clock comparator external interrupt */ if (register_early_external_interrupt(0x1004, 0, - &ext_int_info_timer) != 0) + &ext_int_info_cc) != 0) panic("Couldn't request external interrupt 0x1004"); - /* init CPU timer */ +#ifdef CONFIG_VIRT_TIMER + /* request the cpu timer external interrupt */ + if (register_early_external_interrupt(0x1005, do_cpu_timer_interrupt, + &ext_int_info_timer) != 0) + panic("Couldn't request external interrupt 0x1005"); +#endif + init_cpu_timer(); } + +#ifdef CONFIG_VIRT_TIMER +void init_virt_timer(struct vtimer_list *timer) +{ + timer->magic = VTIMER_MAGIC; + timer->function = NULL; + INIT_LIST_HEAD(&timer->entry); + spin_lock_init(&timer->lock); +} + +static inline int check_vtimer(struct vtimer_list *timer) +{ + if (timer->magic != VTIMER_MAGIC) + return -EINVAL; + return 0; +} + +static inline int vtimer_pending(struct vtimer_list *timer) +{ + return (!list_empty(&timer->entry)); +} + +/* + * this function should only run on the specified CPU + */ +static void internal_add_vtimer(struct vtimer_list *timer) +{ + unsigned long flags; + __u64 done; + struct vtimer_list *event; + struct vtimer_queue *vt_list; + + vt_list = &per_cpu(virt_cpu_timer, timer->cpu); + spin_lock_irqsave(&vt_list->lock, flags); + + if (timer->cpu != smp_processor_id()) + printk("internal_add_vtimer: BUG, running on wrong CPU"); + + /* if list is empty we only have to set the timer */ + if (list_empty(&vt_list->list)) { + /* reset the offset, this may happen if the last timer was + * just deleted by mod_virt_timer and the interrupt + * didn't happen until here + */ + vt_list->offset = 0; + goto fire; + } + + /* save progress */ + asm volatile ("STPT %0" : "=m" (done)); + + /* calculate completed work */ + done = vt_list->to_expire - done + vt_list->offset; + vt_list->offset = 0; + + list_for_each_entry(event, &vt_list->list, entry) + event->expires -= done; + + fire: + list_add_sorted(timer, &vt_list->list); + + /* get first element, which is the next vtimer slice */ + event = list_entry(vt_list->list.next, struct vtimer_list, entry); + + set_vtimer(event->expires); + spin_unlock_irqrestore(&vt_list->lock, flags); + /* release CPU aquired in prepare_vtimer or mod_virt_timer() */ + put_cpu(); +} + +static inline int prepare_vtimer(struct vtimer_list *timer) +{ + if (check_vtimer(timer) || !timer->function) { + printk("add_virt_timer: uninitialized timer\n"); + return -EINVAL; + } + + if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) { + printk("add_virt_timer: invalid timer expire value!\n"); + return -EINVAL; + } + + if (vtimer_pending(timer)) { + printk("add_virt_timer: timer pending\n"); + return -EBUSY; + } + + timer->cpu = get_cpu(); + return 0; +} + +/* + * add_virt_timer - add an oneshot virtual CPU timer + */ +void add_virt_timer(void *new) +{ + struct vtimer_list *timer; + + timer = (struct vtimer_list *)new; + + if (prepare_vtimer(timer) < 0) + return; + + timer->interval = 0; + internal_add_vtimer(timer); +} + +/* + * add_virt_timer_int - add an interval virtual CPU timer + */ +void add_virt_timer_periodic(void *new) +{ + struct vtimer_list *timer; + + timer = (struct vtimer_list *)new; + + if (prepare_vtimer(timer) < 0) + return; + + timer->interval = timer->expires; + internal_add_vtimer(timer); +} + +/* + * If we change a pending timer the function must be called on the CPU + * where the timer is running on, e.g. by smp_call_function_on() + * + * The original mod_timer adds the timer if it is not pending. For compatibility + * we do the same. The timer will be added on the current CPU as a oneshot timer. + * + * returns whether it has modified a pending timer (1) or not (0) + */ +int mod_virt_timer(struct vtimer_list *timer, __u64 expires) +{ + struct vtimer_queue *vt_list; + unsigned long flags; + int cpu; + + if (check_vtimer(timer) || !timer->function) { + printk("mod_virt_timer: uninitialized timer\n"); + return -EINVAL; + } + + if (!expires || expires > VTIMER_MAX_SLICE) { + printk("mod_virt_timer: invalid expire range\n"); + return -EINVAL; + } + + /* + * This is a common optimization triggered by the + * networking code - if the timer is re-modified + * to be the same thing then just return: + */ + if (timer->expires == expires && vtimer_pending(timer)) + return 1; + + cpu = get_cpu(); + vt_list = &per_cpu(virt_cpu_timer, cpu); + + /* disable interrupts before test if timer is pending */ + spin_lock_irqsave(&vt_list->lock, flags); + + /* if timer isn't pending add it on the current CPU */ + if (!vtimer_pending(timer)) { + spin_unlock_irqrestore(&vt_list->lock, flags); + /* we do not activate an interval timer with mod_virt_timer */ + timer->interval = 0; + timer->expires = expires; + timer->cpu = cpu; + internal_add_vtimer(timer); + return 0; + } + + /* check if we run on the right CPU */ + if (timer->cpu != cpu) { + printk("mod_virt_timer: running on wrong CPU, check your code\n"); + spin_unlock_irqrestore(&vt_list->lock, flags); + put_cpu(); + return -EINVAL; + } + + list_del_init(&timer->entry); + timer->expires = expires; + + /* also change the interval if we have an interval timer */ + if (timer->interval) + timer->interval = expires; + + /* the timer can't expire anymore so we can release the lock */ + spin_unlock_irqrestore(&vt_list->lock, flags); + internal_add_vtimer(timer); + return 1; +} + +/* + * delete a virtual timer + * + * returns whether the deleted timer was pending (1) or not (0) + */ +int del_virt_timer(struct vtimer_list *timer) +{ + unsigned long flags; + struct vtimer_queue *vt_list; + + if (check_vtimer(timer)) { + printk("del_virt_timer: timer not initialized\n"); + return -EINVAL; + } + + /* check if timer is pending */ + if (!vtimer_pending(timer)) + return 0; + + if (!cpu_online(timer->cpu)) { + printk("del_virt_timer: CPU not present!\n"); + return -1; + } + + vt_list = &per_cpu(virt_cpu_timer, timer->cpu); + spin_lock_irqsave(&vt_list->lock, flags); + + /* we don't interrupt a running timer, just let it expire! */ + list_del_init(&timer->entry); + + /* last timer removed */ + if (list_empty(&vt_list->list)) { + vt_list->to_expire = 0; + vt_list->offset = 0; + } + + spin_unlock_irqrestore(&vt_list->lock, flags); + return 1; +} +#endif + diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 4031e1f6ede3..26291f8d748a 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -64,6 +64,9 @@ extern void pfault_fini(void); extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code); static ext_int_info_t ext_int_pfault; #endif +#ifdef CONFIG_VIRT_TIMER +extern pgm_check_handler_t do_monitor_call; +#endif #define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; }) @@ -625,6 +628,9 @@ void __init trap_init(void) #endif /* CONFIG_ARCH_S390X */ pgm_check_table[0x15] = &operand_exception; pgm_check_table[0x1C] = &privileged_op; +#ifdef CONFIG_VIRT_TIMER + pgm_check_table[0x40] = &do_monitor_call; +#endif if (MACHINE_IS_VM) { /* * First try to get pfault pseudo page faults going. diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index b94603bdc639..3cf8418ae438 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -5,5 +5,5 @@ EXTRA_AFLAGS := -traditional lib-y += delay.o -lib-$(CONFIG_ARCH_S390_31) += memset.o strcmp.o strncpy.o uaccess.o -lib-$(CONFIG_ARCH_S390X) += memset64.o strcmp64.o strncpy64.o uaccess64.o +lib-$(CONFIG_ARCH_S390_31) += memset.o strcmp.o strcpy.o strncpy.o uaccess.o +lib-$(CONFIG_ARCH_S390X) += memset64.o strcmp64.o strcpy64.o strncpy64.o uaccess64.o diff --git a/arch/s390/lib/strcpy.S b/arch/s390/lib/strcpy.S new file mode 100644 index 000000000000..1d36b9cb8841 --- /dev/null +++ b/arch/s390/lib/strcpy.S @@ -0,0 +1,20 @@ +/* + * arch/s390/kernel/strcpy.S + * S390 strcpy routine + * + * S390 version + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address of destination + * R3 = address of source string + */ + .globl strcpy +strcpy: + sr %r0,%r0 +0: mvst %r2,%r3 + jo 0b + br %r14 + diff --git a/arch/s390/lib/strcpy64.S b/arch/s390/lib/strcpy64.S new file mode 100644 index 000000000000..06815dcd7d6b --- /dev/null +++ b/arch/s390/lib/strcpy64.S @@ -0,0 +1,20 @@ +/* + * arch/s390/kernel/strcpy.S + * S390 strcpy routine + * + * S390 version + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address of destination + * R3 = address of source string + */ + .globl strcpy +strcpy: + sgr %r0,%r0 +0: mvst %r2,%r3 + jo 0b + br %r14 + diff --git a/arch/s390/lib/strncpy.S b/arch/s390/lib/strncpy.S index 3065be2b440c..a3285bd0494e 100644 --- a/arch/s390/lib/strncpy.S +++ b/arch/s390/lib/strncpy.S @@ -23,8 +23,13 @@ strncpy_loop: LA 3,1(3) STC 0,0(1) LA 1,1(1) - JZ strncpy_exit # ICM inserted a 0x00 + JZ strncpy_pad # ICM inserted a 0x00 BRCT 4,strncpy_loop # R4 -= 1, jump to strncpy_loop if > 0 strncpy_exit: BR 14 - +strncpy_clear: + STC 0,0(1) + LA 1,1(1) +strncpy_pad: + BRCT 4,strncpy_clear + BR 14 diff --git a/arch/s390/lib/strncpy64.S b/arch/s390/lib/strncpy64.S index 0360a38c3951..1e455e52b3f5 100644 --- a/arch/s390/lib/strncpy64.S +++ b/arch/s390/lib/strncpy64.S @@ -23,8 +23,13 @@ strncpy_loop: LA 3,1(3) STC 0,0(1) LA 1,1(1) - JZ strncpy_exit # ICM inserted a 0x00 + JZ strncpy_pad # ICM inserted a 0x00 BRCTG 4,strncpy_loop # R4 -= 1, jump to strncpy_loop if > 0 strncpy_exit: BR 14 - +strncpy_clear: + STC 0,0(1) + LA 1,1(1) +strncpy_pad: + BRCTG 4,strncpy_clear + BR 14 diff --git a/arch/s390/math-emu/math.c b/arch/s390/math-emu/math.c index c203afbe1a3f..648df7140335 100644 --- a/arch/s390/math-emu/math.c +++ b/arch/s390/math-emu/math.c @@ -99,7 +99,6 @@ int sysctl_ieee_emulation_warnings=1; static void display_emulation_not_implemented(struct pt_regs *regs, char *instr) { - struct pt_regs *regs; __u16 *location; #ifdef CONFIG_SYSCTL diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile index 3360e50a2559..f8583caa605c 100644 --- a/arch/s390/mm/Makefile +++ b/arch/s390/mm/Makefile @@ -2,4 +2,6 @@ # Makefile for the linux s390-specific parts of the memory manager. # -obj-y := init.o fault.o ioremap.o +obj-y := init.o fault.o ioremap.o extmem.o +obj-$(CONFIG_CMM) += cmm.o + diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c new file mode 100644 index 000000000000..f3221ed4e4c3 --- /dev/null +++ b/arch/s390/mm/cmm.c @@ -0,0 +1,445 @@ +/* + * arch/s390/mm/cmm.c + * + * S390 version + * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * Collaborative memory management interface. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/sysctl.h> +#include <linux/ctype.h> + +#include <asm/pgalloc.h> +#include <asm/uaccess.h> + +#include "../../../drivers/s390/net/smsgiucv.h" + +#define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2) + +struct cmm_page_array { + struct cmm_page_array *next; + unsigned long index; + unsigned long pages[CMM_NR_PAGES]; +}; + +static long cmm_pages = 0; +static long cmm_timed_pages = 0; +static volatile long cmm_pages_target = 0; +static volatile long cmm_timed_pages_target = 0; +static long cmm_timeout_pages = 0; +static long cmm_timeout_seconds = 0; + +static struct cmm_page_array *cmm_page_list = 0; +static struct cmm_page_array *cmm_timed_page_list = 0; + +static unsigned long cmm_thread_active = 0; +static struct work_struct cmm_thread_starter; +static wait_queue_head_t cmm_thread_wait; +static struct timer_list cmm_timer; + +static void cmm_timer_fn(unsigned long); +static void cmm_set_timer(void); + +static long +cmm_strtoul(const char *cp, char **endp) +{ + unsigned int base = 10; + + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x' || *cp == 'X') && isxdigit(cp[1])) { + base = 16; + cp++; + } + } + return simple_strtoul(cp, endp, base); +} + +static long +cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list) +{ + struct cmm_page_array *pa; + unsigned long page; + + pa = *list; + while (pages) { + page = __get_free_page(GFP_NOIO); + if (!page) + break; + if (!pa || pa->index >= CMM_NR_PAGES) { + /* Need a new page for the page list. */ + pa = (struct cmm_page_array *) + __get_free_page(GFP_NOIO); + if (!pa) { + free_page(page); + break; + } + pa->next = *list; + pa->index = 0; + *list = pa; + } + if (page < 0x80000000UL) + diag10(page); + pa->pages[pa->index++] = page; + (*counter)++; + pages--; + } + return pages; +} + +static void +cmm_free_pages(long pages, long *counter, struct cmm_page_array **list) +{ + struct cmm_page_array *pa; + unsigned long page; + + pa = *list; + while (pages) { + if (!pa || pa->index <= 0) + break; + page = pa->pages[--pa->index]; + if (pa->index == 0) { + pa = pa->next; + free_page((unsigned long) *list); + *list = pa; + } + free_page(page); + (*counter)--; + pages--; + } +} + +static int +cmm_thread(void *dummy) +{ + int rc; + + daemonize("cmmthread"); + set_cpus_allowed(current, cpumask_of_cpu(0)); + while (1) { + rc = wait_event_interruptible(cmm_thread_wait, + (cmm_pages != cmm_pages_target || + cmm_timed_pages != cmm_timed_pages_target)); + if (rc == -ERESTARTSYS) { + /* Got kill signal. End thread. */ + clear_bit(0, &cmm_thread_active); + cmm_pages_target = cmm_pages; + cmm_timed_pages_target = cmm_timed_pages; + break; + } + if (cmm_pages_target > cmm_pages) { + if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list)) + cmm_pages_target = cmm_pages; + } else if (cmm_pages_target < cmm_pages) { + cmm_free_pages(1, &cmm_pages, &cmm_page_list); + } + if (cmm_timed_pages_target > cmm_timed_pages) { + if (cmm_alloc_pages(1, &cmm_timed_pages, + &cmm_timed_page_list)) + cmm_timed_pages_target = cmm_timed_pages; + } else if (cmm_timed_pages_target < cmm_timed_pages) { + cmm_free_pages(1, &cmm_timed_pages, + &cmm_timed_page_list); + } + if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer)) + cmm_set_timer(); + } + return 0; +} + +static void +cmm_start_thread(void) +{ + kernel_thread(cmm_thread, 0, 0); +} + +static void +cmm_kick_thread(void) +{ + if (!test_and_set_bit(0, &cmm_thread_active)) + schedule_work(&cmm_thread_starter); + wake_up(&cmm_thread_wait); +} + +static void +cmm_set_timer(void) +{ + if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) { + if (timer_pending(&cmm_timer)) + del_timer(&cmm_timer); + return; + } + if (timer_pending(&cmm_timer)) { + if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ)) + return; + } + cmm_timer.function = cmm_timer_fn; + cmm_timer.data = 0; + cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ; + add_timer(&cmm_timer); +} + +static void +cmm_timer_fn(unsigned long ignored) +{ + long pages; + + pages = cmm_timed_pages_target - cmm_timeout_pages; + if (pages < 0) + cmm_timed_pages_target = 0; + else + cmm_timed_pages_target = pages; + cmm_kick_thread(); + cmm_set_timer(); +} + +void +cmm_set_pages(long pages) +{ + cmm_pages_target = pages; + cmm_kick_thread(); +} + +long +cmm_get_pages(void) +{ + return cmm_pages; +} + +void +cmm_add_timed_pages(long pages) +{ + cmm_timed_pages_target += pages; + cmm_kick_thread(); +} + +long +cmm_get_timed_pages(void) +{ + return cmm_timed_pages; +} + +void +cmm_set_timeout(long pages, long seconds) +{ + cmm_timeout_pages = pages; + cmm_timeout_seconds = seconds; + cmm_set_timer(); +} + +static inline int +cmm_skip_blanks(char *cp, char **endp) +{ + char *str; + + for (str = cp; *str == ' ' || *str == '\t'; str++); + *endp = str; + return str != cp; +} + +#ifdef CONFIG_CMM_PROC +/* These will someday get removed. */ +#define VM_CMM_PAGES 1111 +#define VM_CMM_TIMED_PAGES 1112 +#define VM_CMM_TIMEOUT 1113 + +static struct ctl_table cmm_table[]; + +static int +cmm_pages_handler(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + char buf[16], *p; + long pages; + int len; + + if (!*lenp || (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + if (write) { + len = *lenp; + if (copy_from_user(buf, buffer, + len > sizeof(buf) ? sizeof(buf) : len)) + return -EFAULT; + buf[sizeof(buf) - 1] = '\0'; + cmm_skip_blanks(buf, &p); + pages = cmm_strtoul(p, &p); + if (ctl == &cmm_table[0]) + cmm_set_pages(pages); + else + cmm_add_timed_pages(pages); + } else { + if (ctl == &cmm_table[0]) + pages = cmm_get_pages(); + else + pages = cmm_get_timed_pages(); + len = sprintf(buf, "%ld\n", pages); + if (len > *lenp) + len = *lenp; + if (copy_to_user(buffer, buf, len)) + return -EFAULT; + } + *lenp = len; + filp->f_pos += len; + return 0; +} + +static int +cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + char buf[64], *p; + long pages, seconds; + int len; + + if (!*lenp || (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + if (write) { + len = *lenp; + if (copy_from_user(buf, buffer, + len > sizeof(buf) ? sizeof(buf) : len)) + return -EFAULT; + buf[sizeof(buf) - 1] = '\0'; + cmm_skip_blanks(buf, &p); + pages = cmm_strtoul(p, &p); + cmm_skip_blanks(p, &p); + seconds = cmm_strtoul(p, &p); + cmm_set_timeout(pages, seconds); + } else { + len = sprintf(buf, "%ld %ld\n", + cmm_timeout_pages, cmm_timeout_seconds); + if (len > *lenp) + len = *lenp; + if (copy_to_user(buffer, buf, len)) + return -EFAULT; + } + *lenp = len; + filp->f_pos += len; + return 0; +} + +static struct ctl_table cmm_table[] = { + { + .ctl_name = VM_CMM_PAGES, + .procname = "cmm_pages", + .mode = 0600, + .proc_handler = &cmm_pages_handler, + }, + { + .ctl_name = VM_CMM_TIMED_PAGES, + .procname = "cmm_timed_pages", + .mode = 0600, + .proc_handler = &cmm_pages_handler, + }, + { + .ctl_name = VM_CMM_TIMEOUT, + .procname = "cmm_timeout", + .mode = 0600, + .proc_handler = &cmm_timeout_handler, + }, + { .ctl_name = 0 } +}; + +static struct ctl_table cmm_dir_table[] = { + { + .ctl_name = CTL_VM, + .procname = "vm", + .maxlen = 0, + .mode = 0555, + .child = cmm_table, + }, + { .ctl_name = 0 } +}; +#endif + +#ifdef CONFIG_CMM_IUCV +#define SMSG_PREFIX "CMM" +static void +cmm_smsg_target(char *msg) +{ + long pages, seconds; + + if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg)) + return; + if (strncmp(msg, "SHRINK", 6) == 0) { + if (!cmm_skip_blanks(msg + 6, &msg)) + return; + pages = cmm_strtoul(msg, &msg); + cmm_skip_blanks(msg, &msg); + if (*msg == '\0') + cmm_set_pages(pages); + } else if (strncmp(msg, "RELEASE", 7) == 0) { + if (!cmm_skip_blanks(msg + 7, &msg)) + return; + pages = cmm_strtoul(msg, &msg); + cmm_skip_blanks(msg, &msg); + if (*msg == '\0') + cmm_add_timed_pages(pages); + } else if (strncmp(msg, "REUSE", 5) == 0) { + if (!cmm_skip_blanks(msg + 5, &msg)) + return; + pages = cmm_strtoul(msg, &msg); + if (!cmm_skip_blanks(msg, &msg)) + return; + seconds = cmm_strtoul(msg, &msg); + cmm_skip_blanks(msg, &msg); + if (*msg == '\0') + cmm_set_timeout(pages, seconds); + } +} +#endif + +struct ctl_table_header *cmm_sysctl_header; + +static int +cmm_init (void) +{ +#ifdef CONFIG_CMM_PROC + cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1); +#endif +#ifdef CONFIG_CMM_IUCV + smsg_register_callback(SMSG_PREFIX, cmm_smsg_target); +#endif + INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, 0); + init_waitqueue_head(&cmm_thread_wait); + init_timer(&cmm_timer); + return 0; +} + +static void +cmm_exit(void) +{ + cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list); + cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list); +#ifdef CONFIG_CMM_PROC + unregister_sysctl_table(cmm_sysctl_header); +#endif +#ifdef CONFIG_CMM_IUCV + smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target); +#endif +} + +module_init(cmm_init); +module_exit(cmm_exit); + +EXPORT_SYMBOL(cmm_set_pages); +EXPORT_SYMBOL(cmm_get_pages); +EXPORT_SYMBOL(cmm_add_timed_pages); +EXPORT_SYMBOL(cmm_get_timed_pages); +EXPORT_SYMBOL(cmm_set_timeout); + +MODULE_LICENSE("GPL"); diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c new file mode 100644 index 000000000000..ea3db122dda6 --- /dev/null +++ b/arch/s390/mm/extmem.c @@ -0,0 +1,503 @@ +/* + * File...........: arch/s390/mm/dcss.c + * Author(s)......: Steven Shultz <shultzss@us.ibm.com> + * Carsten Otte <cotte@de.ibm.com> + * Bugreports.to..: <Linux390@de.ibm.com> + * thanks to Rob M van der Heij + * - he wrote the diag64 function + * (C) IBM Corporation 2002 + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/bootmem.h> +#include <asm/page.h> +#include <asm/ebcdic.h> +#include <asm/errno.h> +#include <asm/extmem.h> +#include <asm/cpcmd.h> +#include <linux/ctype.h> + +#define DCSS_DEBUG /* Debug messages on/off */ + +#define DCSS_NAME "extmem" +#ifdef DCSS_DEBUG +#define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSS_NAME " debug:" x) +#else +#define PRINT_DEBUG(x...) do {} while (0) +#endif +#define PRINT_INFO(x...) printk(KERN_INFO DCSS_NAME " info:" x) +#define PRINT_WARN(x...) printk(KERN_WARNING DCSS_NAME " warning:" x) +#define PRINT_ERR(x...) printk(KERN_ERR DCSS_NAME " error:" x) + + +#define DCSS_LOADSHR 0x00 +#define DCSS_LOADNSR 0x04 +#define DCSS_PURGESEG 0x08 +#define DCSS_FINDSEG 0x0c +#define DCSS_LOADNOLY 0x10 +#define DCSS_SEGEXT 0x18 +#define DCSS_QACTV 0x0c + +struct dcss_segment { + struct list_head list; + char dcss_name[8]; + unsigned long start_addr; + unsigned long end; + atomic_t ref_count; + int dcss_attr; + int shared_attr; +}; + +static spinlock_t dcss_lock = SPIN_LOCK_UNLOCKED; +static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); +extern struct {unsigned long addr, size, type;} memory_chunk[16]; + +/* + * Create the 8 bytes, ebcdic VM segment name from + * an ascii name. + */ +static void inline dcss_mkname(char *name, char *dcss_name) +{ + int i; + + for (i = 0; i <= 8; i++) { + if (name[i] == '\0') + break; + dcss_name[i] = toupper(name[i]); + }; + for (; i <= 8; i++) + dcss_name[i] = ' '; + ASCEBC(dcss_name, 8); +} + +/* + * Perform a function on a dcss segment. + */ +static inline int +dcss_diag (__u8 func, void *parameter, + unsigned long *ret1, unsigned long *ret2) +{ + unsigned long rx, ry; + int rc; + + rx = (unsigned long) parameter; + ry = (unsigned long) func; + __asm__ __volatile__( +#ifdef CONFIG_ARCH_S390X + " sam31\n" // switch to 31 bit + " diag %0,%1,0x64\n" + " sam64\n" // switch back to 64 bit +#else + " diag %0,%1,0x64\n" +#endif + " ipm %2\n" + " srl %2,28\n" + : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" ); + *ret1 = rx; + *ret2 = ry; + return rc; +} + + +/* use to issue "extended" dcss query */ +static inline int +dcss_diag_query(char *name, int *rwattr, int *shattr, unsigned long *segstart, unsigned long *segend) +{ + int i,j,rc; + unsigned long rx, ry; + + typedef struct segentry { + char thisseg[8]; + } segentry; + + struct qout64 { + int segstart; + int segend; + int segcnt; + int segrcnt; + segentry segout[6]; + }; + + struct qin64 { + char qopcode; + char rsrv1[3]; + char qrcode; + char rsrv2[3]; + char qname[8]; + unsigned int qoutptr; + short int qoutlen; + }; + + + struct qin64 *qinarea; + struct qout64 *qoutarea; + + qinarea = (struct qin64*) get_zeroed_page (GFP_DMA); + if (!qinarea) { + rc =-ENOMEM; + goto out; + } + qoutarea = (struct qout64*) get_zeroed_page (GFP_DMA); + if (!qoutarea) { + rc = -ENOMEM; + free_page ((unsigned long) qinarea); + goto out; + } + memset (qinarea,0,PAGE_SIZE); + memset (qoutarea,0,PAGE_SIZE); + + qinarea->qopcode = DCSS_QACTV; /* do a query for active + segments */ + qinarea->qoutptr = (unsigned long) qoutarea; + qinarea->qoutlen = sizeof(struct qout64); + + /* Move segment name into double word aligned + field and pad with blanks to 8 long. + */ + + for (i = j = 0 ; i < 8; i++) { + qinarea->qname[i] = (name[j] == '\0') ? ' ' : name[j++]; + } + + /* name already in EBCDIC */ + /* ASCEBC ((void *)&qinarea.qname, 8); */ + + /* set the assembler variables */ + rx = (unsigned long) qinarea; + ry = DCSS_SEGEXT; /* this is extended function */ + + /* issue diagnose x'64' */ + __asm__ __volatile__( +#ifdef CONFIG_ARCH_S390X + " sam31\n" // switch to 31 bit + " diag %0,%1,0x64\n" + " sam64\n" // switch back to 64 bit +#else + " diag %0,%1,0x64\n" +#endif + " ipm %2\n" + " srl %2,28\n" + : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" ); + + /* parse the query output area */ + *segstart=qoutarea->segstart; + *segend=qoutarea->segend; + + if (rc > 1) + { + *rwattr = 2; + *shattr = 2; + rc = 0; + goto free; + } + + if (qoutarea->segcnt > 6) + { + *rwattr = 3; + *shattr = 3; + rc = 0; + goto free; + } + + *rwattr = 1; + *shattr = 1; + + for (i=0; i < qoutarea->segrcnt; i++) { + if (qoutarea->segout[i].thisseg[3] == 2 || + qoutarea->segout[i].thisseg[3] == 3 || + qoutarea->segout[i].thisseg[3] == 6 ) + *rwattr = 0; + if (qoutarea->segout[i].thisseg[3] == 1 || + qoutarea->segout[i].thisseg[3] == 3 || + qoutarea->segout[i].thisseg[3] == 5 ) + *shattr = 0; + } /* end of for statement */ + rc = 0; + free: + free_page ((unsigned long) qoutarea); + free_page ((unsigned long) qinarea); + out: + return rc; +} + +/* + * Load a DCSS segment via the diag 0x64. + */ +int segment_load(char *name, int segtype, unsigned long *addr, + unsigned long *end) +{ + char dcss_name[8]; + struct list_head *l; + struct dcss_segment *seg, *tmp; + unsigned long dummy; + unsigned long segstart, segend; + int rc = 0,i; + int rwattr, shattr; + + if (!MACHINE_IS_VM) + return -ENOSYS; + dcss_mkname(name, dcss_name); + /* search for the dcss in list of currently loaded segments */ + spin_lock(&dcss_lock); + seg = NULL; + list_for_each(l, &dcss_list) { + tmp = list_entry(l, struct dcss_segment, list); + if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { + seg = tmp; + break; + } + } + + if (seg == NULL) { + /* find out the attributes of this + shared segment */ + dcss_diag_query(dcss_name, &rwattr, &shattr, &segstart, &segend); + /* does segment collide with main memory? */ + for (i=0; i<16; i++) { + if (memory_chunk[i].type != 0) + continue; + if (memory_chunk[i].addr > segend) + continue; + if (memory_chunk[i].addr + memory_chunk[i].size <= segstart) + continue; + spin_unlock(&dcss_lock); + return -ENOENT; + } + /* or does it collide with other (loaded) segments? */ + list_for_each(l, &dcss_list) { + tmp = list_entry(l, struct dcss_segment, list); + if ((segstart <= tmp->end && segstart >= tmp->start_addr) || + (segend <= tmp->end && segend >= tmp->start_addr) || + (segstart <= tmp->start_addr && segend >= tmp->end)) { + PRINT_ERR("Segment Overlap!\n"); + spin_unlock(&dcss_lock); + return -ENOENT; + } + } + + /* do case statement on segtype */ + /* if asking for shared ro, + shared rw works */ + /* if asking for exclusive ro, + exclusive rw works */ + + switch(segtype) { + case SEGMENT_SHARED_RO: + if (shattr > 1 || rwattr > 1) { + spin_unlock(&dcss_lock); + return -ENOENT; + } else { + if (shattr == 0 && rwattr == 0) + rc = SEGMENT_EXCLUSIVE_RO; + if (shattr == 0 && rwattr == 1) + rc = SEGMENT_EXCLUSIVE_RW; + if (shattr == 1 && rwattr == 0) + rc = SEGMENT_SHARED_RO; + if (shattr == 1 && rwattr == 1) + rc = SEGMENT_SHARED_RW; + } + break; + case SEGMENT_SHARED_RW: + if (shattr > 1 || rwattr != 1) { + spin_unlock(&dcss_lock); + return -ENOENT; + } else { + if (shattr == 0) + rc = SEGMENT_EXCLUSIVE_RW; + if (shattr == 1) + rc = SEGMENT_SHARED_RW; + } + break; + + case SEGMENT_EXCLUSIVE_RO: + if (shattr > 0 || rwattr > 1) { + spin_unlock(&dcss_lock); + return -ENOENT; + } else { + if (rwattr == 0) + rc = SEGMENT_EXCLUSIVE_RO; + if (rwattr == 1) + rc = SEGMENT_EXCLUSIVE_RW; + } + break; + + case SEGMENT_EXCLUSIVE_RW: +/* if (shattr != 0 || rwattr != 1) { + spin_unlock(&dcss_lock); + return -ENOENT; + } else { +*/ + rc = SEGMENT_EXCLUSIVE_RW; +// } + break; + + default: + spin_unlock(&dcss_lock); + return -ENOENT; + } /* end switch */ + + seg = kmalloc(sizeof(struct dcss_segment), GFP_DMA); + if (seg != NULL) { + memcpy(seg->dcss_name, dcss_name, 8); + if (rc == SEGMENT_EXCLUSIVE_RW) { + if (dcss_diag(DCSS_LOADNSR, seg->dcss_name, + &seg->start_addr, &seg->end) == 0) { + if (seg->end < max_low_pfn*PAGE_SIZE ) { + atomic_set(&seg->ref_count, 1); + list_add(&seg->list, &dcss_list); + *addr = seg->start_addr; + *end = seg->end; + seg->dcss_attr = rc; + if (shattr == 1 && rwattr == 1) + seg->shared_attr = SEGMENT_SHARED_RW; + else if (shattr == 1 && rwattr == 0) + seg->shared_attr = SEGMENT_SHARED_RO; + else + seg->shared_attr = SEGMENT_EXCLUSIVE_RW; + } else { + dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); + kfree (seg); + rc = -ENOENT; + } + } else { + kfree(seg); + rc = -ENOENT; + } + goto out; + } + if (dcss_diag(DCSS_LOADNOLY, seg->dcss_name, + &seg->start_addr, &seg->end) == 0) { + if (seg->end < max_low_pfn*PAGE_SIZE ) { + atomic_set(&seg->ref_count, 1); + list_add(&seg->list, &dcss_list); + *addr = seg->start_addr; + *end = seg->end; + seg->dcss_attr = rc; + seg->shared_attr = rc; + } else { + dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); + kfree (seg); + rc = -ENOENT; + } + } else { + kfree(seg); + rc = -ENOENT; + } + } else rc = -ENOMEM; + } else { + /* found */ + if ((segtype == SEGMENT_EXCLUSIVE_RW) && (seg->dcss_attr != SEGMENT_EXCLUSIVE_RW)) { + PRINT_ERR("Segment already loaded in other mode than EXCLUSIVE_RW!\n"); + rc = -EPERM; + goto out; + /* reload segment in exclusive mode */ +/* dcss_diag(DCSS_LOADNSR, seg->dcss_name, + &seg->start_addr, &seg->end); + seg->dcss_attr = SEGMENT_EXCLUSIVE_RW;*/ + } + if ((segtype != SEGMENT_EXCLUSIVE_RW) && (seg->dcss_attr == SEGMENT_EXCLUSIVE_RW)) { + PRINT_ERR("Segment already loaded in EXCLUSIVE_RW mode!\n"); + rc = -EPERM; + goto out; + } + atomic_inc(&seg->ref_count); + *addr = seg->start_addr; + *end = seg->end; + rc = seg->dcss_attr; + } +out: + spin_unlock(&dcss_lock); + return rc; +} + +/* + * Decrease the use count of a DCSS segment and remove + * it from the address space if nobody is using it + * any longer. + */ +void segment_unload(char *name) +{ + char dcss_name[8]; + unsigned long dummy; + struct list_head *l,*l_tmp; + struct dcss_segment *seg; + + if (!MACHINE_IS_VM) + return; + dcss_mkname(name, dcss_name); + spin_lock(&dcss_lock); + list_for_each_safe(l, l_tmp, &dcss_list) { + seg = list_entry(l, struct dcss_segment, list); + if (memcmp(seg->dcss_name, dcss_name, 8) == 0) { + if (atomic_dec_return(&seg->ref_count) == 0) { + /* Last user of the segment is + gone. */ + list_del(&seg->list); + dcss_diag(DCSS_PURGESEG, seg->dcss_name, + &dummy, &dummy); + kfree(seg); + } + break; + } + } + spin_unlock(&dcss_lock); +} + +/* + * Replace an existing DCSS segment, so that machines + * that load it anew will see the new version. + */ +void segment_replace(char *name) +{ + char dcss_name[8]; + struct list_head *l; + struct dcss_segment *seg; + int mybeg = 0; + int myend = 0; + char mybuff1[80]; + char mybuff2[80]; + + if (!MACHINE_IS_VM) + return; + dcss_mkname(name, dcss_name); + + memset (mybuff1, 0, sizeof(mybuff1)); + memset (mybuff2, 0, sizeof(mybuff2)); + + spin_lock(&dcss_lock); + list_for_each(l, &dcss_list) { + seg = list_entry(l, struct dcss_segment, list); + if (memcmp(seg->dcss_name, dcss_name, 8) == 0) { + mybeg = seg->start_addr >> 12; + myend = (seg->end) >> 12; + if (seg->shared_attr == SEGMENT_EXCLUSIVE_RW) + sprintf(mybuff1, "DEFSEG %s %X-%X EW", + name, mybeg, myend); + if (seg->shared_attr == SEGMENT_EXCLUSIVE_RO) + sprintf(mybuff1, "DEFSEG %s %X-%X RO", + name, mybeg, myend); + if (seg->shared_attr == SEGMENT_SHARED_RW) + sprintf(mybuff1, "DEFSEG %s %X-%X SW", + name, mybeg, myend); + if (seg->shared_attr == SEGMENT_SHARED_RO) + sprintf(mybuff1, "DEFSEG %s %X-%X SR", + name, mybeg, myend); + spin_unlock(&dcss_lock); + sprintf(mybuff2, "SAVESEG %s", name); + cpcmd(mybuff1, NULL, 80); + cpcmd(mybuff2, NULL, 80); + break; + } + + } + if (myend == 0) spin_unlock(&dcss_lock); +} + +EXPORT_SYMBOL(segment_load); +EXPORT_SYMBOL(segment_unload); +EXPORT_SYMBOL(segment_replace); diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index b894f76f377d..7248a1657fe5 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -40,6 +40,19 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE))); char empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +void diag10(unsigned long addr) +{ +#ifdef __s390x__ + if (addr >= 0x80000000) + return; + asm volatile ("sam31\n\t" + "diag %0,%0,0x10\n\t" + "sam64" : : "a" (addr) ); +#else + asm volatile ("diag %0,%0,0x10" : : "a" (addr) ); +#endif +} + void show_mem(void) { int i, total = 0, reserved = 0; diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 7b3d86b00101..2d5106325123 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -13,6 +13,8 @@ #include <linux/timer.h> #include <linux/vmalloc.h> #include <asm/hardirq.h> +#include <linux/bitops.h> +#include <asm/semaphore.h> #include <linux/firmware.h> #include "base.h" @@ -21,19 +23,36 @@ MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); MODULE_LICENSE("GPL"); +enum { + FW_STATUS_LOADING, + FW_STATUS_DONE, + FW_STATUS_ABORT, +}; + static int loading_timeout = 10; /* In seconds */ +/* fw_lock could be moved to 'struct firmware_priv' but since it is just + * guarding for corner cases a global lock should be OK */ +static DECLARE_MUTEX(fw_lock); + struct firmware_priv { char fw_id[FIRMWARE_NAME_MAX]; struct completion completion; struct bin_attribute attr_data; struct firmware *fw; - int loading; - int abort; + unsigned long status; int alloc_size; struct timer_list timeout; }; +static inline void +fw_load_abort(struct firmware_priv *fw_priv) +{ + set_bit(FW_STATUS_ABORT, &fw_priv->status); + wmb(); + complete(&fw_priv->completion); +} + static ssize_t firmware_timeout_show(struct class *class, char *buf) { @@ -91,7 +110,8 @@ static ssize_t firmware_loading_show(struct class_device *class_dev, char *buf) { struct firmware_priv *fw_priv = class_get_devdata(class_dev); - return sprintf(buf, "%d\n", fw_priv->loading); + int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status); + return sprintf(buf, "%d\n", loading); } /** @@ -108,25 +128,31 @@ firmware_loading_store(struct class_device *class_dev, const char *buf, size_t count) { struct firmware_priv *fw_priv = class_get_devdata(class_dev); - int prev_loading = fw_priv->loading; + int loading = simple_strtol(buf, NULL, 10); - fw_priv->loading = simple_strtol(buf, NULL, 10); - - switch (fw_priv->loading) { - case -1: - fw_priv->abort = 1; - wmb(); - complete(&fw_priv->completion); - break; + switch (loading) { case 1: - kfree(fw_priv->fw->data); + down(&fw_lock); + vfree(fw_priv->fw->data); fw_priv->fw->data = NULL; fw_priv->fw->size = 0; fw_priv->alloc_size = 0; + set_bit(FW_STATUS_LOADING, &fw_priv->status); + up(&fw_lock); break; case 0: - if (prev_loading == 1) + if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { complete(&fw_priv->completion); + clear_bit(FW_STATUS_LOADING, &fw_priv->status); + break; + } + /* fallthrough */ + default: + printk(KERN_ERR "%s: unexpected value (%d)\n", __FUNCTION__, + loading); + /* fallthrough */ + case -1: + fw_load_abort(fw_priv); break; } @@ -142,15 +168,26 @@ firmware_data_read(struct kobject *kobj, { struct class_device *class_dev = to_class_dev(kobj); struct firmware_priv *fw_priv = class_get_devdata(class_dev); - struct firmware *fw = fw_priv->fw; + struct firmware *fw; + ssize_t ret_count = count; - if (offset > fw->size) - return 0; - if (offset + count > fw->size) - count = fw->size - offset; + down(&fw_lock); + fw = fw_priv->fw; + if (test_bit(FW_STATUS_DONE, &fw_priv->status)) { + ret_count = -ENODEV; + goto out; + } + if (offset > fw->size) { + ret_count = 0; + goto out; + } + if (offset + ret_count > fw->size) + ret_count = fw->size - offset; - memcpy(buffer, fw->data + offset, count); - return count; + memcpy(buffer, fw->data + offset, ret_count); +out: + up(&fw_lock); + return ret_count; } static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) @@ -164,7 +201,7 @@ fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) if (!new_data) { printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__); /* Make sure that we don't keep incomplete data */ - fw_priv->abort = 1; + fw_load_abort(fw_priv); return -ENOMEM; } fw_priv->alloc_size += PAGE_SIZE; @@ -191,18 +228,26 @@ firmware_data_write(struct kobject *kobj, { struct class_device *class_dev = to_class_dev(kobj); struct firmware_priv *fw_priv = class_get_devdata(class_dev); - struct firmware *fw = fw_priv->fw; - int retval; + struct firmware *fw; + ssize_t retval; + down(&fw_lock); + fw = fw_priv->fw; + if (test_bit(FW_STATUS_DONE, &fw_priv->status)) { + retval = -ENODEV; + goto out; + } retval = fw_realloc_buffer(fw_priv, offset + count); if (retval) - return retval; + goto out; memcpy(fw->data + offset, buffer, count); fw->size = max_t(size_t, offset + count, fw->size); - - return count; + retval = count; +out: + up(&fw_lock); + return retval; } static struct bin_attribute firmware_attr_data_tmpl = { .attr = {.name = "data", .mode = 0644}, @@ -214,6 +259,9 @@ static struct bin_attribute firmware_attr_data_tmpl = { static void fw_class_dev_release(struct class_device *class_dev) { + struct firmware_priv *fw_priv = class_get_devdata(class_dev); + + kfree(fw_priv); kfree(class_dev); } @@ -221,29 +269,30 @@ static void firmware_class_timeout(u_long data) { struct firmware_priv *fw_priv = (struct firmware_priv *) data; - fw_priv->abort = 1; - wmb(); - complete(&fw_priv->completion); + fw_load_abort(fw_priv); } static inline void fw_setup_class_device_id(struct class_device *class_dev, struct device *dev) { /* XXX warning we should watch out for name collisions */ - strncpy(class_dev->class_id, dev->bus_id, BUS_ID_SIZE); - class_dev->class_id[BUS_ID_SIZE - 1] = '\0'; + strlcpy(class_dev->class_id, dev->bus_id, BUS_ID_SIZE); } + static int -fw_setup_class_device(struct class_device **class_dev_p, - const char *fw_name, struct device *device) +fw_register_class_device(struct class_device **class_dev_p, + const char *fw_name, struct device *device) { - int retval = 0; + int retval; struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv), GFP_KERNEL); struct class_device *class_dev = kmalloc(sizeof (struct class_device), GFP_KERNEL); + *class_dev_p = NULL; + if (!fw_priv || !class_dev) { + printk(KERN_ERR "%s: kmalloc failed\n", __FUNCTION__); retval = -ENOMEM; goto error_kfree; } @@ -251,19 +300,15 @@ fw_setup_class_device(struct class_device **class_dev_p, memset(class_dev, 0, sizeof (*class_dev)); init_completion(&fw_priv->completion); - memcpy(&fw_priv->attr_data, &firmware_attr_data_tmpl, - sizeof (firmware_attr_data_tmpl)); - - strncpy(&fw_priv->fw_id[0], fw_name, FIRMWARE_NAME_MAX); - fw_priv->fw_id[FIRMWARE_NAME_MAX - 1] = '\0'; - - fw_setup_class_device_id(class_dev, device); - class_dev->dev = device; + fw_priv->attr_data = firmware_attr_data_tmpl; + strlcpy(fw_priv->fw_id, fw_name, FIRMWARE_NAME_MAX); fw_priv->timeout.function = firmware_class_timeout; fw_priv->timeout.data = (u_long) fw_priv; init_timer(&fw_priv->timeout); + fw_setup_class_device_id(class_dev, device); + class_dev->dev = device; class_dev->class = &firmware_class; class_set_devdata(class_dev, fw_priv); retval = class_device_register(class_dev); @@ -272,12 +317,35 @@ fw_setup_class_device(struct class_device **class_dev_p, __FUNCTION__); goto error_kfree; } + *class_dev_p = class_dev; + return 0; + +error_kfree: + kfree(fw_priv); + kfree(class_dev); + return retval; +} +static int +fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p, + const char *fw_name, struct device *device) +{ + struct class_device *class_dev; + struct firmware_priv *fw_priv; + int retval; + + *class_dev_p = NULL; + retval = fw_register_class_device(&class_dev, fw_name, device); + if (retval) + goto out; + + fw_priv = class_get_devdata(class_dev); + fw_priv->fw = fw; retval = sysfs_create_bin_file(&class_dev->kobj, &fw_priv->attr_data); if (retval) { printk(KERN_ERR "%s: sysfs_create_bin_file failed\n", __FUNCTION__); - goto error_unreg_class_dev; + goto error_unreg; } retval = class_device_create_file(class_dev, @@ -285,43 +353,17 @@ fw_setup_class_device(struct class_device **class_dev_p, if (retval) { printk(KERN_ERR "%s: class_device_create_file failed\n", __FUNCTION__); - goto error_remove_data; - } - - fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL); - if (!fw_priv->fw) { - printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n", - __FUNCTION__); - retval = -ENOMEM; - goto error_remove_loading; + goto error_unreg; } - memset(fw_priv->fw, 0, sizeof (*fw_priv->fw)); + *class_dev_p = class_dev; goto out; -error_remove_loading: - class_device_remove_file(class_dev, &class_device_attr_loading); -error_remove_data: - sysfs_remove_bin_file(&class_dev->kobj, &fw_priv->attr_data); -error_unreg_class_dev: +error_unreg: class_device_unregister(class_dev); -error_kfree: - kfree(fw_priv); - kfree(class_dev); - *class_dev_p = NULL; out: - *class_dev_p = class_dev; return retval; } -static void -fw_remove_class_device(struct class_device *class_dev) -{ - struct firmware_priv *fw_priv = class_get_devdata(class_dev); - - class_device_remove_file(class_dev, &class_device_attr_loading); - sysfs_remove_bin_file(&class_dev->kobj, &fw_priv->attr_data); - class_device_unregister(class_dev); -} /** * request_firmware: - request firmware to hotplug and wait for it @@ -336,21 +378,29 @@ fw_remove_class_device(struct class_device *class_dev) * firmware image for this or any other device. **/ int -request_firmware(const struct firmware **firmware, const char *name, +request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { struct class_device *class_dev; struct firmware_priv *fw_priv; + struct firmware *firmware; int retval; - if (!firmware) + if (!firmware_p) return -EINVAL; - *firmware = NULL; + *firmware_p = firmware = kmalloc(sizeof (struct firmware), GFP_KERNEL); + if (!firmware) { + printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n", + __FUNCTION__); + retval = -ENOMEM; + goto out; + } + memset(firmware, 0, sizeof (*firmware)); - retval = fw_setup_class_device(&class_dev, name, device); + retval = fw_setup_class_device(firmware, &class_dev, name, device); if (retval) - goto out; + goto error_kfree_fw; fw_priv = class_get_devdata(class_dev); @@ -360,18 +410,23 @@ request_firmware(const struct firmware **firmware, const char *name, } wait_for_completion(&fw_priv->completion); + set_bit(FW_STATUS_DONE, &fw_priv->status); del_timer_sync(&fw_priv->timeout); - fw_remove_class_device(class_dev); - if (fw_priv->fw->size && !fw_priv->abort) { - *firmware = fw_priv->fw; - } else { + down(&fw_lock); + if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) { retval = -ENOENT; - vfree(fw_priv->fw->data); - kfree(fw_priv->fw); + release_firmware(fw_priv->fw); + *firmware_p = NULL; } - kfree(fw_priv); + fw_priv->fw = NULL; + up(&fw_lock); + class_device_unregister(class_dev); + goto out; + +error_kfree_fw: + kfree(firmware); out: return retval; } @@ -489,6 +544,7 @@ firmware_class_init(void) error = class_register(&firmware_class); if (error) { printk(KERN_ERR "%s: class_register failed\n", __FUNCTION__); + return error; } error = class_create_file(&firmware_class, &class_attr_timeout); if (error) { @@ -502,7 +558,6 @@ firmware_class_init(void) static void __exit firmware_class_exit(void) { - class_remove_file(&firmware_class, &class_attr_timeout); class_unregister(&firmware_class); } diff --git a/drivers/char/h8.c b/drivers/char/h8.c index ba69f09fdb29..19843a0d1538 100644 --- a/drivers/char/h8.c +++ b/drivers/char/h8.c @@ -30,9 +30,6 @@ #include <linux/init.h> #include <linux/slab.h> -#define __KERNEL_SYSCALLS__ -#include <asm/unistd.h> - #include "h8.h" #define DEBUG_H8 diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 96fc233f8e97..0c02e2debbb1 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -40,7 +40,6 @@ #include <linux/tty.h> #include <linux/timer.h> #include <linux/ctype.h> -#include <linux/kd.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/slab.h> @@ -1091,7 +1090,7 @@ do_it_again: set_bit(TTY_DONT_FLIP, &tty->flags); continue; } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); /* Deal with packet mode. */ if (tty->packet && b == buf) { @@ -1170,7 +1169,7 @@ do_it_again: if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = minimum; - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); size = b - buf; if (size) { retval = size; @@ -1246,7 +1245,7 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file, schedule(); } break_out: - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&tty->write_wait, &wait); return (b - buf) ? b - buf : retval; } diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h index 7297281f358d..9380ff53f8a3 100644 --- a/drivers/char/rocket_int.h +++ b/drivers/char/rocket_int.h @@ -1231,11 +1231,9 @@ struct r_port { #define ROCKET_INITIALIZED 0x80000000 /* Port is active */ #define ROCKET_CLOSING 0x40000000 /* Serial port is closing */ #define ROCKET_NORMAL_ACTIVE 0x20000000 /* Normal port is active */ -#define ROCKET_CALLOUT_ACTIVE 0x10000000 /* Callout port is active */ /* tty subtypes */ #define SERIAL_TYPE_NORMAL 1 -#define SERIAL_TYPE_CALLOUT 2 /* * Assigned major numbers for the Comtrol Rocketport diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 8fde104e4f08..7158fb9a34c6 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -13,7 +13,8 @@ config CPU_FREQ_PROC_INTF choice prompt "Default CPUFreq governor" depends on CPU_FREQ - default CPU_FREQ_DEFAULT_GOV_PERFORMANCE + default CPU_FREQ_DEFAULT_GOV_PERFORMANCE if !CPU_FREQ_SA1100 && !CPU_FREQ_SA1110 + default CPU_FREQ_DEFAULT_GOV_USERSPACE if CPU_FREQ_SA1100 || CPU_FREQ_SA1110 help This option sets which CPUFreq governor shall be loaded at startup. If in doubt, select 'performance'. diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index d425c08e1734..daec60351d42 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -1707,14 +1707,14 @@ isdn_close(struct inode *ino, struct file *filep) static struct file_operations isdn_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: isdn_read, - write: isdn_write, - poll: isdn_poll, - ioctl: isdn_ioctl, - open: isdn_open, - release: isdn_close, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = isdn_read, + .write = isdn_write, + .poll = isdn_poll, + .ioctl = isdn_ioctl, + .open = isdn_open, + .release = isdn_close, }; char * diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index 9e09e7af7ae1..f03fff62b59e 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -1,16 +1,66 @@ menu "Macintosh device drivers" +config ADB + bool "Apple Desktop Bus (ADB) support" + depends on MAC || PPC_PMAC + help + Apple Desktop Bus (ADB) support is for support of devices which + are connected to an ADB port. ADB devices tend to have 4 pins. + If you have an Apple Macintosh prior to the iMac, an iBook or + PowerBook, or a "Blue and White G3", you probably want to say Y + here. Otherwise say N. + +config ADB_MACII + bool "Include Mac II ADB driver" + depends on ADB && MAC + help + Say Y here if want your kernel to support Macintosh systems that use + the Mac II style ADB. This includes the II, IIx, IIcx, SE/30, IIci, + Quadra 610, Quadra 650, Quadra 700, Quadra 800, Centris 610 and + Centris 650. + +config ADB_MACIISI + bool "Include Mac IIsi ADB driver" + depends on ADB && MAC + help + Say Y here if want your kernel to support Macintosh systems that use + the Mac IIsi style ADB. This includes the IIsi, IIvi, IIvx, Classic + II, LC, LC II, LC III, Performa 460, and the Performa 600. + +config ADB_IOP + bool "Include IOP (IIfx/Quadra 9x0) ADB driver" + depends on ADB && MAC + help + The I/O Processor (IOP) is an Apple custom IC designed to provide + intelligent support for I/O controllers. It is described at + <http://www.angelfire.com/ca2/dev68k/iopdesc.html> to enable direct + support for it, say 'Y' here. + +config ADB_PMU68K + bool "Include PMU (Powerbook) ADB driver" + depends on ADB && MAC + help + Say Y here if want your kernel to support the m68k based Powerbooks. + This includes the PowerBook 140, PowerBook 145, PowerBook 150, + PowerBook 160, PowerBook 165, PowerBook 165c, PowerBook 170, + PowerBook 180, PowerBook, 180c, PowerBook 190cs, PowerBook 520, + PowerBook Duo 210, PowerBook Duo 230, PowerBook Duo 250, + PowerBook Duo 270c, PowerBook Duo 280 and PowerBook Duo 280c. + # we want to change this to something like CONFIG_SYSCTRL_CUDA/PMU config ADB_CUDA - bool "Support for CUDA based PowerMacs" - depends on PPC_PMAC && !PPC_PMAC64 + bool "Support for CUDA based Macs and PowerMacs" + depends on (ADB || PPC_PMAC) && !PPC_PMAC64 help - This provides support for CUDA based Power Macintosh systems. This - includes most OldWorld PowerMacs, the first generation iMacs, the - Blue&White G3 and the "Yikes" G4 (PCI Graphics). All later models - should use CONFIG_ADB_PMU instead. It is safe to say Y here even if - your machine doesn't have a CUDA. + This provides support for CUDA based Macintosh and Power Macintosh + systems. This includes many m68k based Macs (Color Classic, Mac TV, + Performa 475, Performa 520, Performa 550, Performa 575, + Performa 588, Quadra 605, Quadra 630, Quadra/Centris 660AV, and + Quadra 840AV), most OldWorld PowerMacs, the first generation iMacs, + the Blue&White G3 and the "Yikes" G4 (PCI Graphics). All later + models should use CONFIG_ADB_PMU instead. It is safe to say Y here + even if your machine doesn't have a CUDA. If unsure say Y. @@ -81,19 +131,9 @@ config MAC_SERIAL This driver is obsolete. Use CONFIG_SERIAL_PMACZILOG in "Character devices --> Serial drivers --> PowerMac z85c30" option. -config ADB - bool "Apple Desktop Bus (ADB) support" - depends on PPC_PMAC - help - Apple Desktop Bus (ADB) support is for support of devices which - are connected to an ADB port. ADB devices tend to have 4 pins. - If you have an Apple Macintosh prior to the iMac, an iBook or - PowerBook, or a "Blue and White G3", you probably want to say Y - here. Otherwise say N. - config ADB_MACIO bool "Include MacIO (CHRP) ADB driver" - depends on ADB && PPC_PMAC && !PPC_PMAC64 + depends on ADB && PPC_CHRP && !PPC_PMAC64 help Say Y here to include direct support for the ADB controller in the Hydra chip used on PowerPC Macintoshes of the CHRP type. (The Hydra @@ -151,6 +191,6 @@ config THERM_PM72 config ANSLCD bool "Support for ANS LCD display" - depends on ADB_CUDA + depends on ADB_CUDA && PPC_PMAC endmenu diff --git a/drivers/md/md.c b/drivers/md/md.c index 4c70edd61008..874b29ccd650 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -44,9 +44,6 @@ #include <linux/kmod.h> #endif -#define __KERNEL_SYSCALLS__ -#include <linux/unistd.h> - #include <asm/unaligned.h> #define MAJOR_NR MD_MAJOR diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c index 706783c1e562..369d949fded4 100644 --- a/drivers/media/common/saa7146_core.c +++ b/drivers/media/common/saa7146_core.c @@ -69,14 +69,14 @@ void saa7146_set_gpio(struct saa7146_dev *dev, u8 pin, u8 data) /* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ int saa7146_wait_for_debi_done(struct saa7146_dev *dev) { - int start; + unsigned long start; /* wait for registers to be programmed */ start = jiffies; while (1) { if (saa7146_read(dev, MC2) & 2) break; - if (jiffies-start > HZ/20) { + if (time_after(jiffies, start + HZ/20)) { DEB_S(("timed out while waiting for registers getting programmed\n")); return -ETIMEDOUT; } @@ -88,7 +88,7 @@ int saa7146_wait_for_debi_done(struct saa7146_dev *dev) if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) break; saa7146_read(dev, MC2); - if (jiffies-start > HZ/4) { + if (time_after(jiffies, start + HZ/4)) { DEB_S(("timed out while waiting for transfer completion\n")); return -ETIMEDOUT; } diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index c3e935d288d6..1dec1678e752 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -381,41 +381,41 @@ static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f) static struct v4l2_queryctrl controls[] = { { - id: V4L2_CID_BRIGHTNESS, - name: "Brightness", - minimum: 0, - maximum: 255, - step: 1, - default_value: 128, - type: V4L2_CTRL_TYPE_INTEGER, + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128, + .type = V4L2_CTRL_TYPE_INTEGER, },{ - id: V4L2_CID_CONTRAST, - name: "Contrast", - minimum: 0, - maximum: 127, - step: 1, - default_value: 64, - type: V4L2_CTRL_TYPE_INTEGER, + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .type = V4L2_CTRL_TYPE_INTEGER, },{ - id: V4L2_CID_SATURATION, - name: "Saturation", - minimum: 0, - maximum: 127, - step: 1, - default_value: 64, - type: V4L2_CTRL_TYPE_INTEGER, + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .type = V4L2_CTRL_TYPE_INTEGER, },{ - id: V4L2_CID_VFLIP, - name: "Vertical flip", - minimum: 0, - maximum: 1, - type: V4L2_CTRL_TYPE_BOOLEAN, + .id = V4L2_CID_VFLIP, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, },{ - id: V4L2_CID_HFLIP, - name: "Horizontal flip", - minimum: 0, - maximum: 1, - type: V4L2_CTRL_TYPE_BOOLEAN, + .id = V4L2_CID_HFLIP, + .name = "Horizontal flip", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, }, }; static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl); @@ -1413,6 +1413,7 @@ static void video_close(struct saa7146_dev *dev, struct file *file) spin_lock_irqsave(&dev->slock,flags); saa7146_stop_preview(fh); spin_unlock_irqrestore(&dev->slock,flags); + saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); } } diff --git a/drivers/media/dvb/b2c2/skystar2.c b/drivers/media/dvb/b2c2/skystar2.c index 0bb04e3a2895..39c84aedd770 100644 --- a/drivers/media/dvb/b2c2/skystar2.c +++ b/drivers/media/dvb/b2c2/skystar2.c @@ -500,7 +500,7 @@ static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, } } -static void sram_writeChunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) +static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) { u32 bank; @@ -520,7 +520,7 @@ static void sram_writeChunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) flex_sram_write(adapter, bank, addr & 0x7fff, buf, len); } -static void sram_readChunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) +static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len) { u32 bank; @@ -554,7 +554,7 @@ static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len) length = (((addr >> 0x0f) + 1) << 0x0f) - addr; } - sram_readChunk(adapter, addr, buf, length); + sram_read_chunk(adapter, addr, buf, length); addr = addr + length; buf = buf + length; @@ -576,7 +576,7 @@ static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len) length = (((addr >> 0x0f) + 1) << 0x0f) - addr; } - sram_writeChunk(adapter, addr, buf, length); + sram_write_chunk(adapter, addr, buf, length); addr = addr + length; buf = buf + length; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 84ad3312248f..d403c3f70c53 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -426,6 +426,7 @@ static int dvb_frontend_is_exiting (struct dvb_frontend_data *fe) static int dvb_frontend_thread (void *data) { struct dvb_frontend_data *fe = (struct dvb_frontend_data *) data; + unsigned long timeout; char name [15]; int quality = 0, delay = 3*HZ; fe_status_t s; @@ -442,12 +443,14 @@ static int dvb_frontend_thread (void *data) dvb_call_frontend_notifiers (fe, 0); dvb_frontend_init (fe); - while (!dvb_frontend_is_exiting (fe)) { + while (1) { up (&fe->sem); /* is locked when we enter the thread... */ - interruptible_sleep_on_timeout (&fe->wait_queue, delay); - if (signal_pending(current)) + timeout = wait_event_interruptible_timeout(fe->wait_queue,0 != dvb_frontend_is_exiting (fe), delay); + if (-ERESTARTSYS == timeout || 0 != dvb_frontend_is_exiting (fe)) { + /* got signal or quitting */ break; + } if (down_interruptible (&fe->sem)) break; @@ -455,9 +458,6 @@ static int dvb_frontend_thread (void *data) if (fe->lost_sync_count == -1) continue; - if (dvb_frontend_is_exiting (fe)) - break; - dvb_frontend_internal_ioctl (&fe->frontend, FE_READ_STATUS, &s); update_delay (&quality, &delay, s & FE_HAS_LOCK); @@ -509,6 +509,8 @@ static int dvb_frontend_thread (void *data) static void dvb_frontend_stop (struct dvb_frontend_data *fe) { + unsigned long ret; + dprintk ("%s\n", __FUNCTION__); fe->exit = 1; @@ -526,10 +528,16 @@ static void dvb_frontend_stop (struct dvb_frontend_data *fe) return; } + /* wake up the frontend thread, so it notices that fe->exit == 1 */ wake_up_interruptible (&fe->wait_queue); - interruptible_sleep_on(&fe->wait_queue); - /* paranoia check */ + /* wait until the frontend thread has exited */ + ret = wait_event_interruptible(fe->wait_queue,0 == fe->thread_pid); + if (-ERESTARTSYS != ret) { + return; + } + + /* paranoia check in case a signal arrived */ if (fe->thread_pid) printk("dvb_frontend_stop: warning: thread PID %d won't exit\n", fe->thread_pid); diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c index c2fa6e04a731..03759e108846 100644 --- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c +++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c @@ -24,8 +24,6 @@ */ - -#define __KERNEL_SYSCALLS__ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> @@ -123,7 +121,7 @@ ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, in if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) return -EFAULT; - rbuf->pread = (rbuf->pread + len) % rbuf->size; + rbuf->pread = (rbuf->pread + todo) % rbuf->size; return len; } @@ -155,7 +153,7 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, if (copy_from_user(rbuf->data+rbuf->pwrite, buf, todo)) return -EFAULT; - rbuf->pwrite = (rbuf->pwrite + len) % rbuf->size; + rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; return len; } diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 0d0fb617f0e3..325e3feebf53 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -154,8 +154,8 @@ config DVB_VES1X93 right one will get autodetected. config DVB_TDA1004X - tristate "Frontends with external TDA1004X demodulators (OFDM)" - depends on DVB_CORE && !STANDALONE + tristate "Frontends with external TDA10045H or TDA10046H demodulators (OFDM)" + depends on DVB_CORE help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -173,3 +173,15 @@ config DVB_TDA1004X_FIRMWARE_FILE wget http://www.technotrend.de/new/215/TTweb_215a_budget_20_05_2003.zip unzip -j TTweb_215a_budget_20_05_2003.zip Software/Oem/PCI/App/ttlcdacc.dll mv ttlcdacc.dll /usr/lib/hotplug/firmware/tda1004x.bin + Note: even if you're using a USB device, you MUST get the file from the + TechnoTrend PCI drivers. + +config DVB_NXT6000 + tristate "Frontends with NxtWave Communications NXT6000 demodulator (OFDM)" + depends on DVB_CORE + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + If you don't know what tuner module is soldered on your + DVB adapter simply enable all supported frontends, the + right one will get autodetected. diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 7dc59058cf12..e536b0de7828 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o obj-$(CONFIG_DVB_SP887X) += sp887x.o +obj-$(CONFIG_DVB_NXT6000) += nxt6000.o diff --git a/drivers/media/dvb/frontends/alps_tdlb7.c b/drivers/media/dvb/frontends/alps_tdlb7.c index 5b0c03290c96..f1ca0da41397 100644 --- a/drivers/media/dvb/frontends/alps_tdlb7.c +++ b/drivers/media/dvb/frontends/alps_tdlb7.c @@ -61,22 +61,29 @@ static int debug = 0; static int errno; static struct dvb_frontend_info tdlb7_info = { - name: "Alps TDLB7", - type: FE_OFDM, - frequency_min: 470000000, - frequency_max: 860000000, - frequency_stepsize: 166666, - caps: FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER + .name = "Alps TDLB7", + .type = FE_OFDM, + .frequency_min = 470000000, + .frequency_max = 860000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER }; static int sp8870_writereg (struct dvb_i2c_bus *i2c, u16 reg, u16 data) { u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; - struct i2c_msg msg = { addr: 0x71, flags: 0, buf: buf, len: 4 }; + struct i2c_msg msg = { + .addr = 0x71, + .flags = 0, + .buf = buf, + .len = 4 + }; int err; if ((err = i2c->xfer (i2c, &msg, 1)) != 1) { @@ -93,8 +100,20 @@ static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg) int ret; u8 b0 [] = { reg >> 8 , reg & 0xff }; u8 b1 [] = { 0, 0 }; - struct i2c_msg msg [] = { { addr: 0x71, flags: 0, buf: b0, len: 2 }, - { addr: 0x71, flags: I2C_M_RD, buf: b1, len: 2 } }; + struct i2c_msg msg [] = { + { + .addr = 0x71, + .flags = 0, + .buf = b0, + .len = 2 + }, + { + .addr = 0x71, + .flags = I2C_M_RD, + .buf = b1, + .len = 2 + } + }; ret = i2c->xfer (i2c, msg, 2); @@ -110,7 +129,12 @@ static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg) static int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4]) { int ret; - struct i2c_msg msg = { addr: 0x60, flags: 0, buf: data, len: 4 }; + struct i2c_msg msg = { + .addr = 0x60, + .flags = 0, + .buf = data, + .len =4 + }; ret = i2c->xfer (i2c, &msg, 1); @@ -665,11 +689,27 @@ static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) static int tdlb7_attach (struct dvb_i2c_bus *i2c, void **data) { - struct i2c_msg msg = { addr: 0x71, flags: 0, buf: NULL, len: 0 }; + u8 b0 [] = { 0x02 , 0x00 }; + u8 b1 [] = { 0, 0 }; + struct i2c_msg msg [] = + { + { + .addr = 0x71, + .flags = 0, + .buf = b0, + .len = 2 + }, + { + .addr = 0x71, + .flags = I2C_M_RD, + .buf = b1, + .len = 2 + } + }; dprintk ("%s\n", __FUNCTION__); - if (i2c->xfer (i2c, &msg, 1) != 1) + if (i2c->xfer (i2c, msg, 2) != 2) return -ENODEV; sp8870_firmware_upload(i2c); diff --git a/drivers/media/dvb/frontends/alps_tdmb7.c b/drivers/media/dvb/frontends/alps_tdmb7.c index 072bc89076c9..6107c2b5e80f 100644 --- a/drivers/media/dvb/frontends/alps_tdmb7.c +++ b/drivers/media/dvb/frontends/alps_tdmb7.c @@ -404,11 +404,14 @@ static int tdmb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) static int tdmb7_attach (struct dvb_i2c_bus *i2c, void **data) { - struct i2c_msg msg = { .addr = 0x43, .flags = 0, .buf = NULL,. len = 0 }; + u8 b0 [] = { 0x7 }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = 0x43, .flags = 0, .buf = b0, .len = 1 }, + { .addr = 0x43, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; dprintk ("%s\n", __FUNCTION__); - if (i2c->xfer (i2c, &msg, 1) != 1) + if (i2c->xfer (i2c, msg, 2) != 2) return -ENODEV; return dvb_register_frontend (tdmb7_ioctl, i2c, NULL, &tdmb7_info); diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c index db739c8b5e8f..14e9cfe2f1d6 100644 --- a/drivers/media/dvb/frontends/nxt6000.c +++ b/drivers/media/dvb/frontends/nxt6000.c @@ -758,11 +758,11 @@ static int nxt6000_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) } case FE_INIT: - case FE_RESET: - nxt6000_reset(fe); nxt6000_setup(fe); + break; + case FE_RESET: break; case FE_SET_FRONTEND: diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c index 0610bd3d2ee2..645aab4f1700 100644 --- a/drivers/media/dvb/frontends/sp887x.c +++ b/drivers/media/dvb/frontends/sp887x.c @@ -58,7 +58,7 @@ static char *sp887x_firmware = DVB_SP887X_FIRMWARE_FILE; static struct dvb_frontend_info sp887x_info = { - .name = "Microtune MT7072DTF", + .name = "Microtune MT7202DTF", .type = FE_OFDM, .frequency_min = 50500000, .frequency_max = 858000000, @@ -74,7 +74,12 @@ static int i2c_writebytes (struct dvb_frontend *fe, u8 addr, u8 *buf, u8 len) { struct dvb_i2c_bus *i2c = fe->i2c; - struct i2c_msg msg = { addr: addr, flags: 0, buf: buf, len: len }; + struct i2c_msg msg = { + .addr = addr, + .flags = 0, + .buf = buf, + .len = len + }; int err; LOG("i2c_writebytes", msg.addr, msg.buf, msg.len); @@ -645,7 +650,12 @@ int sp887x_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) static int sp887x_attach (struct dvb_i2c_bus *i2c, void **data) { - struct i2c_msg msg = { addr: 0x70, flags: 0, buf: NULL, len: 0 }; + struct i2c_msg msg = { + .addr = 0x70, + .flags = 0, + .buf = NULL, + .len = 0 + }; dprintk ("%s\n", __FUNCTION__); diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 705132320f8e..07cf80625e44 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -25,6 +25,9 @@ Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>: + Support for Philips SU1278 on Technotrend hardware + + Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> 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 @@ -65,15 +68,19 @@ static int stv0299_status = 0; /* frontend types */ #define UNKNOWN_FRONTEND -1 -#define PHILIPS_SU1278SH 0 +#define PHILIPS_SU1278_TSA 0 // SU1278 with TSA5959 synth and datasheet recommended settings #define ALPS_BSRU6 1 #define LG_TDQF_S001F 2 -#define PHILIPS_SU1278 3 +#define PHILIPS_SU1278_TUA 3 // SU1278 with TUA6100 synth #define SAMSUNG_TBMU24112IMB 4 +#define PHILIPS_SU1278_TSA_TT 5 // SU1278 with TSA5959 synth and TechnoTrend settings /* Master Clock = 88 MHz */ #define M_CLK (88000000UL) +/* Master Clock for TT cards = 64 MHz */ +#define M_CLK_SU1278_TSA_TT (64000000UL) + static struct dvb_frontend_info uni0299_info = { .name = "STV0299/TSA5059/SL1935 based", .type = FE_QPSK, @@ -201,6 +208,51 @@ static u8 init_tab_samsung [] = { }; +static u8 init_tab_su1278_tsa_tt [] = { + 0x01, 0x0f, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x5b, + 0x05, 0x85, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0x02, + 0x09, 0x00, + 0x0C, 0x01, + 0x0D, 0x81, + 0x0E, 0x44, + 0x0f, 0x14, + 0x10, 0x3c, + 0x11, 0x84, + 0x12, 0xda, + 0x13, 0x97, + 0x14, 0x95, + 0x15, 0xc9, + 0x16, 0x19, + 0x17, 0x8c, + 0x18, 0x59, + 0x19, 0xf8, + 0x1a, 0xfe, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x09, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x13 +}; + static int stv0299_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data) { int ret; @@ -253,8 +305,12 @@ static int stv0299_readregs (struct dvb_i2c_bus *i2c, u8 reg1, u8 *b, u8 len) static int pll_write (struct dvb_i2c_bus *i2c, u8 addr, u8 *data, int len) { int ret; - struct i2c_msg msg = { addr: addr, .flags = 0, .buf = data, .len = len }; - + struct i2c_msg msg = { + .addr = addr, + .flags = 0, + .buf = data, + .len = len + }; stv0299_writereg(i2c, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ @@ -297,21 +353,26 @@ static int tsa5059_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, int ftype, in u8 addr; u32 div; u8 buf[4]; + int i, divisor, regcode; dprintk ("%s: freq %i, ftype %i\n", __FUNCTION__, freq, ftype); if ((freq < 950000) || (freq > 2150000)) return -EINVAL; + divisor = 500; + regcode = 2; + // setup frequency divisor - div = freq / 1000; + div = freq / divisor; buf[0] = (div >> 8) & 0x7f; buf[1] = div & 0xff; - buf[2] = 0x81 | ((div & 0x18000) >> 10); + buf[2] = 0x80 | ((div & 0x18000) >> 10) | regcode; buf[3] = 0; // tuner-specific settings switch(ftype) { - case PHILIPS_SU1278SH: + case PHILIPS_SU1278_TSA: + case PHILIPS_SU1278_TSA_TT: addr = 0x60; buf[3] |= 0x20; @@ -332,7 +393,6 @@ static int tsa5059_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, int ftype, in return -EINVAL; } - // charge pump return pll_write (i2c, addr, buf, sizeof(buf)); } @@ -465,15 +525,20 @@ static int tua6100_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, static int pll_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, int ftype, int srate) { - if (ftype == SAMSUNG_TBMU24112IMB) + switch(ftype) { + case SAMSUNG_TBMU24112IMB: return sl1935_set_tv_freq(i2c, freq, ftype); - else if (ftype == LG_TDQF_S001F) + + case LG_TDQF_S001F: return sl1935_set_tv_freq(i2c, freq, ftype); - else if (ftype == PHILIPS_SU1278) + + case PHILIPS_SU1278_TUA: return tua6100_set_tv_freq(i2c, freq, ftype, srate); - else + + default: return tsa5059_set_tv_freq(i2c, freq, ftype, srate); } +} #if 0 static int tsa5059_read_status (struct dvb_i2c_bus *i2c) @@ -515,18 +580,24 @@ static int stv0299_init (struct dvb_i2c_bus *i2c, int ftype) } break; + case PHILIPS_SU1278_TSA_TT: + for (i=0; i<sizeof(init_tab_su1278_tsa_tt); i+=2) { + stv0299_writereg (i2c, init_tab_su1278_tsa_tt[i], init_tab_su1278_tsa_tt[i+1]); + } + break; + default: stv0299_writereg (i2c, 0x01, 0x15); - stv0299_writereg (i2c, 0x02, ftype == PHILIPS_SU1278 ? 0x00 : 0x30); + stv0299_writereg (i2c, 0x02, ftype == PHILIPS_SU1278_TUA ? 0x00 : 0x30); stv0299_writereg (i2c, 0x03, 0x00); for (i=0; i<sizeof(init_tab); i+=2) stv0299_writereg (i2c, init_tab[i], init_tab[i+1]); /* AGC1 reference register setup */ - if (ftype == PHILIPS_SU1278SH) + if (ftype == PHILIPS_SU1278_TSA) stv0299_writereg (i2c, 0x0f, 0x92); /* Iagc = Inverse, m1 = 18 */ - else if (ftype == PHILIPS_SU1278) + else if (ftype == PHILIPS_SU1278_TUA) stv0299_writereg (i2c, 0x0f, 0x94); /* Iagc = Inverse, m1 = 20 */ else stv0299_writereg (i2c, 0x0f, 0x52); /* Iagc = Normal, m1 = 18 */ @@ -796,10 +867,49 @@ static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner u8 aclk = 0; u8 bclk = 0; u8 m1; + int Mclk = M_CLK; + // check rate is within limits if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; + + // calculate value to program + if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT; + big = big << 20; + do_div(big, Mclk); + ratio = big << 4; + + // program registers switch(tuner_type) { - case PHILIPS_SU1278SH: + case PHILIPS_SU1278_TSA_TT: + stv0299_writereg (i2c, 0x0e, 0x44); + if (srate >= 10000000) { + stv0299_writereg (i2c, 0x13, 0x97); + stv0299_writereg (i2c, 0x14, 0x95); + stv0299_writereg (i2c, 0x15, 0xc9); + stv0299_writereg (i2c, 0x17, 0x8c); + stv0299_writereg (i2c, 0x1a, 0xfe); + stv0299_writereg (i2c, 0x1c, 0x7f); + stv0299_writereg (i2c, 0x2d, 0x09); + } else { + stv0299_writereg (i2c, 0x13, 0x99); + stv0299_writereg (i2c, 0x14, 0x8d); + stv0299_writereg (i2c, 0x15, 0xce); + stv0299_writereg (i2c, 0x17, 0x43); + stv0299_writereg (i2c, 0x1a, 0x1d); + stv0299_writereg (i2c, 0x1c, 0x12); + stv0299_writereg (i2c, 0x2d, 0x05); + } + stv0299_writereg (i2c, 0x0e, 0x23); + stv0299_writereg (i2c, 0x0f, 0x94); + stv0299_writereg (i2c, 0x10, 0x39); + stv0299_writereg (i2c, 0x15, 0xc9); + + stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); + break; + + case PHILIPS_SU1278_TSA: aclk = 0xb5; if (srate < 2000000) bclk = 0x86; else if (srate < 5000000) bclk = 0x89; @@ -808,6 +918,13 @@ static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner m1 = 0x14; if (srate < 4000000) m1 = 0x10; + + stv0299_writereg (i2c, 0x13, aclk); + stv0299_writereg (i2c, 0x14, bclk); + stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); + stv0299_writereg (i2c, 0x0f, (stv0299_readreg(i2c, 0x0f) & 0xc0) | m1); break; case ALPS_BSRU6: @@ -818,24 +935,7 @@ static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner else if (srate <= 14000000) { aclk = 0xb7; bclk = 0x93; } else if (srate <= 30000000) { aclk = 0xb6; bclk = 0x93; } else if (srate <= 45000000) { aclk = 0xb4; bclk = 0x91; } - m1 = 0x12; - break; - } - - dprintk("%s : big = 0x%08x%08x\n", __FUNCTION__, (int) ((big>>32) & 0xffffffff), (int) (big & 0xffffffff) ); - - big = big << 20; - - dprintk("%s : big = 0x%08x%08x\n", __FUNCTION__, (int) ((big>>32) & 0xffffffff), (int) (big & 0xffffffff) ); - - do_div(big, M_CLK); - - dprintk("%s : big = 0x%08x%08x\n", __FUNCTION__, (int) ((big>>32) & 0xffffffff), (int) (big & 0xffffffff) ); - - ratio = big << 4; - - dprintk("%s : ratio = %i\n", __FUNCTION__, ratio); stv0299_writereg (i2c, 0x13, aclk); stv0299_writereg (i2c, 0x14, bclk); @@ -843,12 +943,15 @@ static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); stv0299_writereg (i2c, 0x0f, (stv0299_readreg(i2c, 0x0f) & 0xc0) | m1); + break; + } + return 0; } -static int stv0299_get_symbolrate (struct dvb_i2c_bus *i2c) +static int stv0299_get_symbolrate (struct dvb_i2c_bus *i2c, int tuner_type) { u32 Mclk = M_CLK / 4096L; u32 srate; @@ -858,6 +961,8 @@ static int stv0299_get_symbolrate (struct dvb_i2c_bus *i2c) dprintk ("%s\n", __FUNCTION__); + if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT / 4096L; + stv0299_readregs (i2c, 0x1f, sfr, 3); stv0299_readregs (i2c, 0x1a, &rtf, 1); @@ -891,8 +996,15 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) switch (cmd) { case FE_GET_INFO: + { + struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) arg; memcpy (arg, &uni0299_info, sizeof(struct dvb_frontend_info)); + + if (tuner_type == PHILIPS_SU1278_TSA_TT) { + tmp->frequency_tolerance = M_CLK_SU1278_TSA_TT / 2000; + } break; + } case FE_READ_STATUS: { @@ -976,8 +1088,10 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, tuner_type); stv0299_writereg (i2c, 0x22, 0x00); stv0299_writereg (i2c, 0x23, 0x00); + if (tuner_type != PHILIPS_SU1278_TSA_TT) { stv0299_readreg (i2c, 0x23); stv0299_writereg (i2c, 0x12, 0xb9); + } stv0299_check_inversion (i2c); /* printk ("%s: tsa5059 status: %x\n", __FUNCTION__, tsa5059_read_status(i2c)); */ @@ -988,11 +1102,14 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) { struct dvb_frontend_parameters *p = arg; s32 derot_freq; + int Mclk = M_CLK; + + if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT; derot_freq = (s32)(s16) ((stv0299_readreg (i2c, 0x22) << 8) | stv0299_readreg (i2c, 0x23)); - derot_freq *= (M_CLK >> 16); + derot_freq *= (Mclk >> 16); derot_freq += 500; derot_freq /= 1000; @@ -1000,7 +1117,7 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) p->inversion = (stv0299_readreg (i2c, 0x0c) & 1) ? INVERSION_OFF : INVERSION_ON; p->u.qpsk.fec_inner = stv0299_get_fec (i2c); - p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c); + p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c, tuner_type); break; } @@ -1041,12 +1158,48 @@ static long probe_tuner (struct dvb_i2c_bus *i2c) u8 stat [] = { 0 }; u8 tda6100_buf [] = { 0, 0 }; int ret; - struct i2c_msg msg1 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, len: 2 }, - { .addr = 0x60, .flags = I2C_M_RD, .buf = stat, .len = 1 }}; - struct i2c_msg msg2 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, len: 2 }, - { .addr = 0x61, .flags = I2C_M_RD, .buf = stat, .len = 1 }}; - struct i2c_msg msg3 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, len: 2 }, - { .addr = 0x60, .flags = 0, .buf = tda6100_buf, .len = 2 }}; + struct i2c_msg msg1 [] = { + { + .addr = 0x68, + .flags = 0, + .buf = rpt, + .len = 2 + }, + { + .addr = 0x60, + .flags = I2C_M_RD, + .buf = stat, + .len = 1 + } + }; + struct i2c_msg msg2 [] = { + { + .addr = 0x68, + .flags = 0, + .buf = rpt, + .len = 2 + }, + { + .addr = 0x61, + .flags = I2C_M_RD, + .buf = stat, + .len = 1 + } + }; + struct i2c_msg msg3 [] = { + { + .addr = 0x68, + .flags = 0, + .buf = rpt, + .len = 2 + }, + { + .addr = 0x60, + .flags = 0, + .buf = tda6100_buf, + .len = 2 + } + }; stv0299_writereg (i2c, 0x01, 0x15); stv0299_writereg (i2c, 0x02, 0x30); @@ -1062,10 +1215,16 @@ static long probe_tuner (struct dvb_i2c_bus *i2c) return SAMSUNG_TBMU24112IMB; } - if ((ret = i2c->xfer(i2c, msg1, 2)) == 2) { - printk ("%s: setup for tuner SU1278/SH\n", __FILE__); - return PHILIPS_SU1278SH; + if ( strcmp(adapter->name, "TT-Budget/WinTV-NOVA-CI PCI") == 0 ) { + // technotrend cards require non-datasheet settings + printk ("%s: setup for tuner SU1278 (TSA5959 synth) on TechnoTrend hardware\n", __FILE__); + return PHILIPS_SU1278_TSA_TT; + } else { + // fall back to datasheet-recommended settings + printk ("%s: setup for tuner SU1278 (TSA5959 synth)\n", __FILE__); + return PHILIPS_SU1278_TSA; + } } if ((ret = i2c->xfer(i2c, msg2, 2)) == 2) { @@ -1086,8 +1245,8 @@ static long probe_tuner (struct dvb_i2c_bus *i2c) stv0299_writereg (i2c, 0x02, 0x00); if ((ret = i2c->xfer(i2c, msg3, 2)) == 2) { - printk ("%s: setup for tuner Philips SU1278\n", __FILE__); - return PHILIPS_SU1278; + printk ("%s: setup for tuner Philips SU1278 (TUA6100 synth)\n", __FILE__); + return PHILIPS_SU1278_TUA; } printk ("%s: unknown PLL synthesizer (ret == %i), " diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 648a3f79d83f..48c8435c8a6f 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -1,6 +1,8 @@ /* Driver for Philips tda1004xh OFDM Frontend + (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach + 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 @@ -20,7 +22,7 @@ /* This driver needs a copy of the DLL "ttlcdacc.dll" from the Haupauge or Technotrend - windows driver saved as '/usr/lib/hotplug/firmware/tda1004x.mc'. + windows driver saved as '/usr/lib/hotplug/firmware/tda1004x.bin'. You can also pass the complete file name with the module parameter 'tda1004x_firmware'. Currently the DLL from v2.15a of the technotrend driver is supported. Other versions can @@ -46,16 +48,12 @@ #include "dvb_functions.h" #ifndef DVB_TDA1004X_FIRMWARE_FILE -#define DVB_TDA1004X_FIRMWARE_FILE "/usr/lib/hotplug/firmware/tda1004x.mc" +#define DVB_TDA1004X_FIRMWARE_FILE "/usr/lib/hotplug/firmware/tda1004x.bin" #endif static int tda1004x_debug = 0; static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE; - -#define TDA10045H_ADDRESS 0x08 -#define TD1344_ADDRESS 0x61 -#define TDM1316L_ADDRESS 0x63 #define MC44BC374_ADDRESS 0x65 #define TDA1004X_CHIPID 0x00 @@ -67,8 +65,8 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE; #define TDA1004X_STATUS_CD 0x06 #define TDA1004X_CONFC4 0x07 #define TDA1004X_DSSPARE2 0x0C -#define TDA1004X_CODE_IN 0x0D -#define TDA1004X_FWPAGE 0x0E +#define TDA10045H_CODE_IN 0x0D +#define TDA10045H_FWPAGE 0x0E #define TDA1004X_SCAN_CPT 0x10 #define TDA1004X_DSP_CMD 0x11 #define TDA1004X_DSP_ARG 0x12 @@ -76,10 +74,11 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE; #define TDA1004X_DSP_DATA2 0x14 #define TDA1004X_CONFADC1 0x15 #define TDA1004X_CONFC1 0x16 -#define TDA1004X_SIGNAL_STRENGTH 0x1a +#define TDA10045H_S_AGC 0x1a +#define TDA10046H_AGC_TUN_LEVEL 0x1a #define TDA1004X_SNR 0x1c -#define TDA1004X_REG1E 0x1e -#define TDA1004X_REG1F 0x1f +#define TDA1004X_CONF_TS1 0x1e +#define TDA1004X_CONF_TS2 0x1f #define TDA1004X_CBER_RESET 0x20 #define TDA1004X_CBER_MSB 0x21 #define TDA1004X_CBER_LSB 0x22 @@ -88,18 +87,58 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE; #define TDA1004X_VBER_MID 0x25 #define TDA1004X_VBER_LSB 0x26 #define TDA1004X_UNCOR 0x27 -#define TDA1004X_CONFPLL_P 0x2D -#define TDA1004X_CONFPLL_M_MSB 0x2E -#define TDA1004X_CONFPLL_M_LSB 0x2F -#define TDA1004X_CONFPLL_N 0x30 -#define TDA1004X_UNSURW_MSB 0x31 -#define TDA1004X_UNSURW_LSB 0x32 -#define TDA1004X_WREF_MSB 0x33 -#define TDA1004X_WREF_MID 0x34 -#define TDA1004X_WREF_LSB 0x35 -#define TDA1004X_MUXOUT 0x36 + +#define TDA10045H_CONFPLL_P 0x2D +#define TDA10045H_CONFPLL_M_MSB 0x2E +#define TDA10045H_CONFPLL_M_LSB 0x2F +#define TDA10045H_CONFPLL_N 0x30 + +#define TDA10046H_CONFPLL1 0x2D +#define TDA10046H_CONFPLL2 0x2F +#define TDA10046H_CONFPLL3 0x30 +#define TDA10046H_TIME_WREF1 0x31 +#define TDA10046H_TIME_WREF2 0x32 +#define TDA10046H_TIME_WREF3 0x33 +#define TDA10046H_TIME_WREF4 0x34 +#define TDA10046H_TIME_WREF5 0x35 + +#define TDA10045H_UNSURW_MSB 0x31 +#define TDA10045H_UNSURW_LSB 0x32 +#define TDA10045H_WREF_MSB 0x33 +#define TDA10045H_WREF_MID 0x34 +#define TDA10045H_WREF_LSB 0x35 +#define TDA10045H_MUXOUT 0x36 #define TDA1004X_CONFADC2 0x37 -#define TDA1004X_IOFFSET 0x38 + +#define TDA10045H_IOFFSET 0x38 + +#define TDA10046H_CONF_TRISTATE1 0x3B +#define TDA10046H_CONF_TRISTATE2 0x3C +#define TDA10046H_CONF_POLARITY 0x3D +#define TDA10046H_FREQ_OFFSET 0x3E +#define TDA10046H_GPIO_OUT_SEL 0x41 +#define TDA10046H_GPIO_SELECT 0x42 +#define TDA10046H_AGC_CONF 0x43 +#define TDA10046H_AGC_GAINS 0x46 +#define TDA10046H_AGC_TUN_MIN 0x47 +#define TDA10046H_AGC_TUN_MAX 0x48 +#define TDA10046H_AGC_IF_MIN 0x49 +#define TDA10046H_AGC_IF_MAX 0x4A + +#define TDA10046H_FREQ_PHY2_MSB 0x4D +#define TDA10046H_FREQ_PHY2_LSB 0x4E + +#define TDA10046H_CVBER_CTRL 0x4F +#define TDA10046H_AGC_IF_LEVEL 0x52 +#define TDA10046H_CODE_CPT 0x57 +#define TDA10046H_CODE_IN 0x58 + + +#define FE_TYPE_TDA10045H 0 +#define FE_TYPE_TDA10046H 1 + +#define TUNER_TYPE_TD1344 0 +#define TUNER_TYPE_TD1316 1 #define dprintk if (tda1004x_debug) printk @@ -116,11 +155,27 @@ static struct dvb_frontend_info tda10045h_info = { FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO }; +static struct dvb_frontend_info tda10046h_info = { + .name = "Philips TDA10046H", + .type = FE_OFDM, + .frequency_min = 51000000, + .frequency_max = 858000000, + .frequency_stepsize = 166667, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO +}; + + #pragma pack(1) struct tda1004x_state { u8 tda1004x_address; u8 tuner_address; u8 initialised:1; + u8 tuner_type:2; + u8 fe_type:2; }; #pragma pack() @@ -132,6 +187,9 @@ struct fwinfo { static struct fwinfo tda10045h_fwinfo[] = { {.file_size = 286720,.fw_offset = 0x34cc5,.fw_size = 30555} }; static int tda10045h_fwinfo_count = sizeof(tda10045h_fwinfo) / sizeof(struct fwinfo); +static struct fwinfo tda10046h_fwinfo[] = { {.file_size = 286720,.fw_offset = 0x3c4f9,.fw_size = 24479} }; +static int tda10046h_fwinfo_count = sizeof(tda10046h_fwinfo) / sizeof(struct fwinfo); + static int errno; @@ -246,46 +304,98 @@ static int tda10045h_set_bandwidth(struct dvb_i2c_bus *i2c, switch (bandwidth) { case BANDWIDTH_6_MHZ: tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14); - tda1004x_write_buf(i2c, tda_state, TDA1004X_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); + tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); break; case BANDWIDTH_7_MHZ: tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x80); - tda1004x_write_buf(i2c, tda_state, TDA1004X_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); + tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); break; case BANDWIDTH_8_MHZ: tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14); - tda1004x_write_buf(i2c, tda_state, TDA1004X_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); + tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); break; default: return -EINVAL; } - tda1004x_write_byte(i2c, tda_state, TDA1004X_IOFFSET, 0); + tda1004x_write_byte(i2c, tda_state, TDA10045H_IOFFSET, 0); + + // done + return 0; +} + + +static int tda10046h_set_bandwidth(struct dvb_i2c_bus *i2c, + struct tda1004x_state *tda_state, + fe_bandwidth_t bandwidth) +{ + static u8 bandwidth_6mhz[] = { 0x80, 0x15, 0xfe, 0xab, 0x8e }; + static u8 bandwidth_7mhz[] = { 0x6e, 0x02, 0x53, 0xc8, 0x25 }; + static u8 bandwidth_8mhz[] = { 0x60, 0x12, 0xa8, 0xe4, 0xbd }; + + switch (bandwidth) { + case BANDWIDTH_6_MHZ: + tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz)); + tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0); + break; + + case BANDWIDTH_7_MHZ: + tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz)); + tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0); + break; + + case BANDWIDTH_8_MHZ: + tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz)); + tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0xFF); + break; + + default: + return -EINVAL; + } // done return 0; } -static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state) +static int tda1004x_fwupload(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state) { u8 fw_buf[65]; struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = fw_buf,.len = 0 }; - struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = 0,.len = 0 }; unsigned char *firmware = NULL; int filesize; int fd; int fwinfo_idx; int fw_size = 0; - int fw_pos; + int fw_pos, fw_offset; int tx_size; - static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; mm_segment_t fs = get_fs(); + int dspCodeCounterReg=0, dspCodeInReg=0, dspVersion=0; + int fwInfoCount=0; + struct fwinfo* fwInfo = NULL; + unsigned long timeout; + + // DSP parameters + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: + dspCodeCounterReg = TDA10045H_FWPAGE; + dspCodeInReg = TDA10045H_CODE_IN; + dspVersion = 0x2c; + fwInfoCount = tda10045h_fwinfo_count; + fwInfo = tda10045h_fwinfo; + break; - dprintk("%s\n", __FUNCTION__); + case FE_TYPE_TDA10046H: + dspCodeCounterReg = TDA10046H_CODE_CPT; + dspCodeInReg = TDA10046H_CODE_IN; + dspVersion = 0x20; + fwInfoCount = tda10046h_fwinfo_count; + fwInfo = tda10046h_fwinfo; + break; + } // Load the firmware set_fs(get_ds()); @@ -303,17 +413,18 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta return -EIO; } - // find extraction parameters - for (fwinfo_idx = 0; fwinfo_idx < tda10045h_fwinfo_count; fwinfo_idx++) { - if (tda10045h_fwinfo[fwinfo_idx].file_size == filesize) + // find extraction parameters for firmware + for (fwinfo_idx = 0; fwinfo_idx < fwInfoCount; fwinfo_idx++) { + if (fwInfo[fwinfo_idx].file_size == filesize) break; } - if (fwinfo_idx >= tda10045h_fwinfo_count) { + if (fwinfo_idx >= fwInfoCount) { printk("%s: Unsupported firmware %s\n", __FUNCTION__, tda1004x_firmware); sys_close(fd); return -EIO; } - fw_size = tda10045h_fwinfo[fwinfo_idx].fw_size; + fw_size = fwInfo[fwinfo_idx].fw_size; + fw_offset = fwInfo[fwinfo_idx].fw_offset; // allocate buffer for it firmware = vmalloc(fw_size); @@ -325,7 +436,7 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta } // read it! - lseek(fd, tda10045h_fwinfo[fwinfo_idx].fw_offset, 0); + lseek(fd, fw_offset, 0); if (read(fd, firmware, fw_size) != fw_size) { printk("%s: Failed to read firmware\n", __FUNCTION__); vfree(firmware); @@ -335,39 +446,51 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta sys_close(fd); set_fs(fs); - // Disable the MC44BC374C - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = MC44BC374_ADDRESS; - tuner_msg.buf = disable_mc44BC374c; - tuner_msg.len = sizeof(disable_mc44BC374c); - if (i2c->xfer(i2c, &tuner_msg, 1) != 1) { - i2c->xfer(i2c, &tuner_msg, 1); - } - tda1004x_disable_tuner_i2c(i2c, tda_state); + // set some valid bandwith parameters before uploading + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: + // reset chip + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8); + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0); + dvb_delay(10); - // set some valid bandwith parameters - switch(tda_state->tda1004x_address) { - case TDA10045H_ADDRESS: + // set parameters tda10045h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ); break; + + case FE_TYPE_TDA10046H: + // reset chip + tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 1, 0); + dvb_delay(10); + + // set parameters + tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL2, 10); + tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL3, 0); + tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_OFFSET, 99); + tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_MSB, 0xd4); + tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_LSB, 0x2c); + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST + break; } - dvb_delay(500); // do the firmware upload - tda1004x_write_byte(i2c, tda_state, TDA1004X_FWPAGE, 0); + tda1004x_write_byte(i2c, tda_state, dspCodeCounterReg, 0); // clear code counter fw_msg.addr = tda_state->tda1004x_address; fw_pos = 0; while (fw_pos != fw_size) { + // work out how much to send this time tx_size = fw_size - fw_pos; - if (tx_size > 64) { - tx_size = 64; + if (tx_size > 0x10) { + tx_size = 0x10; } + // send the chunk - fw_buf[0] = TDA1004X_CODE_IN; + fw_buf[0] = dspCodeInReg; memcpy(fw_buf + 1, firmware + fw_pos, tx_size); fw_msg.len = tx_size + 1; if (i2c->xfer(i2c, &fw_msg, 1) != 1) { + printk("tda1004x: Error during firmware upload\n"); vfree(firmware); return -EIO; } @@ -375,35 +498,128 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, fw_pos); } - dvb_delay(100); vfree(firmware); - // Initialise the DSP and check upload was OK - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x10, 0); + // wait for DSP to initialise + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: + // DSPREADY doesn't seem to work on the TDA10045H + dvb_delay(100); + break; + + case FE_TYPE_TDA10046H: + timeout = jiffies + HZ; + while(!(tda1004x_read_byte(i2c, tda_state, TDA1004X_STATUS_CD) & 0x20)) { + if (time_after(jiffies, timeout)) { + printk("tda1004x: DSP failed to initialised.\n"); + return -EIO; + } + + dvb_delay(1); + } + break; + } + + // check upload was OK + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP tda1004x_write_byte(i2c, tda_state, TDA1004X_DSP_CMD, 0x67); if ((tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA1) != 0x67) || - (tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA2) != 0x2c)) { + (tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA2) != dspVersion)) { printk("%s: firmware upload failed!\n", __FUNCTION__); return -EIO; } + // success + return 0; +} + + +static int tda10045h_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state) +{ + struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = 0,.len = 0 }; + static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; + + dprintk("%s\n", __FUNCTION__); + + // Disable the MC44BC374C + tda1004x_enable_tuner_i2c(i2c, tda_state); + tuner_msg.addr = MC44BC374_ADDRESS; + tuner_msg.buf = disable_mc44BC374c; + tuner_msg.len = sizeof(disable_mc44BC374c); + if (i2c->xfer(i2c, &tuner_msg, 1) != 1) { + i2c->xfer(i2c, &tuner_msg, 1); + } + tda1004x_disable_tuner_i2c(i2c, tda_state); + // tda setup - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0x0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer + tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0); // no frequency inversion + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer + tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset + tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset + tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface + tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface + tda1004x_write_mask(i2c, tda_state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity tda1004x_write_byte(i2c, tda_state, TDA1004X_CONFADC1, 0x2e); - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0x80); - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x10, 0); - tda1004x_write_byte(i2c, tda_state, TDA1004X_REG1E, 0); - tda1004x_write_byte(i2c, tda_state, TDA1004X_REG1F, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // done return 0; } + + +static int tda10046h_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state) +{ + struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = 0,.len = 0 }; + static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; + + dprintk("%s\n", __FUNCTION__); + + // Disable the MC44BC374C + tda1004x_enable_tuner_i2c(i2c, tda_state); + tuner_msg.addr = MC44BC374_ADDRESS; + tuner_msg.buf = disable_mc44BC374c; + tuner_msg.len = sizeof(disable_mc44BC374c); + if (i2c->xfer(i2c, &tuner_msg, 1) != 1) { + i2c->xfer(i2c, &tuner_msg, 1); + } + tda1004x_disable_tuner_i2c(i2c, tda_state); + + // tda setup + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0x40); // TT TDA10046H needs inversion ON + tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer + tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL2, 10); // PLL M = 10 + tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0 + tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99 + tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221 + tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // } + tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_CONF, 0); // AGC setup + tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities + tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MIN, 0); // } + tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values + tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MIN, 0); // } + tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MAX, 0xff); // } + tda1004x_write_mask(i2c, tda_state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits + tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1 + tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm + tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config + tda1004x_write_mask(i2c, tda_state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config + tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN + tda1004x_write_byte(i2c, tda_state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup + tda1004x_write_byte(i2c, tda_state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config + tda1004x_write_mask(i2c, tda_state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select + tda10046h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz + + // done + return 0; +} + + + static int tda1004x_encode_fec(int fec) { // convert known FEC values @@ -450,17 +666,18 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, { u8 tuner_buf[4]; struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) }; - int tuner_frequency; + int tuner_frequency = 0; u8 band, cp, filter; int counter, counter2; dprintk("%s\n", __FUNCTION__); // setup the frequency buffer - switch (tda_state->tuner_address) { - case TD1344_ADDRESS: + switch (tda_state->tuner_type) { + case TUNER_TYPE_TD1344: // setup tuner buffer + // ((Fif+((1000000/6)/2)) + Finput)/(1000000/6) tuner_frequency = (((fe_params->frequency / 1000) * 6) + 217502) / 1000; tuner_buf[0] = tuner_frequency >> 8; @@ -499,7 +716,7 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, tda1004x_disable_tuner_i2c(i2c, tda_state); break; - case TDM1316L_ADDRESS: + case TUNER_TYPE_TD1316: // determine charge pump tuner_frequency = fe_params->frequency + 36130000; if (tuner_frequency < 87000000) { @@ -542,9 +759,7 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, // work out filter switch (fe_params->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: - // 6 MHz isn't supported directly, but set this to - // the 8 MHz setting in case we can fiddle it later - filter = 1; + filter = 0; break; case BANDWIDTH_7_MHZ: @@ -559,15 +774,27 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, return -EINVAL; } - // calculate tuner parameters + // calculate divisor + // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) tuner_frequency = (((fe_params->frequency / 1000) * 6) + 217280) / 1000; + + // setup tuner buffer tuner_buf[0] = tuner_frequency >> 8; tuner_buf[1] = tuner_frequency & 0xff; tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; // tune it + if (tda_state->fe_type == FE_TYPE_TDA10046H) { + // setup auto offset + tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); + tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x80, 0); + tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0); + + // disable agc_conf[2] + tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 0); + } tda1004x_enable_tuner_i2c(i2c, tda_state); tuner_msg.addr = tda_state->tuner_address; tuner_msg.len = 4; @@ -576,6 +803,8 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, } dvb_delay(1); tda1004x_disable_tuner_i2c(i2c, tda_state); + if (tda_state->fe_type == FE_TYPE_TDA10046H) + tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 4); break; default: @@ -593,13 +822,12 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c, struct dvb_frontend_parameters *fe_params) { int tmp; + int inversion; dprintk("%s\n", __FUNCTION__); - // set frequency - tmp = tda1004x_set_frequency(i2c, tda_state, fe_params); - if (tmp < 0) + if ((tmp = tda1004x_set_frequency(i2c, tda_state, fe_params)) < 0) return tmp; // hardcoded to use auto as much as possible @@ -673,14 +901,24 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c, } // set bandwidth - switch(tda_state->tda1004x_address) { - case TDA10045H_ADDRESS: + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: tda10045h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth); break; + + case FE_TYPE_TDA10046H: + tda10046h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth); + break; + } + + // need to invert the inversion for TT TDA10046H + inversion = fe_params->inversion; + if (tda_state->fe_type == FE_TYPE_TDA10046H) { + inversion = inversion ? INVERSION_OFF : INVERSION_ON; } // set inversion - switch (fe_params->inversion) { + switch (inversion) { case INVERSION_OFF: tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0); break; @@ -745,10 +983,19 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c, return -EINVAL; } - // reset DSP + // start the lock + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8); tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0); dvb_delay(10); + break; + + case FE_TYPE_TDA10046H: + tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x40, 0x40); + dvb_delay(10); + break; + } // done return 0; @@ -766,8 +1013,15 @@ static int tda1004x_get_fe(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_s fe_params->inversion = INVERSION_ON; } + // need to invert the inversion for TT TDA10046H + if (tda_state->fe_type == FE_TYPE_TDA10046H) { + fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; + } + // bandwidth - switch (tda1004x_read_byte(i2c, tda_state, TDA1004X_WREF_LSB)) { + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: + switch (tda1004x_read_byte(i2c, tda_state, TDA10045H_WREF_LSB)) { case 0x14: fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; break; @@ -778,6 +1032,22 @@ static int tda1004x_get_fe(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_s fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; break; } + break; + + case FE_TYPE_TDA10046H: + switch (tda1004x_read_byte(i2c, tda_state, TDA10046H_TIME_WREF1)) { + case 0x60: + fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + break; + case 0x6e: + fe_params->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + break; + case 0x80: + fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + break; + } + break; + } // FEC fe_params->u.ofdm.code_rate_HP = @@ -906,11 +1176,23 @@ static int tda1004x_read_status(struct dvb_i2c_bus *i2c, struct tda1004x_state* static int tda1004x_read_signal_strength(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, u16 * signal) { int tmp; + int reg = 0; dprintk("%s\n", __FUNCTION__); + // determine the register to use + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: + reg = TDA10045H_S_AGC; + break; + + case FE_TYPE_TDA10046H: + reg = TDA10046H_AGC_IF_LEVEL; + break; + } + // read it - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_SIGNAL_STRENGTH); + tmp = tda1004x_read_byte(i2c, tda_state, reg); if (tmp < 0) return -EIO; @@ -1009,10 +1291,14 @@ static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) switch (cmd) { case FE_GET_INFO: - switch(tda_state->tda1004x_address) { - case TDA10045H_ADDRESS: + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: memcpy(arg, &tda10045h_info, sizeof(struct dvb_frontend_info)); break; + + case FE_TYPE_TDA10046H: + memcpy(arg, &tda10046h_info, sizeof(struct dvb_frontend_info)); + break; } break; @@ -1043,7 +1329,15 @@ static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) return 0; // OK, perform initialisation - status = tda1004x_init(i2c, tda_state); + switch(tda_state->fe_type) { + case FE_TYPE_TDA10045H: + status = tda10045h_init(i2c, tda_state); + break; + + case FE_TYPE_TDA10046H: + status = tda10046h_init(i2c, tda_state); + break; + } if (status == 0) tda_state->initialised = 1; return status; @@ -1060,42 +1354,81 @@ static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data) { int tda1004x_address = -1; int tuner_address = -1; + int fe_type = -1; + int tuner_type = -1; struct tda1004x_state tda_state; struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=0, .len=0 }; static u8 td1344_init[] = { 0x0b, 0xf5, 0x88, 0xab }; - static u8 tdm1316l_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + static u8 td1316_init_tda10046h[] = { 0x0b, 0xf5, 0x80, 0xab }; + int status; dprintk("%s\n", __FUNCTION__); - // probe for frontend - tda_state.tda1004x_address = TDA10045H_ADDRESS; + // probe for tda10045h + if (tda1004x_address == -1) { + tda_state.tda1004x_address = 0x08; if (tda1004x_read_byte(i2c, &tda_state, TDA1004X_CHIPID) == 0x25) { - tda1004x_address = TDA10045H_ADDRESS; + tda1004x_address = 0x08; + fe_type = FE_TYPE_TDA10045H; printk("tda1004x: Detected Philips TDA10045H.\n"); } + } + + // probe for tda10046h + if (tda1004x_address == -1) { + tda_state.tda1004x_address = 0x08; + if (tda1004x_read_byte(i2c, &tda_state, TDA1004X_CHIPID) == 0x46) { + tda1004x_address = 0x08; + fe_type = FE_TYPE_TDA10046H; + printk("tda1004x: Detected Philips TDA10046H.\n"); + } + } // did we find a frontend? if (tda1004x_address == -1) { return -ENODEV; } - // supported tuner? + // enable access to the tuner tda1004x_enable_tuner_i2c(i2c, &tda_state); - tuner_msg.addr = TD1344_ADDRESS; + + // check for a TD1344 first + if (tuner_address == -1) { + tuner_msg.addr = 0x61; tuner_msg.buf = td1344_init; tuner_msg.len = sizeof(td1344_init); if (i2c->xfer(i2c, &tuner_msg, 1) == 1) { dvb_delay(1); - tuner_address = TD1344_ADDRESS; - printk("tda1004x: Detected Philips TD1344 tuner. PLEASE CHECK THIS AND REPORT BACK!.\n"); - } else { - tuner_msg.addr = TDM1316L_ADDRESS; - tuner_msg.buf = tdm1316l_init; - tuner_msg.len = sizeof(tdm1316l_init); + tuner_address = 0x61; + tuner_type = TUNER_TYPE_TD1344; + printk("tda1004x: Detected Philips TD1344 tuner.\n"); + } + } + + // OK, try a TD1316 on address 0x63 + if (tuner_address == -1) { + tuner_msg.addr = 0x63; + tuner_msg.buf = td1316_init; + tuner_msg.len = sizeof(td1316_init); if (i2c->xfer(i2c, &tuner_msg, 1) == 1) { dvb_delay(1); - tuner_address = TDM1316L_ADDRESS; - printk("tda1004x: Detected Philips TDM1316L tuner.\n"); + tuner_address = 0x63; + tuner_type = TUNER_TYPE_TD1316; + printk("tda1004x: Detected Philips TD1316 tuner.\n"); + } + } + + // OK, TD1316 again, on address 0x60 (TDA10046H) + if (tuner_address == -1) { + tuner_msg.addr = 0x60; + tuner_msg.buf = td1316_init_tda10046h; + tuner_msg.len = sizeof(td1316_init_tda10046h); + if (i2c->xfer(i2c, &tuner_msg, 1) == 1) { + dvb_delay(1); + tuner_address = 0x60; + tuner_type = TUNER_TYPE_TD1316; + printk("tda1004x: Detected Philips TD1316 tuner.\n"); } } tda1004x_disable_tuner_i2c(i2c, &tda_state); @@ -1108,16 +1441,25 @@ static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data) // create state tda_state.tda1004x_address = tda1004x_address; + tda_state.fe_type = fe_type; tda_state.tuner_address = tuner_address; + tda_state.tuner_type = tuner_type; tda_state.initialised = 0; + // upload firmware + if ((status = tda1004x_fwupload(i2c, &tda_state)) != 0) return status; + // register - switch(tda_state.tda1004x_address) { - case TDA10045H_ADDRESS: + switch(tda_state.fe_type) { + case FE_TYPE_TDA10045H: return dvb_register_frontend(tda1004x_ioctl, i2c, (void *)(*((u32*) &tda_state)), &tda10045h_info); - default: - return -ENODEV; + + case FE_TYPE_TDA10046H: + return dvb_register_frontend(tda1004x_ioctl, i2c, (void *)(*((u32*) &tda_state)), &tda10046h_info); } + + // should not get here + return -EINVAL; } @@ -1146,7 +1488,7 @@ void __exit exit_tda1004x(void) module_init(init_tda1004x); module_exit(exit_tda1004x); -MODULE_DESCRIPTION("Philips TDA10045H DVB-T Frontend"); +MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Frontend"); MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c index bdd84c055a88..1652be9d3167 100644 --- a/drivers/media/dvb/frontends/ves1820.c +++ b/drivers/media/dvb/frontends/ves1820.c @@ -120,7 +120,7 @@ static struct dvb_frontend_info ves1820_info = { static u8 ves1820_inittab [] = { 0x69, 0x6A, 0x9B, 0x12, 0x12, 0x46, 0x26, 0x1A, - 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x28, + 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20, 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -380,7 +380,7 @@ static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) sync = ves1820_readreg (fe, 0x11); - if (sync & 2) + if (sync & 1) *status |= FE_HAS_SIGNAL; if (sync & 2) @@ -440,13 +440,14 @@ static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) s8 afc = 0; sync = ves1820_readreg (fe, 0x11); - if (sync & 2) - /* AFC only valid when carrier has been recovered */ afc = ves1820_readreg(fe, 0x19); - if (verbose) - printk ("DVB: VES1820(%d): AFC (%d) %dHz\n", + if (verbose) { + /* AFC only valid when carrier has been recovered */ + printk(sync & 2 ? "DVB: VES1820(%d): AFC (%d) %dHz\n" : + "DVB: VES1820(%d): [AFC (%d) %dHz]\n", fe->i2c->adapter->num, afc, - -((s32)(p->u.qam.symbol_rate >> 3) * afc >> 7)); + -((s32)p->u.qam.symbol_rate * afc) >> 10); + } p->inversion = HAS_INVERSION(reg0) ? INVERSION_ON : INVERSION_OFF; p->u.qam.modulation = ((reg0 >> 2) & 7) + QAM_16; @@ -454,9 +455,8 @@ static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) p->u.qam.fec_inner = FEC_NONE; p->frequency = ((p->frequency + 31250) / 62500) * 62500; - /* To prevent overflow, shift symbol rate first a - couple of bits. */ - p->frequency -= (s32)(p->u.qam.symbol_rate >> 3) * afc >> 7; + if (sync & 2) + p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10; break; } case FE_SLEEP: diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 64930ebe9fe7..fcd7f125c668 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -105,6 +105,7 @@ static void arm_error(struct av7110 *av7110) static int arm_thread(void *data) { struct av7110 *av7110 = data; + unsigned long timeout; u16 newloops = 0; DEB_EE(("av7110: %p\n",av7110)); @@ -112,8 +113,12 @@ static int arm_thread(void *data) dvb_kernel_thread_setup ("arm_mon"); av7110->arm_thread = current; - while (!av7110->arm_rmmod && !signal_pending(current)) { - interruptible_sleep_on_timeout(&av7110->arm_wait, 5*HZ); + while (1) { + timeout = wait_event_interruptible_timeout(av7110->arm_wait,0 != av7110->arm_rmmod, 5*HZ); + if (-ERESTARTSYS == timeout || 0 != av7110->arm_rmmod) { + /* got signal or told to quit*/ + break; + } if (!av7110->arm_ready) continue; @@ -1283,7 +1288,7 @@ static int check_firmware(struct av7110* av7110) return -EINVAL; } if( crc != crc32_le(0,ptr,len)) { - printk("dvb-ttpci: crc32 of dpram file does not match.\n"); + printk("dvb-ttpci: crc32 of root file does not match.\n"); return -EINVAL; } av7110->bin_root = ptr; @@ -1426,7 +1431,10 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d /* load firmware into AV7110 cards */ av7110_bootarm(av7110); - av7110_firmversion(av7110); + if (av7110_firmversion(av7110)) { + ret = -EIO; + goto err2; + } if (FW_VERSION(av7110->arm_app)<0x2501) printk ("av7110: Warning, firmware version 0x%04x is too old. " @@ -1497,6 +1505,9 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d av7110_num++; return 0; +err2: + av7110_ca_exit(av7110); + av7110_av_exit(av7110); err: if (NULL != av7110 ) { kfree(av7110); diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index bd8eca1f7e4d..e1a6e76b72e6 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -249,7 +249,11 @@ int av7110_bootarm(struct av7110 *av7110) mwdebi(av7110, DEBISWAB, DPRAM_BASE, bootcode, sizeof(bootcode)); iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - saa7146_wait_for_debi_done(av7110->dev); + if (saa7146_wait_for_debi_done(av7110->dev)) { + printk(KERN_ERR "dvb: av7110_bootarm(): " + "saa7146_wait_for_debi_done() timed out\n"); + return -1; + } saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); //FIXME: necessary? set_current_state(TASK_INTERRUPTIBLE); @@ -265,7 +269,11 @@ int av7110_bootarm(struct av7110 *av7110) DEB_D(("av7110_bootarm: load dpram code\n")); mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); - saa7146_wait_for_debi_done(av7110->dev); + if (saa7146_wait_for_debi_done(av7110->dev)) { + printk(KERN_ERR "dvb: av7110_bootarm(): " + "saa7146_wait_for_debi_done() timed out after loading DRAM\n"); + return -1; + } saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); //FIXME: necessary? mdelay(800); @@ -515,14 +523,18 @@ int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length) ****************************************************************************/ /* get version of the firmware ROM, RTSL, video ucode and ARM application */ -void av7110_firmversion(struct av7110 *av7110) +int av7110_firmversion(struct av7110 *av7110) { u16 buf[20]; u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion); DEB_EE(("av7110: %p\n", av7110)); - av7110_fw_query(av7110, tag, buf, 16); + if (av7110_fw_query(av7110, tag, buf, 16)) { + printk("DVB: AV7110-%d: ERROR: Failed to boot firmware\n", + av7110->dvb_adapter->num); + return -EIO; + } av7110->arm_fw = (buf[0] << 16) + buf[1]; av7110->arm_rtsl = (buf[2] << 16) + buf[3]; @@ -542,7 +554,7 @@ void av7110_firmversion(struct av7110 *av7110) printk("DVB: AV711%d(%d) - no firmware support for CI link layer interface\n", av7110->avtype, av7110->dvb_adapter->num); - return; + return 0; } diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h index c0f539b93800..aa674b84e435 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.h +++ b/drivers/media/dvb/ttpci/av7110_hw.h @@ -362,7 +362,7 @@ enum av7110_command_type { extern void av7110_reset_arm(struct av7110 *av7110); extern int av7110_bootarm(struct av7110 *av7110); -extern void av7110_firmversion(struct av7110 *av7110); +extern int av7110_firmversion(struct av7110 *av7110); #define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) #define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index 2dbc1bbeff4a..e1d3901e5dc6 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -191,7 +191,7 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh) if (0 != av7110->current_input) { adswitch = 1; - band = 0x68; /* analog band */ + band = 0x60; /* analog band */ source = SAA7146_HPS_SOURCE_PORT_B; sync = SAA7146_HPS_SYNC_PORT_B; memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); @@ -204,7 +204,7 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh) msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume } else { adswitch = 0; - band = 0x28; /* digital band */ + band = 0x20; /* digital band */ source = SAA7146_HPS_SOURCE_PORT_A; sync = SAA7146_HPS_SYNC_PORT_A; memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); @@ -638,7 +638,7 @@ static struct saa7146_standard standard[] = { static struct saa7146_standard analog_standard[] = { { .name = "PAL", .id = V4L2_STD_PAL_BG, - .v_offset = 0x18 /* 0 */ , .v_field = 288, .v_calc = 576, + .v_offset = 0x1b, .v_field = 288, .v_calc = 576, .h_offset = 0x08, .h_pixels = 708, .h_calc = 709, .v_max_out = 576, .h_max_out = 768, }, { diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index b2dbacbd8315..1b0fc3e82389 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -68,8 +68,8 @@ static int i2c_readregs(struct dvb_i2c_bus *i2c, u8 id, u8 reg, u8 *buf, u8 len) { u8 mm1[] = { reg }; struct i2c_msg msgs[2] = { - { addr: id/2, flags: 0, buf: mm1, len: 1 }, - { addr: id/2, flags: I2C_M_RD, buf: buf, len: len } + { .addr = id/2, .flags = 0, .buf = mm1, .len = 1 }, + { .addr = id/2, .flags = I2C_M_RD, .buf = buf, .len = len } }; if (i2c->xfer(i2c, msgs, 2) != 2) diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 96d89e4ed7a7..0ebc29f4e52a 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -223,6 +223,9 @@ static int ttusb_i2c_msg(struct ttusb *ttusb, err = ttusb_result(ttusb, b, 0x20); + /* check if the i2c transaction was successful */ + if ((snd_len != b[5]) || (rcv_len != b[6])) return -EREMOTEIO; + if (rcv_len > 0) { if (err || b[0] != 0x55 || b[1] != id) { @@ -273,7 +276,7 @@ static int ttusb_i2c_xfer(struct dvb_i2c_bus *i2c, const struct i2c_msg msg[], snd_buf, snd_len, rcv_buf, rcv_len); if (err < rcv_len) { - printk("%s: i == %i\n", __FUNCTION__, i); + dprintk("%s: i == %i\n", __FUNCTION__, i); break; } @@ -432,7 +435,8 @@ static int ttusb_init_controller(struct ttusb *ttusb) get_version[7], get_version[8]); if (memcmp(get_version + 4, "V 0.0", 5) && - memcmp(get_version + 4, "V 1.1", 5)) { + memcmp(get_version + 4, "V 1.1", 5) && + memcmp(get_version + 4, "V 2.1", 5)) { printk ("%s: unknown STC version %c%c%c%c%c, please report!\n", __FUNCTION__, get_version[4], get_version[5], @@ -932,7 +936,7 @@ static int ttusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) struct ttusb *ttusb = (struct ttusb *) dvbdmxfeed->demux; struct ttusb_channel *channel; - printk("ttusb_start_feed\n"); + dprintk("ttusb_start_feed\n"); switch (dvbdmxfeed->type) { case DMX_TYPE_TS: @@ -1004,7 +1008,7 @@ static int ttusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) static int ttusb_setup_interfaces(struct ttusb *ttusb) { - usb_reset_configuration(ttusb->dev); + usb_set_configuration(ttusb->dev, 1); usb_set_interface(ttusb->dev, 1, 1); ttusb->bulk_out_pipe = usb_sndbulkpipe(ttusb->dev, 1); @@ -1186,7 +1190,7 @@ static void ttusb_disconnect(struct usb_interface *intf) static struct usb_device_id ttusb_table[] = { {USB_DEVICE(0xb48, 0x1003)}, {USB_DEVICE(0xb48, 0x1004)}, /* to be confirmed ???? */ - {USB_DEVICE(0xb48, 0x1005)}, /* to be confirmed ???? */ + {USB_DEVICE(0xb48, 0x1005)}, {} }; diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h b/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h index 17a91e1f6880..287645d8a9e6 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h @@ -1,7 +1,7 @@ #include <asm/types.h> -u8 dsp_bootcode [] __initdata = { +u8 dsp_bootcode [] = { 0x08, 0xaa, 0x00, 0x18, 0x00, 0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x80, 0x18, 0x5f, 0x00, 0x00, 0x01, 0x80, 0x77, 0x18, 0x2a, 0xeb, diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 40efcf8e2e27..bc3af9202d0a 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -111,6 +111,16 @@ config VIDEO_CPIA_USB otherwise say N. This will not work with the Creative Webcam III. It is also available as a module (cpia_usb). +config VIDEO_SAA5246A + tristate "SAA5246A Teletext processor" + depends on VIDEO_DEV && I2C + help + Support for I2C bus based teletext using the SAA5246A chip. Useful + only if you live in Europe. + + To compile this driver as a module, choose M here: the + module will be called saa5246a. + config VIDEO_SAA5249 tristate "SAA5249 Teletext processor" depends on VIDEO_DEV && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 5139bbae3051..56f2e7c4a3f4 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \ obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_ZR36120) += zoran.o +obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c new file mode 100644 index 000000000000..715a7cc5fed3 --- /dev/null +++ b/drivers/media/video/saa5246a.c @@ -0,0 +1,800 @@ +/* + * Driver for the SAA5246A videotext decoder chip from Philips. + * + * Only capturing of videotext pages is tested. The SAA5246A chip also has + * a TV output but my hardware doesn't use it. For this reason this driver + * does not support changing any TV display settings. + * + * Copyright (C) 2004 Michael Geng <linux@MichaelGeng.de> + * + * Derived from + * + * saa5249 driver + * Copyright (C) 1998 Richard Guenther + * <richard.guenther@student.uni-tuebingen.de> + * + * with changes by + * Alan Cox <Alan.Cox@linux.org> + * + * and + * + * vtx.c + * Copyright (C) 1994-97 Martin Buck <martin-2.buck@student.uni-ulm.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videotext.h> +#include <linux/videodev.h> +#include "saa5246a.h" + +struct saa5246a_device +{ + u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; + int is_searching[NUM_DAUS]; + struct i2c_client *client; + struct semaphore lock; +}; + +static struct video_device saa_template; /* Declared near bottom */ + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +static struct i2c_client client_template; + +static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind) +{ + int pgbuf; + int err; + struct i2c_client *client; + struct video_device *vd; + struct saa5246a_device *t; + + printk(KERN_INFO "saa5246a: teletext chip found.\n"); + client=kmalloc(sizeof(*client), GFP_KERNEL); + if(client==NULL) + return -ENOMEM; + client_template.adapter = adap; + client_template.addr = addr; + memcpy(client, &client_template, sizeof(*client)); + t = kmalloc(sizeof(*t), GFP_KERNEL); + if(t==NULL) + { + kfree(client); + return -ENOMEM; + } + memset(t, 0, sizeof(*t)); + strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); + init_MUTEX(&t->lock); + + /* + * Now create a video4linux device + */ + + vd = video_device_alloc(); + if(vd==NULL) + { + kfree(t); + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, vd); + memcpy(vd, &saa_template, sizeof(*vd)); + + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) + { + memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); + t->is_searching[pgbuf] = FALSE; + } + vd->priv=t; + + + /* + * Register it + */ + + if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) + { + kfree(t); + kfree(client); + video_device_release(vd); + return err; + } + t->client = client; + i2c_attach_client(client); + return 0; +} + +/* + * We do most of the hard work when we become a device on the i2c. + */ +static int saa5246a_probe(struct i2c_adapter *adap) +{ + if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) + return i2c_probe(adap, &addr_data, saa5246a_attach); + return 0; +} + +static int saa5246a_detach(struct i2c_client *client) +{ + struct video_device *vd = i2c_get_clientdata(client); + i2c_detach_client(client); + video_unregister_device(vd); + kfree(vd->priv); + kfree(client); + return 0; +} + +static int saa5246a_command(struct i2c_client *device, unsigned int cmd, + void *arg) +{ + return -EINVAL; +} + +/* + * I2C interfaces + */ + +static struct i2c_driver i2c_driver_videotext = +{ + .owner = THIS_MODULE, + .name = IF_NAME, /* name */ + .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ + .flags = I2C_DF_NOTIFY, + .attach_adapter = saa5246a_probe, + .detach_client = saa5246a_detach, + .command = saa5246a_command +}; + +static struct i2c_client client_template = { + .id = -1, + .driver = &i2c_driver_videotext, + .name = "(unset)", +}; + +static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) +{ + char buf[64]; + + buf[0] = reg; + memcpy(buf+1, data, count); + + if(i2c_master_send(t->client, buf, count+1)==count+1) + return 0; + return -1; +} + +static int i2c_senddata(struct saa5246a_device *t, ...) +{ + unsigned char buf[64]; + int v; + int ct=0; + va_list argp; + va_start(argp,t); + + while((v=va_arg(argp,int))!=-1) + buf[ct++]=v; + return i2c_sendbuf(t, buf[0], ct-1, buf+1); +} + +/* Get count number of bytes from I²C-device at address adr, store them in buf. + * Start & stop handshaking is done by this routine, ack will be sent after the + * last byte to inhibit further sending of data. If uaccess is TRUE, data is + * written to user-space with put_user. Returns -1 if I²C-device didn't send + * acknowledge, 0 otherwise + */ +static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf) +{ + if(i2c_master_recv(t->client, buf, count)!=count) + return -1; + return 0; +} + +/* When a page is found then the not FOUND bit in one of the status registers + * of the SAA5264A chip is cleared. Unfortunately this bit is not set + * automatically when a new page is requested. Instead this function must be + * called after a page has been requested. + * + * Return value: 0 if successful + */ +static int saa5246a_clear_found_bit(struct saa5246a_device *t, + unsigned char dau_no) +{ + unsigned char row_25_column_8; + + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + dau_no | + R8_DO_NOT_CLEAR_MEMORY, + + R9_CURSER_ROW_25, + + R10_CURSER_COLUMN_8, + + COMMAND_END) || + i2c_getdata(t, 1, &row_25_column_8)) + { + return -EIO; + } + row_25_column_8 |= ROW25_COLUMN8_PAGE_NOT_FOUND; + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + dau_no | + R8_DO_NOT_CLEAR_MEMORY, + + R9_CURSER_ROW_25, + + R10_CURSER_COLUMN_8, + + row_25_column_8, + + COMMAND_END)) + { + return -EIO; + } + + return 0; +} + +/* Requests one videotext page as described in req. The fields of req are + * checked and an error is returned if something is invalid. + * + * Return value: 0 if successful + */ +static int saa5246a_request_page(struct saa5246a_device *t, + vtx_pagereq_t *req) +{ + if (req->pagemask < 0 || req->pagemask >= PGMASK_MAX) + return -EINVAL; + if (req->pagemask & PGMASK_PAGE) + if (req->page < 0 || req->page > PAGE_MAX) + return -EINVAL; + if (req->pagemask & PGMASK_HOUR) + if (req->hour < 0 || req->hour > HOUR_MAX) + return -EINVAL; + if (req->pagemask & PGMASK_MINUTE) + if (req->minute < 0 || req->minute > MINUTE_MAX) + return -EINVAL; + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + + if (i2c_senddata(t, SAA5246A_REGISTER_R2, + + R2_IN_R3_SELECT_PAGE_HUNDREDS | + req->pgbuf << 4 | + R2_BANK_0 | + R2_HAMMING_CHECK_OFF, + + HUNDREDS_OF_PAGE(req->page) | + R3_HOLD_PAGE | + (req->pagemask & PG_HUND ? + R3_PAGE_HUNDREDS_DO_CARE : + R3_PAGE_HUNDREDS_DO_NOT_CARE), + + TENS_OF_PAGE(req->page) | + (req->pagemask & PG_TEN ? + R3_PAGE_TENS_DO_CARE : + R3_PAGE_TENS_DO_NOT_CARE), + + UNITS_OF_PAGE(req->page) | + (req->pagemask & PG_UNIT ? + R3_PAGE_UNITS_DO_CARE : + R3_PAGE_UNITS_DO_NOT_CARE), + + TENS_OF_HOUR(req->hour) | + (req->pagemask & HR_TEN ? + R3_HOURS_TENS_DO_CARE : + R3_HOURS_TENS_DO_NOT_CARE), + + UNITS_OF_HOUR(req->hour) | + (req->pagemask & HR_UNIT ? + R3_HOURS_UNITS_DO_CARE : + R3_HOURS_UNITS_DO_NOT_CARE), + + TENS_OF_MINUTE(req->minute) | + (req->pagemask & MIN_TEN ? + R3_MINUTES_TENS_DO_CARE : + R3_MINUTES_TENS_DO_NOT_CARE), + + UNITS_OF_MINUTE(req->minute) | + (req->pagemask & MIN_UNIT ? + R3_MINUTES_UNITS_DO_CARE : + R3_MINUTES_UNITS_DO_NOT_CARE), + + COMMAND_END) || i2c_senddata(t, SAA5246A_REGISTER_R2, + + R2_IN_R3_SELECT_PAGE_HUNDREDS | + req->pgbuf << 4 | + R2_BANK_0 | + R2_HAMMING_CHECK_OFF, + + HUNDREDS_OF_PAGE(req->page) | + R3_UPDATE_PAGE | + (req->pagemask & PG_HUND ? + R3_PAGE_HUNDREDS_DO_CARE : + R3_PAGE_HUNDREDS_DO_NOT_CARE), + + COMMAND_END)) + { + return -EIO; + } + + t->is_searching[req->pgbuf] = TRUE; + return 0; +} + +/* This routine decodes the page number from the infobits contained in line 25. + * + * Parameters: + * infobits: must be bits 0 to 9 of column 25 + * + * Return value: page number coded in hexadecimal, i. e. page 123 is coded 0x123 + */ +static inline int saa5246a_extract_pagenum_from_infobits( + unsigned char infobits[10]) +{ + int page_hundreds, page_tens, page_units; + + page_units = infobits[0] & ROW25_COLUMN0_PAGE_UNITS; + page_tens = infobits[1] & ROW25_COLUMN1_PAGE_TENS; + page_hundreds = infobits[8] & ROW25_COLUMN8_PAGE_HUNDREDS; + + /* page 0x.. means page 8.. */ + if (page_hundreds == 0) + page_hundreds = 8; + + return((page_hundreds << 8) | (page_tens << 4) | page_units); +} + +/* Decodes the hour from the infobits contained in line 25. + * + * Parameters: + * infobits: must be bits 0 to 9 of column 25 + * + * Return: hour coded in hexadecimal, i. e. 12h is coded 0x12 + */ +static inline int saa5246a_extract_hour_from_infobits( + unsigned char infobits[10]) +{ + int hour_tens, hour_units; + + hour_units = infobits[4] & ROW25_COLUMN4_HOUR_UNITS; + hour_tens = infobits[5] & ROW25_COLUMN5_HOUR_TENS; + + return((hour_tens << 4) | hour_units); +} + +/* Decodes the minutes from the infobits contained in line 25. + * + * Parameters: + * infobits: must be bits 0 to 9 of column 25 + * + * Return: minutes coded in hexadecimal, i. e. 10min is coded 0x10 + */ +static inline int saa5246a_extract_minutes_from_infobits( + unsigned char infobits[10]) +{ + int minutes_tens, minutes_units; + + minutes_units = infobits[2] & ROW25_COLUMN2_MINUTES_UNITS; + minutes_tens = infobits[3] & ROW25_COLUMN3_MINUTES_TENS; + + return((minutes_tens << 4) | minutes_units); +} + +/* Reads the status bits contained in the first 10 columns of the first line + * and extracts the information into info. + * + * Return value: 0 if successful + */ +static inline int saa5246a_get_status(struct saa5246a_device *t, + vtx_pageinfo_t *info, unsigned char dau_no) +{ + unsigned char infobits[10]; + int column; + + if (dau_no >= NUM_DAUS) + return -EINVAL; + + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + dau_no | + R8_DO_NOT_CLEAR_MEMORY, + + R9_CURSER_ROW_25, + + R10_CURSER_COLUMN_0, + + COMMAND_END) || + i2c_getdata(t, 10, infobits)) + { + return -EIO; + } + + info->pagenum = saa5246a_extract_pagenum_from_infobits(infobits); + info->hour = saa5246a_extract_hour_from_infobits(infobits); + info->minute = saa5246a_extract_minutes_from_infobits(infobits); + info->charset = ((infobits[7] & ROW25_COLUMN7_CHARACTER_SET) >> 1); + info->delete = !!(infobits[3] & ROW25_COLUMN3_DELETE_PAGE); + info->headline = !!(infobits[5] & ROW25_COLUMN5_INSERT_HEADLINE); + info->subtitle = !!(infobits[5] & ROW25_COLUMN5_INSERT_SUBTITLE); + info->supp_header = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_HEADER); + info->update = !!(infobits[6] & ROW25_COLUMN6_UPDATE_PAGE); + info->inter_seq = !!(infobits[6] & ROW25_COLUMN6_INTERRUPTED_SEQUENCE); + info->dis_disp = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_DISPLAY); + info->serial = !!(infobits[7] & ROW25_COLUMN7_SERIAL_MODE); + info->notfound = !!(infobits[8] & ROW25_COLUMN8_PAGE_NOT_FOUND); + info->pblf = !!(infobits[9] & ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR); + info->hamming = 0; + for (column = 0; column <= 7; column++) { + if (infobits[column] & ROW25_COLUMN0_TO_7_HAMMING_ERROR) { + info->hamming = 1; + break; + } + } + if (!info->hamming && !info->notfound) + t->is_searching[dau_no] = FALSE; + return 0; +} + +/* Reads 1 videotext page buffer of the SAA5246A. + * + * req is used both as input and as output. It contains information which part + * must be read. The videotext page is copied into req->buffer. + * + * Return value: 0 if successful + */ +static inline int saa5246a_get_page(struct saa5246a_device *t, + vtx_pagereq_t *req) +{ + int start, end; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || + req->start < 0 || req->start > req->end || req->end >= VTX_PAGESIZE) + return -EINVAL; + /* Read "normal" part of page */ + end = min(req->end, VTX_PAGESIZE - 1); + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + req->pgbuf | + R8_DO_NOT_CLEAR_MEMORY, + + ROW(req->start), + + COLUMN(req->start), + + COMMAND_END) || + i2c_getdata(t, end - req->start + 1, req->buffer)) + { + return -EIO; + } + /* Always get the time from buffer 4, since this stupid SAA5246A only + * updates the currently displayed buffer... + */ + if (REQ_CONTAINS_TIME(req)) + { + start = max(req->start, POS_TIME_START); + end = min(req->end, POS_TIME_END); + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + R8_ACTIVE_CHAPTER_4 | + R8_DO_NOT_CLEAR_MEMORY, + + R9_CURSER_ROW_0, + + start, + + COMMAND_END) || + i2c_getdata(t, end - start + 1, + req->buffer + start - req->start)) + { + return -EIO; + } + } + /* Insert the header from buffer 4 only, if acquisition circuit is still searching for a page */ + if (REQ_CONTAINS_HEADER(req) && t->is_searching[req->pgbuf]) + { + start = max(req->start, POS_HEADER_START); + end = min(req->end, POS_HEADER_END); + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + R8_ACTIVE_CHAPTER_4 | + R8_DO_NOT_CLEAR_MEMORY, + + R9_CURSER_ROW_0, + + start, + + COMMAND_END) || + i2c_getdata(t, end - start + 1, + req->buffer + start - req->start)) + { + return -EIO; + } + } + + return 0; +} + +/* Stops the acquisition circuit given in dau_no. The page buffer associated + * with this acquisition circuit will no more be updated. The other daus are + * not affected. + * + * Return value: 0 if successful + */ +static inline int saa5246a_stop_dau(struct saa5246a_device *t, + unsigned char dau_no) +{ + if (dau_no >= NUM_DAUS) + return -EINVAL; + if (i2c_senddata(t, SAA5246A_REGISTER_R2, + + R2_IN_R3_SELECT_PAGE_HUNDREDS | + dau_no << 4 | + R2_BANK_0 | + R2_HAMMING_CHECK_OFF, + + R3_PAGE_HUNDREDS_0 | + R3_HOLD_PAGE | + R3_PAGE_HUNDREDS_DO_NOT_CARE, + + COMMAND_END)) + { + return -EIO; + } + t->is_searching[dau_no] = FALSE; + return 0; +} + +/* Handles ioctls defined in videotext.h + * + * Returns 0 if successful + */ +static int do_saa5246a_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vd = video_devdata(file); + struct saa5246a_device *t=vd->priv; + switch(cmd) + { + case VTXIOCGETINFO: + { + vtx_info_t *info = arg; + + info->version_major = MAJOR_VERSION; + info->version_minor = MINOR_VERSION; + info->numpages = NUM_DAUS; + return 0; + } + + case VTXIOCCLRPAGE: + { + vtx_pagereq_t *req = arg; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + memset(t->pgbuf[req->pgbuf], ' ', sizeof(t->pgbuf[0])); + return 0; + } + + case VTXIOCCLRFOUND: + { + vtx_pagereq_t *req = arg; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + return(saa5246a_clear_found_bit(t, req->pgbuf)); + } + + case VTXIOCPAGEREQ: + { + vtx_pagereq_t *req = arg; + + return(saa5246a_request_page(t, req)); + } + + case VTXIOCGETSTAT: + { + vtx_pagereq_t *req = arg; + vtx_pageinfo_t info; + int rval; + + if ((rval = saa5246a_get_status(t, &info, req->pgbuf))) + return rval; + if(copy_to_user(req->buffer, &info, + sizeof(vtx_pageinfo_t))) + return -EFAULT; + return 0; + } + + case VTXIOCGETPAGE: + { + vtx_pagereq_t *req = arg; + + return(saa5246a_get_page(t, req)); + } + + case VTXIOCSTOPDAU: + { + vtx_pagereq_t *req = arg; + + return(saa5246a_stop_dau(t, req->pgbuf)); + } + + case VTXIOCPUTPAGE: + case VTXIOCSETDISP: + case VTXIOCPUTSTAT: + return 0; + + case VTXIOCCLRCACHE: + { + return 0; + } + + case VTXIOCSETVIRT: + { + /* I do not know what "virtual mode" means */ + return 0; + } + } + return -EINVAL; +} + +/* + * Handle the locking + */ +static int saa5246a_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vd = video_devdata(file); + struct saa5246a_device *t = vd->priv; + int err; + + down(&t->lock); + err = video_usercopy(inode, file, cmd, arg, do_saa5246a_ioctl); + up(&t->lock); + return err; +} + +static int saa5246a_open(struct inode *inode, struct file *file) +{ + struct video_device *vd = video_devdata(file); + struct saa5246a_device *t = vd->priv; + int err; + + err = video_exclusive_open(inode,file); + if (err < 0) + return err; + + if (t->client==NULL) { + err = -ENODEV; + goto fail; + } + + if (i2c_senddata(t, SAA5246A_REGISTER_R0, + + R0_SELECT_R11 | + R0_PLL_TIME_CONSTANT_LONG | + R0_ENABLE_nODD_EVEN_OUTPUT | + R0_ENABLE_HDR_POLL | + R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED | + R0_NO_FREE_RUN_PLL | + R0_NO_AUTOMATIC_FASTEXT_PROMPT, + + R1_NON_INTERLACED_312_312_LINES | + R1_DEW | + R1_EXTENDED_PACKET_DISABLE | + R1_DAUS_ALL_ON | + R1_8_BITS_NO_PARITY | + R1_VCS_TO_SCS, + + COMMAND_END) || + i2c_senddata(t, SAA5246A_REGISTER_R4, + + /* We do not care much for the TV display but nevertheless we + * need the currently displayed page later because only on that + * page the time is updated. */ + R4_DISPLAY_PAGE_4, + + COMMAND_END)) + { + err = -EIO; + goto fail; + } + + return 0; + +fail: + video_exclusive_release(inode,file); + return err; +} + +static int saa5246a_release(struct inode *inode, struct file *file) +{ + struct video_device *vd = video_devdata(file); + struct saa5246a_device *t = vd->priv; + + /* Stop all acquisition circuits. */ + i2c_senddata(t, SAA5246A_REGISTER_R1, + + R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES | + R1_DEW | + R1_EXTENDED_PACKET_DISABLE | + R1_DAUS_ALL_OFF | + R1_8_BITS_NO_PARITY | + R1_VCS_TO_SCS, + + COMMAND_END); + video_exclusive_release(inode,file); + return 0; +} + +static int __init init_saa_5246a (void) +{ + printk(KERN_INFO "SAA5246A driver (" IF_NAME + " interface) for VideoText version %d.%d\n", + MAJOR_VERSION, MINOR_VERSION); + return i2c_add_driver(&i2c_driver_videotext); +} + +static void __exit cleanup_saa_5246a (void) +{ + i2c_del_driver(&i2c_driver_videotext); +} + +module_init(init_saa_5246a); +module_exit(cleanup_saa_5246a); + +static struct file_operations saa_fops = { + .owner = THIS_MODULE, + .open = saa5246a_open, + .release = saa5246a_release, + .ioctl = saa5246a_ioctl, + .llseek = no_llseek, +}; + +static struct video_device saa_template = +{ + .owner = THIS_MODULE, + .name = IF_NAME, + .type = VID_TYPE_TELETEXT, + .hardware = VID_HARDWARE_SAA5249, + .fops = &saa_fops, + .release = video_device_release, + .minor = -1, +}; + +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/saa5246a.h b/drivers/media/video/saa5246a.h new file mode 100644 index 000000000000..e8f1adf9a5fd --- /dev/null +++ b/drivers/media/video/saa5246a.h @@ -0,0 +1,362 @@ +/* + Driver for the SAA5246A videotext decoder chip from Philips. + Copyright (C) 2004 Michael Geng (linux@MichaelGeng.de) + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ +#ifndef __SAA5246A_H__ +#define __SAA5246A_H__ + +#define MAJOR_VERSION 1 /* driver major version number */ +#define MINOR_VERSION 6 /* driver minor version number */ + +#define IF_NAME "SAA5246A" + +#define I2C_ADDRESS 17 + +/* Number of DAUs = number of pages that can be searched at the same time. */ +#define NUM_DAUS 4 + +#define NUM_ROWS_PER_PAGE 40 + +/* first column is 0 (not 1) */ +#define POS_TIME_START 32 +#define POS_TIME_END 39 + +#define POS_HEADER_START 7 +#define POS_HEADER_END 31 + +/* Returns TRUE if the part of the videotext page described with req contains + (at least parts of) the time field */ +#define REQ_CONTAINS_TIME(p_req) \ + ((p_req)->start <= POS_TIME_END && \ + (p_req)->end >= POS_TIME_START) + +/* Returns TRUE if the part of the videotext page described with req contains + (at least parts of) the page header */ +#define REQ_CONTAINS_HEADER(p_req) \ + ((p_req)->start <= POS_HEADER_END && \ + (p_req)->end >= POS_HEADER_START) + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/*****************************************************************************/ +/* Mode register numbers of the SAA5246A */ +/*****************************************************************************/ +#define SAA5246A_REGISTER_R0 0 +#define SAA5246A_REGISTER_R1 1 +#define SAA5246A_REGISTER_R2 2 +#define SAA5246A_REGISTER_R3 3 +#define SAA5246A_REGISTER_R4 4 +#define SAA5246A_REGISTER_R5 5 +#define SAA5246A_REGISTER_R6 6 +#define SAA5246A_REGISTER_R7 7 +#define SAA5246A_REGISTER_R8 8 +#define SAA5246A_REGISTER_R9 9 +#define SAA5246A_REGISTER_R10 10 +#define SAA5246A_REGISTER_R11 11 +#define SAA5246A_REGISTER_R11B 11 + +/* SAA5246A mode registers often autoincrement to the next register. + Therefore we use variable argument lists. The following macro indicates + the end of a command list. */ +#define COMMAND_END (- 1) + +/*****************************************************************************/ +/* Contents of the mode registers of the SAA5246A */ +/*****************************************************************************/ +/* Register R0 (Advanced Control) */ +#define R0_SELECT_R11 0x00 +#define R0_SELECT_R11B 0x01 + +#define R0_PLL_TIME_CONSTANT_LONG 0x00 +#define R0_PLL_TIME_CONSTANT_SHORT 0x02 + +#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00 +#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04 + +#define R0_ENABLE_HDR_POLL 0x00 +#define R0_DISABLE_HDR_POLL 0x10 + +#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00 +#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20 + +#define R0_NO_FREE_RUN_PLL 0x00 +#define R0_FREE_RUN_PLL 0x40 + +#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00 +#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80 + +/* Register R1 (Mode) */ +#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00 +#define R1_NON_INTERLACED_312_313_LINES 0x01 +#define R1_NON_INTERLACED_312_312_LINES 0x02 +#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03 +#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07 + +#define R1_DEW 0x00 +#define R1_FULL_FIELD 0x08 + +#define R1_EXTENDED_PACKET_DISABLE 0x00 +#define R1_EXTENDED_PACKET_ENABLE 0x10 + +#define R1_DAUS_ALL_ON 0x00 +#define R1_DAUS_ALL_OFF 0x20 + +#define R1_7_BITS_PLUS_PARITY 0x00 +#define R1_8_BITS_NO_PARITY 0x40 + +#define R1_VCS_TO_SCS 0x00 +#define R1_NO_VCS_TO_SCS 0x80 + +/* Register R2 (Page request address) */ +#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00 +#define R2_IN_R3_SELECT_PAGE_TENS 0x01 +#define R2_IN_R3_SELECT_PAGE_UNITS 0x02 +#define R2_IN_R3_SELECT_HOURS_TENS 0x03 +#define R2_IN_R3_SELECT_HOURS_UNITS 0x04 +#define R2_IN_R3_SELECT_MINUTES_TENS 0x05 +#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06 + +#define R2_DAU_0 0x00 +#define R2_DAU_1 0x10 +#define R2_DAU_2 0x20 +#define R2_DAU_3 0x30 + +#define R2_BANK_0 0x00 +#define R2_BANK 1 0x40 + +#define R2_HAMMING_CHECK_ON 0x80 +#define R2_HAMMING_CHECK_OFF 0x00 + +/* Register R3 (Page request data) */ +#define R3_PAGE_HUNDREDS_0 0x00 +#define R3_PAGE_HUNDREDS_1 0x01 +#define R3_PAGE_HUNDREDS_2 0x02 +#define R3_PAGE_HUNDREDS_3 0x03 +#define R3_PAGE_HUNDREDS_4 0x04 +#define R3_PAGE_HUNDREDS_5 0x05 +#define R3_PAGE_HUNDREDS_6 0x06 +#define R3_PAGE_HUNDREDS_7 0x07 + +#define R3_HOLD_PAGE 0x00 +#define R3_UPDATE_PAGE 0x08 + +#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00 +#define R3_PAGE_HUNDREDS_DO_CARE 0x10 + +#define R3_PAGE_TENS_DO_NOT_CARE 0x00 +#define R3_PAGE_TENS_DO_CARE 0x10 + +#define R3_PAGE_UNITS_DO_NOT_CARE 0x00 +#define R3_PAGE_UNITS_DO_CARE 0x10 + +#define R3_HOURS_TENS_DO_NOT_CARE 0x00 +#define R3_HOURS_TENS_DO_CARE 0x10 + +#define R3_HOURS_UNITS_DO_NOT_CARE 0x00 +#define R3_HOURS_UNITS_DO_CARE 0x10 + +#define R3_MINUTES_TENS_DO_NOT_CARE 0x00 +#define R3_MINUTES_TENS_DO_CARE 0x10 + +#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00 +#define R3_MINUTES_UNITS_DO_CARE 0x10 + +/* Register R4 (Display chapter) */ +#define R4_DISPLAY_PAGE_0 0x00 +#define R4_DISPLAY_PAGE_1 0x01 +#define R4_DISPLAY_PAGE_2 0x02 +#define R4_DISPLAY_PAGE_3 0x03 +#define R4_DISPLAY_PAGE_4 0x04 +#define R4_DISPLAY_PAGE_5 0x05 +#define R4_DISPLAY_PAGE_6 0x06 +#define R4_DISPLAY_PAGE_7 0x07 + +/* Register R5 (Normal display control) */ +#define R5_PICTURE_INSIDE_BOXING_OFF 0x00 +#define R5_PICTURE_INSIDE_BOXING_ON 0x01 + +#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00 +#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02 + +#define R5_TEXT_INSIDE_BOXING_OFF 0x00 +#define R5_TEXT_INSIDE_BOXING_ON 0x04 + +#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00 +#define R5_TEXT_OUTSIDE_BOXING_ON 0x08 + +#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 +#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 + +#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 +#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 + +#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 +#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 + +#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 +#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 + +/* Register R6 (Newsflash display) */ +#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01 + +#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02 + +#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04 + +#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08 + +#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 + +#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 + +#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 + +#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 + +/* Register R7 (Display mode) */ +#define R7_BOX_OFF_ROW_0 0x00 +#define R7_BOX_ON_ROW_0 0x01 + +#define R7_BOX_OFF_ROW_1_TO_23 0x00 +#define R7_BOX_ON_ROW_1_TO_23 0x02 + +#define R7_BOX_OFF_ROW_24 0x00 +#define R7_BOX_ON_ROW_24 0x04 + +#define R7_SINGLE_HEIGHT 0x00 +#define R7_DOUBLE_HEIGHT 0x08 + +#define R7_TOP_HALF 0x00 +#define R7_BOTTOM_HALF 0x10 + +#define R7_REVEAL_OFF 0x00 +#define R7_REVEAL_ON 0x20 + +#define R7_CURSER_OFF 0x00 +#define R7_CURSER_ON 0x40 + +#define R7_STATUS_BOTTOM 0x00 +#define R7_STATUS_TOP 0x80 + +/* Register R8 (Active chapter) */ +#define R8_ACTIVE_CHAPTER_0 0x00 +#define R8_ACTIVE_CHAPTER_1 0x01 +#define R8_ACTIVE_CHAPTER_2 0x02 +#define R8_ACTIVE_CHAPTER_3 0x03 +#define R8_ACTIVE_CHAPTER_4 0x04 +#define R8_ACTIVE_CHAPTER_5 0x05 +#define R8_ACTIVE_CHAPTER_6 0x06 +#define R8_ACTIVE_CHAPTER_7 0x07 + +#define R8_CLEAR_MEMORY 0x08 +#define R8_DO_NOT_CLEAR_MEMORY 0x00 + +/* Register R9 (Curser row) */ +#define R9_CURSER_ROW_0 0x00 +#define R9_CURSER_ROW_1 0x01 +#define R9_CURSER_ROW_2 0x02 +#define R9_CURSER_ROW_25 0x19 + +/* Register R10 (Curser column) */ +#define R10_CURSER_COLUMN_0 0x00 +#define R10_CURSER_COLUMN_6 0x06 +#define R10_CURSER_COLUMN_8 0x08 + +/*****************************************************************************/ +/* Row 25 control data in column 0 to 9 */ +/*****************************************************************************/ +#define ROW25_COLUMN0_PAGE_UNITS 0x0F + +#define ROW25_COLUMN1_PAGE_TENS 0x0F + +#define ROW25_COLUMN2_MINUTES_UNITS 0x0F + +#define ROW25_COLUMN3_MINUTES_TENS 0x07 +#define ROW25_COLUMN3_DELETE_PAGE 0x08 + +#define ROW25_COLUMN4_HOUR_UNITS 0x0F + +#define ROW25_COLUMN5_HOUR_TENS 0x03 +#define ROW25_COLUMN5_INSERT_HEADLINE 0x04 +#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08 + +#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01 +#define ROW25_COLUMN6_UPDATE_PAGE 0x02 +#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04 +#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08 + +#define ROW25_COLUMN7_SERIAL_MODE 0x01 +#define ROW25_COLUMN7_CHARACTER_SET 0x0E + +#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07 +#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10 + +#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20 + +#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10 + +/*****************************************************************************/ +/* Helper macros for extracting page, hour and minute digits */ +/*****************************************************************************/ +/* BYTE_POS 0 is at row 0, column 0, + BYTE_POS 1 is at row 0, column 1, + BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40) + BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40), + ... */ +#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE) +#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE) + +/*****************************************************************************/ +/* Helper macros for extracting page, hour and minute digits */ +/*****************************************************************************/ +/* Macros for extracting hundreds, tens and units of a page number which + must be in the range 0 ... 0x799. + Note that page is coded in hexadecimal, i.e. 0x123 means page 123. + page 0x.. means page 8.. */ +#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7) +#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF) +#define UNITS_OF_PAGE(page) ((page) & 0xF) + +/* Macros for extracting tens and units of a hour information which + must be in the range 0 ... 0x24. + Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */ +#define TENS_OF_HOUR(hour) ((hour) / 0x10) +#define UNITS_OF_HOUR(hour) ((hour) & 0xF) + +/* Macros for extracting tens and units of a minute information which + must be in the range 0 ... 0x59. + Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */ +#define TENS_OF_MINUTE(minute) ((minute) / 0x10) +#define UNITS_OF_MINUTE(minute) ((minute) & 0xF) + +#define HOUR_MAX 0x23 +#define MINUTE_MAX 0x59 +#define PAGE_MAX 0x8FF + +#endif /* __SAA5246A_H__ */ diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c index 61d17ed4f008..f7700bbf4ba0 100644 --- a/drivers/media/video/videocodec.c +++ b/drivers/media/video/videocodec.c @@ -108,13 +108,17 @@ videocodec_attach (struct videocodec_master *master) if ((master->flags & h->codec->flags) == master->flags) { dprintk(4, "videocodec_attach: try '%s'\n", h->codec->name); + + if (!try_module_get(h->codec->owner)) + return NULL; + codec = kmalloc(sizeof(struct videocodec), GFP_KERNEL); if (!codec) { dprintk(1, KERN_ERR "videocodec_attach: no mem\n"); - return NULL; + goto out_module_put; } memcpy(codec, h->codec, sizeof(struct videocodec)); @@ -132,26 +136,12 @@ videocodec_attach (struct videocodec_master *master) dprintk(1, KERN_ERR "videocodec_attach: no memory\n"); - kfree(codec); - return NULL; + goto out_kfree; } memset(ptr, 0, sizeof(struct attached_list)); ptr->codec = codec; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_INC_USE_COUNT; -#else - if (!try_module_get(THIS_MODULE)) { - dprintk(1, - KERN_ERR - "videocodec: failed to increment usecount\n"); - kfree(codec); - kfree(ptr); - return NULL; - } -#endif - a = h->list; if (!a) { h->list = ptr; @@ -177,6 +167,12 @@ videocodec_attach (struct videocodec_master *master) dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n"); return NULL; + + out_module_put: + module_put(h->codec->owner); + out_kfree: + kfree(codec); + return NULL; } int @@ -228,16 +224,10 @@ videocodec_detach (struct videocodec *codec) dprintk(4, "videocodec: delete middle\n"); } + module_put(a->codec->owner); kfree(a->codec); kfree(a); h->attached -= 1; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_DEC_USE_COUNT; -#else - module_put(THIS_MODULE); -#endif - return 0; } prev = a; @@ -274,18 +264,6 @@ videocodec_register (const struct videocodec *codec) memset(ptr, 0, sizeof(struct codec_list)); ptr->codec = codec; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_INC_USE_COUNT; -#else - if (!try_module_get(THIS_MODULE)) { - dprintk(1, - KERN_ERR - "videocodec: failed to increment module count\n"); - kfree(ptr); - return -ENODEV; - } -#endif - if (!h) { codeclist_top = ptr; dprintk(4, "videocodec: hooked in as first element\n"); @@ -342,13 +320,6 @@ videocodec_unregister (const struct videocodec *codec) "videocodec: delete middle element\n"); } kfree(h); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_DEC_USE_COUNT; -#else - module_put(THIS_MODULE); -#endif - return 0; } prev = h; diff --git a/drivers/media/video/videocodec.h b/drivers/media/video/videocodec.h index 848022a90424..6541aa069816 100644 --- a/drivers/media/video/videocodec.h +++ b/drivers/media/video/videocodec.h @@ -249,6 +249,7 @@ struct tvnorm { }; struct videocodec { + struct module *owner; /* -- filled in by slave device during register -- */ char name[32]; unsigned long magic; /* may be used for client<->master attaching */ diff --git a/drivers/media/video/zr36016.c b/drivers/media/video/zr36016.c index f0fdbbc07148..ceb83bd57c39 100644 --- a/drivers/media/video/zr36016.c +++ b/drivers/media/video/zr36016.c @@ -422,12 +422,6 @@ zr36016_unset (struct videocodec *codec) codec->data = NULL; zr36016_codecs--; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_DEC_USE_COUNT; -#else - module_put(THIS_MODULE); -#endif - return 0; } @@ -470,19 +464,6 @@ zr36016_setup (struct videocodec *codec) ptr->num = zr36016_codecs++; ptr->codec = codec; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_INC_USE_COUNT; -#else - if (!try_module_get(THIS_MODULE)) { - dprintk(1, - KERN_ERR - "zr36016: failed to increase module use count\n"); - kfree(ptr); - zr36016_codecs--; - return -ENODEV; - } -#endif - //testing res = zr36016_basic_test(ptr); if (res < 0) { @@ -504,6 +485,7 @@ zr36016_setup (struct videocodec *codec) } static const struct videocodec zr36016_codec = { + .owner = THIS_MODULE, .name = "zr36016", .magic = 0L, // magic not used .flags = diff --git a/drivers/media/video/zr36050.c b/drivers/media/video/zr36050.c index be09ffccc47a..aa09a9297435 100644 --- a/drivers/media/video/zr36050.c +++ b/drivers/media/video/zr36050.c @@ -737,12 +737,6 @@ zr36050_unset (struct videocodec *codec) codec->data = NULL; zr36050_codecs--; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_DEC_USE_COUNT; -#else - module_put(THIS_MODULE); -#endif - return 0; } @@ -785,19 +779,6 @@ zr36050_setup (struct videocodec *codec) ptr->num = zr36050_codecs++; ptr->codec = codec; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_INC_USE_COUNT; -#else - if (!try_module_get(THIS_MODULE)) { - dprintk(1, - KERN_ERR - "zr36050: failed to increase module use count\n"); - kfree(ptr); - zr36050_codecs--; - return -ENODEV; - } -#endif - //testing res = zr36050_basic_test(ptr); if (res < 0) { @@ -826,6 +807,7 @@ zr36050_setup (struct videocodec *codec) } static const struct videocodec zr36050_codec = { + .owner = THIS_MODULE, .name = "zr36050", .magic = 0L, // magic not used .flags = diff --git a/drivers/media/video/zr36060.c b/drivers/media/video/zr36060.c index 41ae711a6e17..2d582a38dada 100644 --- a/drivers/media/video/zr36060.c +++ b/drivers/media/video/zr36060.c @@ -868,12 +868,6 @@ zr36060_unset (struct videocodec *codec) codec->data = NULL; zr36060_codecs--; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_DEC_USE_COUNT; -#else - module_put(THIS_MODULE); -#endif - return 0; } @@ -916,19 +910,6 @@ zr36060_setup (struct videocodec *codec) ptr->num = zr36060_codecs++; ptr->codec = codec; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - MOD_INC_USE_COUNT; -#else - if (!try_module_get(THIS_MODULE)) { - dprintk(1, - KERN_ERR - "zr36060: failed to increase module use count\n"); - kfree(ptr); - zr36060_codecs--; - return -ENODEV; - } -#endif - //testing res = zr36060_basic_test(ptr); if (res < 0) { @@ -958,6 +939,7 @@ zr36060_setup (struct videocodec *codec) } static const struct videocodec zr36060_codec = { + .owner = THIS_MODULE, .name = "zr36060", .magic = 0L, // magic not used .flags = diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 60efe39db6cd..4a0f2ea7c286 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -131,6 +131,8 @@ static int mtd_blktrans_thread(void *arg) end_request(req, res); } + spin_unlock_irq(rq->queue_lock); + complete_and_exit(&tr->blkcore_priv->thread_dead, 0); } diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c index 5ecdf5ab189f..9c81e7ba80a8 100644 --- a/drivers/net/3c505.c +++ b/drivers/net/3c505.c @@ -106,6 +106,7 @@ #include <linux/ioport.h> #include <linux/spinlock.h> #include <linux/ethtool.h> +#include <linux/delay.h> #include <asm/uaccess.h> #include <asm/bitops.h> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 2b71e23456f6..fe2464f6618e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1676,8 +1676,8 @@ config VIA_RHINE select CRC32 select MII help - If you have a VIA "rhine" based network card (Rhine-I (3043) or - Rhine-2 (VT86c100A)), say Y here. + If you have a VIA "rhine" based network card (Rhine-I (3043), + Rhine-2 (VT86c100A) or Rhine-III (VT6105)), say Y here. To compile this driver as a module, choose M here and read <file:Documentation/networking/net-modules.txt>. The module @@ -2172,6 +2172,17 @@ config HIPPI under Linux, say Y here (you must also remember to enable the driver for your HIPPI card below). Most people will say N here. +config IBMVETH + tristate "IBM LAN Virtual Ethernet support" + depends on NETDEVICES && NET_ETHERNET && PPC_PSERIES + ---help--- + This driver supports virtual ethernet adapters on newer IBM iSeries + and pSeries systems. + + To compile this driver as a module, choose M here and read + <file:Documentation/networking/net-modules.txt>. The module will + be called ibmveth. + config ROADRUNNER tristate "Essential RoadRunner HIPPI PCI adapter support (EXPERIMENTAL)" depends on HIPPI && PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 64aaa4d753af..2a8605626e14 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -174,6 +174,7 @@ obj-$(CONFIG_TUN) += tun.o obj-$(CONFIG_DL2K) += dl2k.o obj-$(CONFIG_R8169) += r8169.o obj-$(CONFIG_AMD8111_ETH) += amd8111e.o +obj-$(CONFIG_IBMVETH) += ibmveth.o obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_NET_FC) += fc/ diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index bc85d5d0016e..258fb84d8b23 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -59,9 +59,6 @@ #include <net/ax25.h> #endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ -#define __KERNEL_SYSCALLS__ -#include <linux/unistd.h> - /* --------------------------------------------------------------------- */ #define BAYCOM_DEBUG diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c new file mode 100644 index 000000000000..466b76383eb0 --- /dev/null +++ b/drivers/net/ibmveth.c @@ -0,0 +1,1141 @@ +/**************************************************************************/ +/* */ +/* IBM eServer i/pSeries Virtual Ethernet Device Driver */ +/* Copyright (C) 2003 IBM Corp. */ +/* Dave Larson (larson1@us.ibm.com) */ +/* Santiago Leon (santil@us.ibm.com) */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* 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 */ +/* */ +/* This module contains the implementation of a virtual ethernet device */ +/* for use with IBM i/pSeries LPAR Linux. It utilizes the logical LAN */ +/* option of the RS/6000 Platform Architechture to interface with virtual */ +/* ethernet NICs that are presented to the partition by the hypervisor. */ +/* */ +/**************************************************************************/ +/* + TODO: + - remove frag processing code - no longer needed + - add support for sysfs + - possibly remove procfs support +*/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/ethtool.h> +#include <linux/proc_fs.h> +#include <asm/semaphore.h> +#include <asm/hvcall.h> +#include <asm/atomic.h> +#include <asm/pci_dma.h> +#include <asm/vio.h> +#include <asm/uaccess.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#include "ibmveth.h" + +#define DEBUG 1 + +#define ibmveth_printk(fmt, args...) \ + printk(KERN_INFO "%s: " fmt, __FILE__, ## args) + +#define ibmveth_error_printk(fmt, args...) \ + printk(KERN_ERR "(%s:%3.3d ua:%lx) ERROR: " fmt, __FILE__, __LINE__ , adapter->vdev->unit_address, ## args) + +#ifdef DEBUG +#define ibmveth_debug_printk_no_adapter(fmt, args...) \ + printk(KERN_DEBUG "(%s:%3.3d): " fmt, __FILE__, __LINE__ , ## args) +#define ibmveth_debug_printk(fmt, args...) \ + printk(KERN_DEBUG "(%s:%3.3d ua:%lx): " fmt, __FILE__, __LINE__ , adapter->vdev->unit_address, ## args) +#define ibmveth_assert(expr) \ + if(!(expr)) { \ + printk(KERN_DEBUG "assertion failed (%s:%3.3d ua:%lx): %s\n", __FILE__, __LINE__, adapter->vdev->unit_address, #expr); \ + BUG(); \ + } +#else +#define ibmveth_debug_printk_no_adapter(fmt, args...) +#define ibmveth_debug_printk(fmt, args...) +#define ibmveth_assert(expr) +#endif + +static int ibmveth_open(struct net_device *dev); +static int ibmveth_close(struct net_device *dev); +static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static int ibmveth_poll(struct net_device *dev, int *budget); +static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *dev); +static struct net_device_stats *ibmveth_get_stats(struct net_device *dev); +static void ibmveth_set_multicast_list(struct net_device *dev); +static int ibmveth_change_mtu(struct net_device *dev, int new_mtu); +static void ibmveth_proc_register_driver(void); +static void ibmveth_proc_unregister_driver(void); +static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter); +static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter); +static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs *regs); + +#ifdef CONFIG_PROC_FS +#define IBMVETH_PROC_DIR "ibmveth" +static struct proc_dir_entry *ibmveth_proc_dir; +#endif + +static const char ibmveth_driver_name[] = "ibmveth"; +static const char ibmveth_driver_string[] = "IBM i/pSeries Virtual Ethernet Driver"; +static const char ibmveth_driver_version[] = "1.0"; + +MODULE_AUTHOR("Dave Larson <larson1@us.ibm.com>"); +MODULE_DESCRIPTION("IBM i/pSeries Virtual Ethernet Driver"); +MODULE_LICENSE("GPL"); + +/* simple methods of getting data from the current rxq entry */ +static inline int ibmveth_rxq_pending_buffer(struct ibmveth_adapter *adapter) +{ + return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].toggle == adapter->rx_queue.toggle); +} + +static inline int ibmveth_rxq_buffer_valid(struct ibmveth_adapter *adapter) +{ + return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].valid); +} + +static inline int ibmveth_rxq_frame_offset(struct ibmveth_adapter *adapter) +{ + return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].offset); +} + +static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter) +{ + return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].length); +} + +/* setup the initial settings for a buffer pool */ +static void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool, u32 pool_index, u32 pool_size, u32 buff_size) +{ + pool->size = pool_size; + pool->index = pool_index; + pool->buff_size = buff_size; + pool->threshold = pool_size / 2; +} + +/* allocate and setup an buffer pool - called during open */ +static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool) +{ + int i; + + pool->free_map = kmalloc(sizeof(u16) * pool->size, GFP_KERNEL); + + if(!pool->free_map) { + return -1; + } + + pool->dma_addr = kmalloc(sizeof(dma_addr_t) * pool->size, GFP_KERNEL); + if(!pool->dma_addr) { + kfree(pool->free_map); + pool->free_map = NULL; + return -1; + } + + pool->skbuff = kmalloc(sizeof(void*) * pool->size, GFP_KERNEL); + + if(!pool->skbuff) { + kfree(pool->dma_addr); + pool->dma_addr = NULL; + + kfree(pool->free_map); + pool->free_map = NULL; + return -1; + } + + memset(pool->skbuff, 0, sizeof(void*) * pool->size); + memset(pool->dma_addr, 0, sizeof(dma_addr_t) * pool->size); + + for(i = 0; i < pool->size; ++i) { + pool->free_map[i] = i; + } + + atomic_set(&pool->available, 0); + pool->producer_index = 0; + pool->consumer_index = 0; + + return 0; +} + +/* replenish the buffers for a pool. note that we don't need to + * skb_reserve these since they are used for incoming... + */ +static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struct ibmveth_buff_pool *pool) +{ + u32 i; + u32 count = pool->size - atomic_read(&pool->available); + u32 buffers_added = 0; + + mb(); + + for(i = 0; i < count; ++i) { + struct sk_buff *skb; + unsigned int free_index, index; + u64 correlator; + union ibmveth_buf_desc desc; + unsigned long lpar_rc; + dma_addr_t dma_addr; + + skb = alloc_skb(pool->buff_size, GFP_ATOMIC); + + if(!skb) { + ibmveth_debug_printk("replenish: unable to allocate skb\n"); + adapter->replenish_no_mem++; + break; + } + + free_index = pool->consumer_index++ % pool->size; + index = pool->free_map[free_index]; + + ibmveth_assert(index != 0xffff); + ibmveth_assert(pool->skbuff[index] == NULL); + + dma_addr = vio_map_single(adapter->vdev, skb->data, pool->buff_size, PCI_DMA_FROMDEVICE); + + pool->dma_addr[index] = dma_addr; + pool->skbuff[index] = skb; + + correlator = ((u64)pool->index << 32) | index; + *(u64*)skb->data = correlator; + + desc.desc = 0; + desc.fields.valid = 1; + desc.fields.length = pool->buff_size; + desc.fields.address = dma_addr; + + lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc); + + if(lpar_rc != H_Success) { + pool->skbuff[index] = NULL; + pool->consumer_index--; + vio_unmap_single(adapter->vdev, pool->dma_addr[index], pool->buff_size, PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + adapter->replenish_add_buff_failure++; + break; + } else { + pool->free_map[free_index] = 0xffff; + buffers_added++; + adapter->replenish_add_buff_success++; + } + } + + mb(); + atomic_add(buffers_added, &(pool->available)); +} + +/* check if replenishing is needed. */ +static inline int ibmveth_is_replenishing_needed(struct ibmveth_adapter *adapter) +{ + return ((atomic_read(&adapter->rx_buff_pool[0].available) < adapter->rx_buff_pool[0].threshold) || + (atomic_read(&adapter->rx_buff_pool[1].available) < adapter->rx_buff_pool[1].threshold) || + (atomic_read(&adapter->rx_buff_pool[2].available) < adapter->rx_buff_pool[2].threshold)); +} + +/* replenish tasklet routine */ +static void ibmveth_replenish_task(struct ibmveth_adapter *adapter) +{ + adapter->replenish_task_cycles++; + + ibmveth_replenish_buffer_pool(adapter, &adapter->rx_buff_pool[0]); + ibmveth_replenish_buffer_pool(adapter, &adapter->rx_buff_pool[1]); + ibmveth_replenish_buffer_pool(adapter, &adapter->rx_buff_pool[2]); + + adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8); + + atomic_inc(&adapter->not_replenishing); + ibmveth_assert(atomic_read(&adapter->not_replenishing) == 1); +} + +/* kick the replenish tasklet if we need replenishing and it isn't already running */ +static inline void ibmveth_schedule_replenishing(struct ibmveth_adapter *adapter) +{ + if(ibmveth_is_replenishing_needed(adapter) && + (atomic_dec_if_positive(&adapter->not_replenishing) == 0)) { + schedule_work(&adapter->replenish_task); + } +} + +/* empty and free ana buffer pool - also used to do cleanup in error paths */ +static void ibmveth_free_buffer_pool(struct ibmveth_adapter *adapter, struct ibmveth_buff_pool *pool) +{ + int i; + + if(pool->free_map) { + kfree(pool->free_map); + pool->free_map = NULL; + } + + if(pool->skbuff && pool->dma_addr) { + for(i = 0; i < pool->size; ++i) { + struct sk_buff *skb = pool->skbuff[i]; + if(skb) { + vio_unmap_single(adapter->vdev, + pool->dma_addr[i], + pool->buff_size, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + pool->skbuff[i] = NULL; + } + } + } + + if(pool->dma_addr) { + kfree(pool->dma_addr); + pool->dma_addr = NULL; + } + + if(pool->skbuff) { + kfree(pool->skbuff); + pool->skbuff = NULL; + } +} + +/* remove a buffer from a pool */ +static void ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter, u64 correlator) +{ + unsigned int pool = correlator >> 32; + unsigned int index = correlator & 0xffffffffUL; + unsigned int free_index; + struct sk_buff *skb; + + ibmveth_assert(pool < IbmVethNumBufferPools); + ibmveth_assert(index < adapter->rx_buff_pool[pool].size); + + skb = adapter->rx_buff_pool[pool].skbuff[index]; + + ibmveth_assert(skb != NULL); + + adapter->rx_buff_pool[pool].skbuff[index] = NULL; + + vio_unmap_single(adapter->vdev, + adapter->rx_buff_pool[pool].dma_addr[index], + adapter->rx_buff_pool[pool].buff_size, + PCI_DMA_FROMDEVICE); + + free_index = adapter->rx_buff_pool[pool].producer_index++ % adapter->rx_buff_pool[pool].size; + adapter->rx_buff_pool[pool].free_map[free_index] = index; + + mb(); + + atomic_dec(&(adapter->rx_buff_pool[pool].available)); +} + +/* get the current buffer on the rx queue */ +static inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *adapter) +{ + u64 correlator = adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator; + unsigned int pool = correlator >> 32; + unsigned int index = correlator & 0xffffffffUL; + + ibmveth_assert(pool < IbmVethNumBufferPools); + ibmveth_assert(index < adapter->rx_buff_pool[pool].size); + + return adapter->rx_buff_pool[pool].skbuff[index]; +} + +/* recycle the current buffer on the rx queue */ +static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter) +{ + u32 q_index = adapter->rx_queue.index; + u64 correlator = adapter->rx_queue.queue_addr[q_index].correlator; + unsigned int pool = correlator >> 32; + unsigned int index = correlator & 0xffffffffUL; + union ibmveth_buf_desc desc; + unsigned long lpar_rc; + + ibmveth_assert(pool < IbmVethNumBufferPools); + ibmveth_assert(index < adapter->rx_buff_pool[pool].size); + + desc.desc = 0; + desc.fields.valid = 1; + desc.fields.length = adapter->rx_buff_pool[pool].buff_size; + desc.fields.address = adapter->rx_buff_pool[pool].dma_addr[index]; + + lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc); + + if(lpar_rc != H_Success) { + ibmveth_debug_printk("h_add_logical_lan_buffer failed during recycle rc=%ld", lpar_rc); + ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator); + } + + if(++adapter->rx_queue.index == adapter->rx_queue.num_slots) { + adapter->rx_queue.index = 0; + adapter->rx_queue.toggle = !adapter->rx_queue.toggle; + } +} + +static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) +{ + ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator); + + if(++adapter->rx_queue.index == adapter->rx_queue.num_slots) { + adapter->rx_queue.index = 0; + adapter->rx_queue.toggle = !adapter->rx_queue.toggle; + } +} + +static void ibmveth_cleanup(struct ibmveth_adapter *adapter) +{ + if(adapter->buffer_list_addr != NULL) { + if(adapter->buffer_list_dma != NO_TCE) { + vio_unmap_single(adapter->vdev, adapter->buffer_list_dma, 4096, PCI_DMA_BIDIRECTIONAL); + adapter->buffer_list_dma = NO_TCE; + } + free_page((unsigned long)adapter->buffer_list_addr); + adapter->buffer_list_addr = NULL; + } + + if(adapter->filter_list_addr != NULL) { + if(adapter->filter_list_dma != NO_TCE) { + vio_unmap_single(adapter->vdev, adapter->filter_list_dma, 4096, PCI_DMA_BIDIRECTIONAL); + adapter->filter_list_dma = NO_TCE; + } + free_page((unsigned long)adapter->filter_list_addr); + adapter->filter_list_addr = NULL; + } + + if(adapter->rx_queue.queue_addr != NULL) { + if(adapter->rx_queue.queue_dma != NO_TCE) { + vio_unmap_single(adapter->vdev, adapter->rx_queue.queue_dma, adapter->rx_queue.queue_len, PCI_DMA_BIDIRECTIONAL); + adapter->rx_queue.queue_dma = NO_TCE; + } + kfree(adapter->rx_queue.queue_addr); + adapter->rx_queue.queue_addr = NULL; + } + + ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[0]); + ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[1]); + ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[2]); +} + +static int ibmveth_open(struct net_device *netdev) +{ + struct ibmveth_adapter *adapter = netdev->priv; + u64 mac_address = 0; + int rxq_entries; + unsigned long lpar_rc; + int rc; + union ibmveth_buf_desc rxq_desc; + + ibmveth_debug_printk("open starting\n"); + + rxq_entries = + adapter->rx_buff_pool[0].size + + adapter->rx_buff_pool[1].size + + adapter->rx_buff_pool[2].size + 1; + + adapter->buffer_list_addr = (void*) get_zeroed_page(GFP_KERNEL); + adapter->filter_list_addr = (void*) get_zeroed_page(GFP_KERNEL); + + if(!adapter->buffer_list_addr || !adapter->filter_list_addr) { + ibmveth_error_printk("unable to allocate filter or buffer list pages\n"); + ibmveth_cleanup(adapter); + return -ENOMEM; + } + + adapter->rx_queue.queue_len = sizeof(struct ibmveth_rx_q_entry) * rxq_entries; + adapter->rx_queue.queue_addr = kmalloc(adapter->rx_queue.queue_len, GFP_KERNEL); + + if(!adapter->rx_queue.queue_addr) { + ibmveth_error_printk("unable to allocate rx queue pages\n"); + ibmveth_cleanup(adapter); + return -ENOMEM; + } + + adapter->buffer_list_dma = vio_map_single(adapter->vdev, adapter->buffer_list_addr, 4096, PCI_DMA_BIDIRECTIONAL); + adapter->filter_list_dma = vio_map_single(adapter->vdev, adapter->filter_list_addr, 4096, PCI_DMA_BIDIRECTIONAL); + adapter->rx_queue.queue_dma = vio_map_single(adapter->vdev, adapter->rx_queue.queue_addr, adapter->rx_queue.queue_len, PCI_DMA_BIDIRECTIONAL); + + if((adapter->buffer_list_dma == NO_TCE) || + (adapter->filter_list_dma == NO_TCE) || + (adapter->rx_queue.queue_dma == NO_TCE)) { + ibmveth_error_printk("unable to map filter or buffer list pages\n"); + ibmveth_cleanup(adapter); + return -ENOMEM; + } + + adapter->rx_queue.index = 0; + adapter->rx_queue.num_slots = rxq_entries; + adapter->rx_queue.toggle = 1; + + if(ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[0]) || + ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[1]) || + ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[2])) + { + ibmveth_error_printk("unable to allocate buffer pools\n"); + ibmveth_cleanup(adapter); + return -ENOMEM; + } + + memcpy(&mac_address, netdev->dev_addr, netdev->addr_len); + mac_address = mac_address >> 16; + + rxq_desc.desc = 0; + rxq_desc.fields.valid = 1; + rxq_desc.fields.length = adapter->rx_queue.queue_len; + rxq_desc.fields.address = adapter->rx_queue.queue_dma; + + ibmveth_debug_printk("buffer list @ 0x%p\n", adapter->buffer_list_addr); + ibmveth_debug_printk("filter list @ 0x%p\n", adapter->filter_list_addr); + ibmveth_debug_printk("receive q @ 0x%p\n", adapter->rx_queue.queue_addr); + + + lpar_rc = h_register_logical_lan(adapter->vdev->unit_address, + adapter->buffer_list_dma, + rxq_desc.desc, + adapter->filter_list_dma, + mac_address); + + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_register_logical_lan failed with %ld\n", lpar_rc); + ibmveth_error_printk("buffer TCE:0x%x filter TCE:0x%x rxq desc:0x%lx MAC:0x%lx\n", + adapter->buffer_list_dma, + adapter->filter_list_dma, + rxq_desc.desc, + mac_address); + ibmveth_cleanup(adapter); + return -ENONET; + } + + ibmveth_debug_printk("registering irq 0x%x\n", netdev->irq); + if((rc = request_irq(netdev->irq, &ibmveth_interrupt, 0, netdev->name, netdev)) != 0) { + ibmveth_error_printk("unable to request irq 0x%x, rc %d\n", netdev->irq, rc); + h_free_logical_lan(adapter->vdev->unit_address); + ibmveth_cleanup(adapter); + return rc; + } + + netif_start_queue(netdev); + + ibmveth_debug_printk("scheduling initial replenish cycle\n"); + ibmveth_schedule_replenishing(adapter); + + ibmveth_debug_printk("open complete\n"); + + return 0; +} + +static int ibmveth_close(struct net_device *netdev) +{ + struct ibmveth_adapter *adapter = netdev->priv; + long lpar_rc; + + ibmveth_debug_printk("close starting\n"); + + netif_stop_queue(netdev); + + free_irq(netdev->irq, netdev); + + cancel_delayed_work(&adapter->replenish_task); + flush_scheduled_work(); + + lpar_rc = h_free_logical_lan(adapter->vdev->unit_address); + + if(lpar_rc != H_Success) + { + ibmveth_error_printk("h_free_logical_lan failed with %lx, continuing with close\n", + lpar_rc); + } + + adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8); + + ibmveth_cleanup(adapter); + + ibmveth_debug_printk("close complete\n"); + + return 0; +} + +static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { + cmd->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE); + cmd->advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_FIBRE); + cmd->speed = SPEED_1000; + cmd->duplex = DUPLEX_FULL; + cmd->port = PORT_FIBRE; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_ENABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 1; + return 0; +} + +static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) { + strncpy(info->driver, ibmveth_driver_name, sizeof(info->driver) - 1); + strncpy(info->version, ibmveth_driver_version, sizeof(info->version) - 1); +} + +static u32 netdev_get_link(struct net_device *dev) { + return 0; +} + +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_settings = netdev_get_settings, + .get_link = netdev_get_link, + .get_sg = ethtool_op_get_sg, + .get_tx_csum = ethtool_op_get_tx_csum, +}; + +static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + return -EOPNOTSUPP; +} + +#define page_offset(v) ((unsigned long)(v) & ((1 << 12) - 1)) + +static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ibmveth_adapter *adapter = netdev->priv; + union ibmveth_buf_desc desc[IbmVethMaxSendFrags]; + unsigned long lpar_rc; + int nfrags = 0, curfrag; + + if ((skb_shinfo(skb)->nr_frags + 1) > IbmVethMaxSendFrags) { + adapter->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + memset(&desc, 0, sizeof(desc)); + + /* nfrags = number of frags after the initial fragment */ + nfrags = skb_shinfo(skb)->nr_frags; + + if(nfrags) + adapter->tx_multidesc_send++; + + /* map the initial fragment */ + desc[0].fields.length = nfrags ? skb->len - skb->data_len : skb->len; + desc[0].fields.address = vio_map_single(adapter->vdev, skb->data, desc[0].fields.length, PCI_DMA_TODEVICE); + desc[0].fields.valid = 1; + + if(desc[0].fields.address == NO_TCE) { + ibmveth_error_printk("tx: unable to map initial fragment\n"); + adapter->tx_map_failed++; + adapter->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + curfrag = nfrags; + + /* map fragments past the initial portion if there are any */ + while(curfrag--) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[curfrag]; + desc[curfrag+1].fields.address = vio_map_single(adapter->vdev, + page_address(frag->page) + frag->page_offset, + frag->size, PCI_DMA_TODEVICE); + desc[curfrag+1].fields.length = frag->size; + desc[curfrag+1].fields.valid = 1; + + if(desc[curfrag+1].fields.address == NO_TCE) { + ibmveth_error_printk("tx: unable to map fragment %d\n", curfrag); + adapter->tx_map_failed++; + adapter->stats.tx_dropped++; + /* Free all the mappings we just created */ + while(curfrag < nfrags) { + vio_unmap_single(adapter->vdev, + desc[curfrag+1].fields.address, + desc[curfrag+1].fields.length, + PCI_DMA_TODEVICE); + curfrag++; + } + dev_kfree_skb(skb); + return 0; + } + } + + /* send the frame. Arbitrarily set retrycount to 1024 */ + unsigned long correlator = 0; + unsigned int retry_count = 1024; + do { + lpar_rc = h_send_logical_lan(adapter->vdev->unit_address, + desc[0].desc, + desc[1].desc, + desc[2].desc, + desc[3].desc, + desc[4].desc, + desc[5].desc, + correlator); + } while ((lpar_rc == H_Busy) && (retry_count--)); + + if(lpar_rc != H_Success && lpar_rc != H_Dropped) { + int i; + ibmveth_error_printk("tx: h_send_logical_lan failed with rc=%ld\n", lpar_rc); + for(i = 0; i < 6; i++) { + ibmveth_error_printk("tx: desc[%i] valid=%d, len=%d, address=0x%d\n", i, + desc[i].fields.valid, desc[i].fields.length, desc[i].fields.address); + } + adapter->tx_send_failed++; + adapter->stats.tx_dropped++; + } else { + adapter->stats.tx_packets++; + adapter->stats.tx_bytes += skb->len; + } + + do { + vio_unmap_single(adapter->vdev, desc[nfrags].fields.address, desc[nfrags].fields.length, PCI_DMA_TODEVICE); + } while(--nfrags >= 0); + + dev_kfree_skb(skb); + return 0; +} + +static int ibmveth_poll(struct net_device *netdev, int *budget) +{ + struct ibmveth_adapter *adapter = netdev->priv; + int max_frames_to_process = netdev->quota; + int frames_processed = 0; + int more_work = 1; + unsigned long lpar_rc; + + restart_poll: + do { + struct net_device *netdev = adapter->netdev; + + if(ibmveth_rxq_pending_buffer(adapter)) { + struct sk_buff *skb; + + if(!ibmveth_rxq_buffer_valid(adapter)) { + wmb(); /* suggested by larson1 */ + adapter->rx_invalid_buffer++; + ibmveth_debug_printk("recycling invalid buffer\n"); + ibmveth_rxq_recycle_buffer(adapter); + } else { + int length = ibmveth_rxq_frame_length(adapter); + int offset = ibmveth_rxq_frame_offset(adapter); + skb = ibmveth_rxq_get_buffer(adapter); + + ibmveth_rxq_harvest_buffer(adapter); + + skb_reserve(skb, offset); + skb_put(skb, length); + skb->dev = netdev; + skb->protocol = eth_type_trans(skb, netdev); + + netif_receive_skb(skb); /* send it up */ + + adapter->stats.rx_packets++; + adapter->stats.rx_bytes += length; + frames_processed++; + } + } else { + more_work = 0; + } + } while(more_work && (frames_processed < max_frames_to_process)); + + ibmveth_schedule_replenishing(adapter); + + if(more_work) { + /* more work to do - return that we are not done yet */ + netdev->quota -= frames_processed; + *budget -= frames_processed; + return 1; + } + + /* we think we are done - reenable interrupts, then check once more to make sure we are done */ + lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_ENABLE); + + ibmveth_assert(lpar_rc == H_Success); + + netif_rx_complete(netdev); + + if(ibmveth_rxq_pending_buffer(adapter) && netif_rx_reschedule(netdev, frames_processed)) + { + lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE); + ibmveth_assert(lpar_rc == H_Success); + more_work = 1; + goto restart_poll; + } + + netdev->quota -= frames_processed; + *budget -= frames_processed; + + /* we really are done */ + return 0; +} + +static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *netdev = dev_instance; + struct ibmveth_adapter *adapter = netdev->priv; + unsigned long lpar_rc; + + if(netif_rx_schedule_prep(netdev)) { + lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE); + ibmveth_assert(lpar_rc == H_Success); + __netif_rx_schedule(netdev); + } + return IRQ_HANDLED; +} + +static struct net_device_stats *ibmveth_get_stats(struct net_device *dev) +{ + struct ibmveth_adapter *adapter = dev->priv; + return &adapter->stats; +} + +static void ibmveth_set_multicast_list(struct net_device *netdev) +{ + struct ibmveth_adapter *adapter = netdev->priv; + unsigned long lpar_rc; + + if((netdev->flags & IFF_PROMISC) || (netdev->mc_count > adapter->mcastFilterSize)) { + lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, + IbmVethMcastEnableRecv | + IbmVethMcastDisableFiltering, + 0); + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_multicast_ctrl rc=%ld when entering promisc mode\n", lpar_rc); + } + } else { + struct dev_mc_list *mclist = netdev->mc_list; + int i; + /* clear the filter table & disable filtering */ + lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, + IbmVethMcastEnableRecv | + IbmVethMcastDisableFiltering | + IbmVethMcastClearFilterTable, + 0); + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_multicast_ctrl rc=%ld when attempting to clear filter table\n", lpar_rc); + } + /* add the addresses to the filter table */ + for(i = 0; i < netdev->mc_count; ++i, mclist = mclist->next) { + // add the multicast address to the filter table + unsigned long mcast_addr = 0; + memcpy(((char *)&mcast_addr)+2, mclist->dmi_addr, 6); + lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, + IbmVethMcastAddFilter, + mcast_addr); + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_multicast_ctrl rc=%ld when adding an entry to the filter table\n", lpar_rc); + } + } + + /* re-enable filtering */ + lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, + IbmVethMcastEnableFiltering, + 0); + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_multicast_ctrl rc=%ld when enabling filtering\n", lpar_rc); + } + } +} + +static int ibmveth_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > (1<<20))) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) +{ + int rc; + struct net_device *netdev; + struct ibmveth_adapter *adapter; + + unsigned int *mac_addr_p; + unsigned int *mcastFilterSize_p; + + + ibmveth_debug_printk_no_adapter("entering ibmveth_probe for UA 0x%lx\n", + dev->unit_address); + + mac_addr_p = (unsigned int *) vio_get_attribute(dev, VETH_MAC_ADDR, 0); + if(!mac_addr_p) { + ibmveth_error_printk("Can't find VETH_MAC_ADDR attribute\n"); + return 0; + } + + mcastFilterSize_p= (unsigned int *) vio_get_attribute(dev, VETH_MCAST_FILTER_SIZE, 0); + if(!mcastFilterSize_p) { + ibmveth_error_printk("Can't find VETH_MCAST_FILTER_SIZE attribute\n"); + return 0; + } + + netdev = alloc_etherdev(sizeof(struct ibmveth_adapter)); + + if(!netdev) + return -ENOMEM; + + SET_MODULE_OWNER(netdev); + + adapter = netdev->priv; + memset(adapter, 0, sizeof(adapter)); + dev->driver_data = netdev; + + adapter->vdev = dev; + adapter->netdev = netdev; + adapter->mcastFilterSize= *mcastFilterSize_p; + + /* Some older boxes running PHYP non-natively have an OF that + returns a 8-byte local-mac-address field (and the first + 2 bytes have to be ignored) while newer boxes' OF return + a 6-byte field. Note that IEEE 1275 specifies that + local-mac-address must be a 6-byte field. + The RPA doc specifies that the first byte must be 10b, so + we'll just look for it to solve this 8 vs. 6 byte field issue */ + + while (*((char*)mac_addr_p) != (char)(0x02)) + ((char*)mac_addr_p)++; + + adapter->mac_addr = 0; + memcpy(&adapter->mac_addr, mac_addr_p, 6); + + adapter->liobn = dev->tce_table->index; + + netdev->irq = dev->irq; + netdev->open = ibmveth_open; + netdev->poll = ibmveth_poll; + netdev->weight = 16; + netdev->stop = ibmveth_close; + netdev->hard_start_xmit = ibmveth_start_xmit; + netdev->get_stats = ibmveth_get_stats; + netdev->set_multicast_list = ibmveth_set_multicast_list; + netdev->do_ioctl = ibmveth_ioctl; + netdev->ethtool_ops = &netdev_ethtool_ops; + netdev->change_mtu = ibmveth_change_mtu; + + memcpy(&netdev->dev_addr, &adapter->mac_addr, netdev->addr_len); + + ibmveth_init_buffer_pool(&adapter->rx_buff_pool[0], 0, IbmVethPool0DftCnt, IbmVethPool0DftSize); + ibmveth_init_buffer_pool(&adapter->rx_buff_pool[1], 1, IbmVethPool1DftCnt, IbmVethPool1DftSize); + ibmveth_init_buffer_pool(&adapter->rx_buff_pool[2], 2, IbmVethPool2DftCnt, IbmVethPool2DftSize); + + ibmveth_debug_printk("adapter @ 0x%p\n", adapter); + + INIT_WORK(&adapter->replenish_task, (void*)ibmveth_replenish_task, (void*)adapter); + + adapter->buffer_list_dma = NO_TCE; + adapter->filter_list_dma = NO_TCE; + adapter->rx_queue.queue_dma = NO_TCE; + + atomic_set(&adapter->not_replenishing, 1); + + ibmveth_debug_printk("registering netdev...\n"); + + rc = register_netdev(netdev); + + if(rc) { + ibmveth_debug_printk("failed to register netdev rc=%d\n", rc); + free_netdev(netdev); + return rc; + } + + ibmveth_debug_printk("registered\n"); + + ibmveth_proc_register_adapter(adapter); + + return 0; +} + +static int __devexit ibmveth_remove(struct vio_dev *dev) +{ + struct net_device *netdev = dev->driver_data; + struct ibmveth_adapter *adapter = netdev->priv; + + unregister_netdev(netdev); + + ibmveth_proc_unregister_adapter(adapter); + + free_netdev(netdev); + return 0; +} + +#ifdef CONFIG_PROC_FS +static void ibmveth_proc_register_driver(void) +{ + ibmveth_proc_dir = create_proc_entry(IBMVETH_PROC_DIR, S_IFDIR, proc_net); + if (ibmveth_proc_dir) { + SET_MODULE_OWNER(ibmveth_proc_dir); + } +} + +static void ibmveth_proc_unregister_driver(void) +{ + remove_proc_entry(IBMVETH_PROC_DIR, proc_net); +} + +static void *ibmveth_seq_start(struct seq_file *seq, loff_t *pos) +{ + if (*pos == 0) { + return (void *)1; + } else { + return NULL; + } +} + +static void *ibmveth_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void ibmveth_seq_stop(struct seq_file *seq, void *v) +{ +} + +static int ibmveth_seq_show(struct seq_file *seq, void *v) +{ + struct ibmveth_adapter *adapter = seq->private; + char *current_mac = ((char*) &adapter->netdev->dev_addr); + char *firmware_mac = ((char*) &adapter->mac_addr) ; + + seq_printf(seq, "%s %s\n\n", ibmveth_driver_string, ibmveth_driver_version); + + seq_printf(seq, "Unit Address: 0x%lx\n", adapter->vdev->unit_address); + seq_printf(seq, "LIOBN: 0x%lx\n", adapter->liobn); + seq_printf(seq, "Current MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + current_mac[0], current_mac[1], current_mac[2], + current_mac[3], current_mac[4], current_mac[5]); + seq_printf(seq, "Firmware MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + firmware_mac[0], firmware_mac[1], firmware_mac[2], + firmware_mac[3], firmware_mac[4], firmware_mac[5]); + + seq_printf(seq, "\nAdapter Statistics:\n"); + seq_printf(seq, " TX: skbuffs linearized: %ld\n", adapter->tx_linearized); + seq_printf(seq, " multi-descriptor sends: %ld\n", adapter->tx_multidesc_send); + seq_printf(seq, " skb_linearize failures: %ld\n", adapter->tx_linearize_failed); + seq_printf(seq, " vio_map_single failres: %ld\n", adapter->tx_map_failed); + seq_printf(seq, " send failures: %ld\n", adapter->tx_send_failed); + seq_printf(seq, " RX: replenish task cycles: %ld\n", adapter->replenish_task_cycles); + seq_printf(seq, " alloc_skb_failures: %ld\n", adapter->replenish_no_mem); + seq_printf(seq, " add buffer failures: %ld\n", adapter->replenish_add_buff_failure); + seq_printf(seq, " invalid buffers: %ld\n", adapter->rx_invalid_buffer); + seq_printf(seq, " no buffers: %ld\n", adapter->rx_no_buffer); + + return 0; +} +static struct seq_operations ibmveth_seq_ops = { + .start = ibmveth_seq_start, + .next = ibmveth_seq_next, + .stop = ibmveth_seq_stop, + .show = ibmveth_seq_show, +}; + +static int ibmveth_proc_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + struct proc_dir_entry *proc; + int rc; + + rc = seq_open(file, &ibmveth_seq_ops); + if (!rc) { + /* recover the pointer buried in proc_dir_entry data */ + seq = file->private_data; + proc = PDE(inode); + seq->private = proc->data; + } + return rc; +} + +static struct file_operations ibmveth_proc_fops = { + .owner = THIS_MODULE, + .open = ibmveth_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter) +{ + struct proc_dir_entry *entry; + if (ibmveth_proc_dir) { + entry = create_proc_entry(adapter->netdev->name, S_IFREG, ibmveth_proc_dir); + if (!entry) { + ibmveth_error_printk("Cannot create adapter proc entry"); + } else { + entry->data = (void *) adapter; + entry->proc_fops = &ibmveth_proc_fops; + SET_MODULE_OWNER(entry); + } + } + return; +} + +static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter) +{ + if (ibmveth_proc_dir) { + remove_proc_entry(adapter->netdev->name, ibmveth_proc_dir); + } +} + +#else /* CONFIG_PROC_FS */ +static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter) +{ +} + +static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter) +{ +} +static void ibmveth_proc_register_driver(void) +{ +} + +static void ibmveth_proc_unregister_driver(void) +{ +} +#endif /* CONFIG_PROC_FS */ + +static struct vio_device_id ibmveth_device_table[] __devinitdata= { + { "network", "IBM,l-lan"}, + { 0,} +}; + +MODULE_DEVICE_TABLE(vio, ibmveth_device_table); + +static struct vio_driver ibmveth_driver = { + .name = (char *)ibmveth_driver_name, + .id_table = ibmveth_device_table, + .probe = ibmveth_probe, + .remove = ibmveth_remove +}; + +static int __init ibmveth_module_init(void) +{ + ibmveth_printk("%s: %s %s\n", ibmveth_driver_name, ibmveth_driver_string, ibmveth_driver_version); + + ibmveth_proc_register_driver(); + + return vio_register_driver(&ibmveth_driver); +} + +static void __exit ibmveth_module_exit(void) +{ + vio_unregister_driver(&ibmveth_driver); + ibmveth_proc_unregister_driver(); +} + +module_init(ibmveth_module_init); +module_exit(ibmveth_module_exit); diff --git a/drivers/net/ibmveth.h b/drivers/net/ibmveth.h new file mode 100644 index 000000000000..599d15b2f09c --- /dev/null +++ b/drivers/net/ibmveth.h @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* */ +/* IBM eServer i/[Series Virtual Ethernet Device Driver */ +/* Copyright (C) 2003 IBM Corp. */ +/* Dave Larson (larson1@us.ibm.com) */ +/* Santiago Leon (santil@us.ibm.com) */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* 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 */ +/* */ +/**************************************************************************/ + +#ifndef _IBMVETH_H +#define _IBMVETH_H + +#define IbmVethMaxSendFrags 6 + +/* constants for H_MULTICAST_CTRL */ +#define IbmVethMcastReceptionModifyBit 0x80000UL +#define IbmVethMcastReceptionEnableBit 0x20000UL +#define IbmVethMcastFilterModifyBit 0x40000UL +#define IbmVethMcastFilterEnableBit 0x10000UL + +#define IbmVethMcastEnableRecv (IbmVethMcastReceptionModifyBit | IbmVethMcastReceptionEnableBit) +#define IbmVethMcastDisableRecv (IbmVethMcastReceptionModifyBit) +#define IbmVethMcastEnableFiltering (IbmVethMcastFilterModifyBit | IbmVethMcastFilterEnableBit) +#define IbmVethMcastDisableFiltering (IbmVethMcastFilterModifyBit) +#define IbmVethMcastAddFilter 0x1UL +#define IbmVethMcastRemoveFilter 0x2UL +#define IbmVethMcastClearFilterTable 0x3UL + +/* hcall numbers */ +#define H_VIO_SIGNAL 0x104 +#define H_REGISTER_LOGICAL_LAN 0x114 +#define H_FREE_LOGICAL_LAN 0x118 +#define H_ADD_LOGICAL_LAN_BUFFER 0x11C +#define H_SEND_LOGICAL_LAN 0x120 +#define H_MULTICAST_CTRL 0x130 +#define H_CHANGE_LOGICAL_LAN_MAC 0x14C + +/* hcall macros */ +#define h_register_logical_lan(ua, buflst, rxq, fltlst, mac) \ + plpar_hcall_norets(H_REGISTER_LOGICAL_LAN, ua, buflst, rxq, fltlst, mac) + +#define h_free_logical_lan(ua) \ + plpar_hcall_norets(H_FREE_LOGICAL_LAN, ua) + +#define h_add_logical_lan_buffer(ua, buf) \ + plpar_hcall_norets(H_ADD_LOGICAL_LAN_BUFFER, ua, buf) + +#define h_send_logical_lan(ua, buf1, buf2, buf3, buf4, buf5, buf6, correlator) \ + plpar_hcall_8arg_2ret(H_SEND_LOGICAL_LAN, ua, buf1, buf2, buf3, buf4, buf5, buf6, correlator, &correlator) + +#define h_multicast_ctrl(ua, cmd, mac) \ + plpar_hcall_norets(H_MULTICAST_CTRL, ua, cmd, mac) + +#define h_change_logical_lan_mac(ua, mac) \ + plpar_hcall_norets(H_CHANGE_LOGICAL_LAN_MAC, ua, mac) + +#define IbmVethNumBufferPools 3 +#define IbmVethPool0DftSize (1024 * 2) +#define IbmVethPool1DftSize (1024 * 4) +#define IbmVethPool2DftSize (1024 * 10) +#define IbmVethPool0DftCnt 256 +#define IbmVethPool1DftCnt 256 +#define IbmVethPool2DftCnt 256 + +struct ibmveth_buff_pool { + u32 size; + u32 index; + u32 buff_size; + u32 threshold; + atomic_t available; + u32 consumer_index; + u32 producer_index; + u16 *free_map; + dma_addr_t *dma_addr; + struct sk_buff **skbuff; +}; + +struct ibmveth_rx_q { + u64 index; + u64 num_slots; + u64 toggle; + dma_addr_t queue_dma; + u32 queue_len; + struct ibmveth_rx_q_entry *queue_addr; +}; + +struct ibmveth_adapter { + struct vio_dev *vdev; + struct net_device *netdev; + struct net_device_stats stats; + unsigned int mcastFilterSize; + unsigned long mac_addr; + unsigned long liobn; + void * buffer_list_addr; + void * filter_list_addr; + dma_addr_t buffer_list_dma; + dma_addr_t filter_list_dma; + struct ibmveth_buff_pool rx_buff_pool[IbmVethNumBufferPools]; + struct ibmveth_rx_q rx_queue; + atomic_t not_replenishing; + + /* helper tasks */ + struct work_struct replenish_task; + + /* adapter specific stats */ + u64 replenish_task_cycles; + u64 replenish_no_mem; + u64 replenish_add_buff_failure; + u64 replenish_add_buff_success; + u64 rx_invalid_buffer; + u64 rx_no_buffer; + u64 tx_multidesc_send; + u64 tx_linearized; + u64 tx_linearize_failed; + u64 tx_map_failed; + u64 tx_send_failed; +}; + +struct ibmveth_buf_desc_fields { + u32 valid : 1; + u32 toggle : 1; + u32 reserved : 6; + u32 length : 24; + u32 address; +}; + +union ibmveth_buf_desc { + u64 desc; + struct ibmveth_buf_desc_fields fields; +}; + +struct ibmveth_rx_q_entry { + u16 toggle : 1; + u16 valid : 1; + u16 reserved : 14; + u16 offset; + u32 length; + u64 correlator; +}; + +#endif /* _IBMVETH_H */ diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 72cdce031bda..5a2289a00039 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -77,7 +77,7 @@ static struct usb_device_id dongles[] = { /* ACTiSYS Corp., ACT-IR2000U FIR-USB Adapter */ { USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */ - { USB_DEVICE(0x4428, 0x012), driver_info: IUC_SPEED_BUG | IUC_NO_WINDOW }, + { USB_DEVICE(0x4428, 0x012), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, /* KC Technology Inc., KC-180 USB IrDA Device */ { USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c index b2cbf973918d..eaac53958f2c 100644 --- a/drivers/net/lasi_82596.c +++ b/drivers/net/lasi_82596.c @@ -1016,8 +1016,6 @@ static int i596_open(struct net_device *dev) { DEB(DEB_OPEN,printk("%s: i596_open() irq %d.\n", dev->name, dev->irq)); - MOD_INC_USE_COUNT; - if (request_irq(dev->irq, &i596_interrupt, 0, "i82596", dev)) { printk("%s: IRQ %d not free\n", dev->name, dev->irq); goto out; @@ -1038,8 +1036,6 @@ out_remove_rx_bufs: remove_rx_bufs(dev); free_irq(dev->irq, dev); out: - MOD_DEC_USE_COUNT; - return -EAGAIN; } diff --git a/drivers/net/meth.c b/drivers/net/meth.c index 17524d261b1a..3361d2701664 100644 --- a/drivers/net/meth.c +++ b/drivers/net/meth.c @@ -356,8 +356,6 @@ int meth_open(struct net_device *dev) meth_private *priv=dev->priv; volatile meth_regs *regs=priv->regs; - MOD_INC_USE_COUNT; - /* Start DMA */ regs->dma_ctrl|= METH_DMA_TX_EN|/*METH_DMA_TX_INT_EN|*/ @@ -380,7 +378,6 @@ int meth_release(struct net_device *dev) ~(METH_DMA_TX_EN|METH_DMA_TX_INT_EN| METH_DMA_RX_EN|METH_DMA_RX_INT_EN); free_irq(dev->irq, dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c index 72bb389e0d34..718108487c1e 100644 --- a/drivers/net/net_init.c +++ b/drivers/net/net_init.c @@ -103,116 +103,6 @@ struct net_device *alloc_netdev(int sizeof_priv, const char *mask, } EXPORT_SYMBOL(alloc_netdev); -static struct net_device *init_alloc_dev(int sizeof_priv) -{ - struct net_device *dev; - int alloc_size; - - /* ensure 32-byte alignment of the private area */ - alloc_size = sizeof (*dev) + sizeof_priv + 31; - - dev = (struct net_device *) kmalloc (alloc_size, GFP_KERNEL); - if (dev == NULL) - { - printk(KERN_ERR "alloc_dev: Unable to allocate device memory.\n"); - return NULL; - } - - memset(dev, 0, alloc_size); - - if (sizeof_priv) - dev->priv = (void *) (((long)(dev + 1) + 31) & ~31); - - return dev; -} - -/* - * Create and name a device from a prototype, then perform any needed - * setup. - */ - -static struct net_device *init_netdev(struct net_device *dev, int sizeof_priv, - char *mask, void (*setup)(struct net_device *)) -{ - int new_device = 0; - - /* - * Allocate a device if one is not provided. - */ - - if (dev == NULL) { - dev=init_alloc_dev(sizeof_priv); - if(dev==NULL) - return NULL; - new_device = 1; - } - - /* - * Allocate a name - */ - - if (dev->name[0] == '\0' || dev->name[0] == ' ') { - strcpy(dev->name, mask); - rtnl_lock(); - if (dev_alloc_name(dev, mask)<0) { - rtnl_unlock(); - if (new_device) - kfree(dev); - return NULL; - } - rtnl_unlock(); - } - - netdev_boot_setup_check(dev); - - /* - * Configure via the caller provided setup function then - * register if needed. - */ - - setup(dev); - - if (new_device) { - int err; - - rtnl_lock(); - err = register_netdevice(dev); - rtnl_unlock(); - - if (err < 0) { - kfree(dev); - dev = NULL; - } - } - return dev; -} - -/** - * init_etherdev - Register ethernet device - * @dev: An ethernet device structure to be filled in, or %NULL if a new - * struct should be allocated. - * @sizeof_priv: Size of additional driver-private structure to be allocated - * for this ethernet device - * - * Fill in the fields of the device structure with ethernet-generic values. - * - * If no device structure is passed, a new one is constructed, complete with - * a private data area of size @sizeof_priv. A 32-byte (not bit) - * alignment is enforced for this private data area. - * - * If an empty string area is passed as dev->name, or a new structure is made, - * a new name string is constructed. - * - * Deprecated because of exposed window between device registration - * and interfaces pointers that need to be set by driver. - * Use alloc_etherdev and register_netdev instead. - */ - -struct net_device *__init_etherdev(struct net_device *dev, int sizeof_priv) -{ - return init_netdev(dev, sizeof_priv, "eth%d", ether_setup); -} - /** * alloc_etherdev - Allocates and sets up an ethernet device * @sizeof_priv: Size of additional driver-private structure to be allocated @@ -231,7 +121,6 @@ struct net_device *alloc_etherdev(int sizeof_priv) return alloc_netdev(sizeof_priv, "eth%d", ether_setup); } -EXPORT_SYMBOL(__init_etherdev); EXPORT_SYMBOL(alloc_etherdev); static int eth_mac_addr(struct net_device *dev, void *p) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index f7da45b36f4a..cb07b9e817c7 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -526,13 +526,17 @@ static u32 pcnet32_get_link(struct net_device *dev) { struct pcnet32_private *lp = dev->priv; unsigned long flags; - int r = 1; + int r; + spin_lock_irqsave(&lp->lock, flags); if (lp->mii) { - spin_lock_irqsave(&lp->lock, flags); r = mii_link_ok(&lp->mii_if); - spin_unlock_irqrestore(&lp->lock, flags); + } else { + ulong ioaddr = dev->base_addr; /* card base I/O address */ + r = (lp->a.read_bcr(ioaddr, 4) != 0xc0); } + spin_unlock_irqrestore(&lp->lock, flags); + return r; } diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 0c1e6b6f5a18..8fff89ff4cb1 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -2456,8 +2456,6 @@ static int sbmac_open(struct net_device *dev) { struct sbmac_softc *sc = (struct sbmac_softc *)dev->priv; - MOD_INC_USE_COUNT; - if (debug > 1) { printk(KERN_DEBUG "%s: sbmac_open() irq %d.\n", dev->name, dev->irq); } @@ -2466,10 +2464,8 @@ static int sbmac_open(struct net_device *dev) * map/route interrupt */ - if (request_irq(dev->irq, &sbmac_intr, SA_SHIRQ, dev->name, dev)) { - MOD_DEC_USE_COUNT; + if (request_irq(dev->irq, &sbmac_intr, SA_SHIRQ, dev->name, dev)) return -EBUSY; - } /* * Configure default speed @@ -2791,8 +2787,6 @@ static int sbmac_close(struct net_device *dev) sbdma_emptyring(&(sc->sbm_txdma)); sbdma_emptyring(&(sc->sbm_rxdma)); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c index 461d4e6072f3..bbb62e7a4be7 100644 --- a/drivers/net/sun3lance.c +++ b/drivers/net/sun3lance.c @@ -430,7 +430,6 @@ static int lance_open( struct net_device *dev ) netif_start_queue(dev); DPRINTK( 2, ( "%s: LANCE is open, csr0 %04x\n", dev->name, DREG )); - MOD_INC_USE_COUNT; return( 0 ); } @@ -883,8 +882,6 @@ static int lance_close( struct net_device *dev ) /* We stop the LANCE here -- it occasionally polls memory if we don't. */ DREG = CSR0_STOP; - - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c index 78ee02e19451..90a0fca56f84 100644 --- a/drivers/net/tulip/de4x5.c +++ b/drivers/net/tulip/de4x5.c @@ -474,9 +474,9 @@ #include <asm/byteorder.h> #include <asm/unaligned.h> #include <asm/uaccess.h> -#ifdef CONFIG_PPC +#ifdef CONFIG_PPC_MULTIPLATFORM #include <asm/machdep.h> -#endif /* CONFIG_PPC */ +#endif /* CONFIG_PPC_MULTIPLATFORM */ #include "de4x5.h" @@ -4146,12 +4146,12 @@ get_hw_addr(struct net_device *dev) /* If possible, try to fix a broken card - SMC only so far */ srom_repair(dev, broken); -#ifdef CONFIG_PPC +#ifdef CONFIG_PPC_MULTIPLATFORM /* ** If the address starts with 00 a0, we have to bit-reverse ** each byte of the address. */ - if ( (ppc_md.ppc_machine & _MACH_Pmac) && + if ( (_machine & _MACH_Pmac) && (dev->dev_addr[0] == 0) && (dev->dev_addr[1] == 0xa0) ) { @@ -4163,7 +4163,7 @@ get_hw_addr(struct net_device *dev) dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); } } -#endif /* CONFIG_PPC */ +#endif /* CONFIG_PPC_MULTIPLATFORM */ /* Test for a bad enet address */ status = test_bad_enet(dev, status); diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c index d1524eff34e5..899034164003 100644 --- a/drivers/net/wan/pci200syn.c +++ b/drivers/net/wan/pci200syn.c @@ -451,10 +451,10 @@ static struct pci_device_id pci200_pci_tbl[] __devinitdata = { static struct pci_driver pci200_pci_driver = { - name: "PCI200SYN", - id_table: pci200_pci_tbl, - probe: pci200_pci_init_one, - remove: pci200_pci_remove_one, + .name = "PCI200SYN", + .id_table = pci200_pci_tbl, + .probe = pci200_pci_init_one, + .remove = pci200_pci_remove_one, }; diff --git a/drivers/net/wan/sdlamain.c b/drivers/net/wan/sdlamain.c index 6c95d5f4005f..be10f8c419a7 100644 --- a/drivers/net/wan/sdlamain.c +++ b/drivers/net/wan/sdlamain.c @@ -1160,7 +1160,6 @@ STATIC irqreturn_t sdla_isr (int irq, void* dev_id, struct pt_regs *regs) void wanpipe_open (sdla_t* card) { ++card->open_cnt; - MOD_INC_USE_COUNT; } /*============================================================================ @@ -1172,7 +1171,6 @@ void wanpipe_open (sdla_t* card) void wanpipe_close (sdla_t* card) { --card->open_cnt; - MOD_DEC_USE_COUNT; } /*============================================================================ diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index b67121f78a5f..b7eff809f391 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -826,10 +826,10 @@ static struct pci_device_id wanxl_pci_tbl[] __devinitdata = { static struct pci_driver wanxl_pci_driver = { - name: "wanXL", - id_table: wanxl_pci_tbl, - probe: wanxl_pci_init_one, - remove: wanxl_pci_remove_one, + .name = "wanXL", + .id_table = wanxl_pci_tbl, + .probe = wanxl_pci_init_one, + .remove = wanxl_pci_remove_one, }; diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index b4e699d2e2cf..f569e18e45b9 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1036,8 +1036,8 @@ typedef struct { } WifiCtlHdr; WifiCtlHdr wifictlhdr8023 = { -ctlhdr: { - ctl: HOST_DONT_RLSE, + .ctlhdr = { + .ctl = HOST_DONT_RLSE, } }; diff --git a/drivers/net/zorro8390.c b/drivers/net/zorro8390.c index c3272c259185..455ba34965be 100644 --- a/drivers/net/zorro8390.c +++ b/drivers/net/zorro8390.c @@ -59,9 +59,6 @@ #define WORDSWAP(a) ((((a)>>8)&0xff) | ((a)<<8)) -#ifdef MODULE -static struct net_device *root_zorro8390_dev; -#endif static const struct card_info { zorro_id id; @@ -72,9 +69,11 @@ static const struct card_info { { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, "X-Surf", 0x8600 }, }; -static int __init zorro8390_probe(void); -static int __init zorro8390_init(struct net_device *dev, unsigned long board, - const char *name, unsigned long ioaddr); +static int __devinit zorro8390_init_one(struct zorro_dev *z, + const struct zorro_device_id *ent); +static int __devinit zorro8390_init(struct net_device *dev, + unsigned long board, const char *name, + unsigned long ioaddr); static int zorro8390_open(struct net_device *dev); static int zorro8390_close(struct net_device *dev); static void zorro8390_reset_8390(struct net_device *dev); @@ -85,48 +84,54 @@ static void zorro8390_block_input(struct net_device *dev, int count, static void zorro8390_block_output(struct net_device *dev, const int count, const unsigned char *buf, const int start_page); -static void __exit zorro8390_cleanup(void); +static void __devexit zorro8390_remove_one(struct zorro_dev *z); + +static struct zorro_device_id zorro8390_zorro_tbl[] __devinitdata = { + { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, }, + { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, }, + { 0 } +}; -static int __init zorro8390_probe(void) +static struct zorro_driver zorro8390_driver = { + .name = "zorro8390", + .id_table = zorro8390_zorro_tbl, + .probe = zorro8390_init_one, + .remove = __devexit_p(zorro8390_remove_one), +}; + +static int __devinit zorro8390_init_one(struct zorro_dev *z, + const struct zorro_device_id *ent) { struct net_device *dev; - struct zorro_dev *z = NULL; unsigned long board, ioaddr; - int err = -ENODEV; - int i; + int err, i; - while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { - for (i = ARRAY_SIZE(cards)-1; i >= 0; i--) - if (z->id == cards[i].id) - break; - if (i < 0) - continue; - board = z->resource.start; - ioaddr = board+cards[i].offset; - dev = alloc_ei_netdev(); - if (!dev) - return -ENOMEM; - SET_MODULE_OWNER(dev); - if (!request_mem_region(ioaddr, NE_IO_EXTENT*2, dev->name)) { - free_netdev(dev); - continue; - } - if ((err = zorro8390_init(dev, board, cards[i].name, - ZTWO_VADDR(ioaddr)))) { - release_mem_region(ioaddr, NE_IO_EXTENT*2); - free_netdev(dev); - return err; - } - err = 0; + for (i = ARRAY_SIZE(cards)-1; i >= 0; i--) + if (z->id == cards[i].id) + break; + board = z->resource.start; + ioaddr = board+cards[i].offset; + dev = alloc_ei_netdev(); + if (!dev) + return -ENOMEM; + SET_MODULE_OWNER(dev); + if (!request_mem_region(ioaddr, NE_IO_EXTENT*2, dev->name)) { + free_netdev(dev); + return -EBUSY; } - - if (err == -ENODEV) - printk("No Ariadne II or X-Surf ethernet card found.\n"); - return err; + if ((err = zorro8390_init(dev, board, cards[i].name, + ZTWO_VADDR(ioaddr)))) { + release_mem_region(ioaddr, NE_IO_EXTENT*2); + free_netdev(dev); + return err; + } + zorro_set_drvdata(z, dev); + return 0; } -static int __init zorro8390_init(struct net_device *dev, unsigned long board, - const char *name, unsigned long ioaddr) +static int __devinit zorro8390_init(struct net_device *dev, + unsigned long board, const char *name, + unsigned long ioaddr) { int i; int err; @@ -222,10 +227,6 @@ static int __init zorro8390_init(struct net_device *dev, unsigned long board, ei_status.reg_offset = zorro8390_offsets; dev->open = &zorro8390_open; dev->stop = &zorro8390_close; -#ifdef MODULE - ei_status.priv = (unsigned long)root_zorro8390_dev; - root_zorro8390_dev = dev; -#endif NS8390_init(dev, 0); err = register_netdev(dev); if (err) @@ -401,23 +402,27 @@ static void zorro8390_block_output(struct net_device *dev, int count, return; } -static void __exit zorro8390_cleanup(void) +static void __devexit zorro8390_remove_one(struct zorro_dev *z) { -#ifdef MODULE - struct net_device *dev, *next; + struct net_device *dev = zorro_get_drvdata(z); - while ((dev = root_zorro8390_dev)) { - next = (struct net_device *)(ei_status.priv); - unregister_netdev(dev); - free_irq(IRQ_AMIGA_PORTS, dev); - release_mem_region(ZTWO_PADDR(dev->base_addr), NE_IO_EXTENT*2); - free_netdev(dev); - root_zorro8390_dev = next; - } -#endif + unregister_netdev(dev); + free_irq(IRQ_AMIGA_PORTS, dev); + release_mem_region(ZTWO_PADDR(dev->base_addr), NE_IO_EXTENT*2); + free_netdev(dev); +} + +static int __init zorro8390_init_module(void) +{ + return zorro_module_init(&zorro8390_driver); +} + +static void __exit zorro8390_cleanup_module(void) +{ + zorro_unregister_driver(&zorro8390_driver); } -module_init(zorro8390_probe); -module_exit(zorro8390_cleanup); +module_init(zorro8390_init_module); +module_exit(zorro8390_cleanup_module); MODULE_LICENSE("GPL"); diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 30f1791e2ff6..e707bb81fade 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -212,5 +212,13 @@ config HOTPLUG_PCI_RPA_DLPAR When in doubt, say N. +config HOTPLUG_PCI_SGI + tristate "SGI PCI Hotplug Support" + depends on HOTPLUG_PCI && IA64_SGI_SN2 + help + Say Y here if you have an SGI IA64 Altix system. + + When in doubt, say N. + endmenu diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig index 08484fdefd0b..2917d483a80f 100644 --- a/drivers/s390/block/Kconfig +++ b/drivers/s390/block/Kconfig @@ -11,6 +11,11 @@ config BLK_DEV_XPRAM This option is also available as a module which will be called xpram. If unsure, say "N". +config DCSSBLK + tristate "DCSSBLK support" + help + Support for dcss block device + config DASD tristate "Support for DASD devices" depends on CCW @@ -47,3 +52,13 @@ config DASD_DIAG Select this option if you want to use CMS reserved Disks under VM with the Diagnose250 command. If you are not running under VM or unsure what it is, say "N". + +config DASD_CMB + tristate "Compatibility interface for DASD channel measurement blocks" + depends on DASD + help + This driver provides an additional interface to the channel measurement + facility, which is normally accessed though sysfs, with a set of + ioctl functions specific to the dasd driver. + This is only needed if you want to use applications written for + linux-2.4 dasd channel measurement facility interface. diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile index 9b4729c2cb8d..58c6780134f7 100644 --- a/drivers/s390/block/Makefile +++ b/drivers/s390/block/Makefile @@ -12,4 +12,6 @@ obj-$(CONFIG_DASD) += dasd_mod.o obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o +obj-$(CONFIG_DASD_CMB) += dasd_cmb.o obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o +obj-$(CONFIG_DCSSBLK) += dcssblk.o diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f9f8ac0e3b62..3231be77f9a8 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -7,7 +7,7 @@ * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * - * $Revision: 1.123 $ + * $Revision: 1.129 $ */ #include <linux/config.h> @@ -668,7 +668,7 @@ dasd_check_cqr(struct dasd_ccw_req *cqr) /* * Terminate the current i/o and set the request to failed. - * ccw_device_halt/ccw_device_clear can fail if the i/o subsystem + * ccw_device_clear can fail if the i/o subsystem * is in a bad mood. */ int @@ -684,10 +684,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr) retries = 0; device = (struct dasd_device *) cqr->device; while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) { - if (retries < 2) - rc = ccw_device_halt(device->cdev, (long) cqr); - else - rc = ccw_device_clear(device->cdev, (long) cqr); + rc = ccw_device_clear(device->cdev, (long) cqr); switch (rc) { case 0: /* termination successful */ cqr->status = DASD_CQR_FAILED; @@ -736,6 +733,7 @@ dasd_start_IO(struct dasd_ccw_req * cqr) return rc; device = (struct dasd_device *) cqr->device; cqr->startclk = get_clock(); + cqr->starttime = jiffies; rc = ccw_device_start(device->cdev, cqr->cpaddr, (long) cqr, cqr->lpm, 0); switch (rc) { @@ -788,14 +786,11 @@ dasd_timeout_device(unsigned long ptr) } /* - * Setup timeout for a device. + * Setup timeout for a device in jiffies. */ void dasd_set_timer(struct dasd_device *device, int expires) { - /* FIXME: timeouts are based on jiffies but the timeout - * comparision in __dasd_check_expire is based on the - * TOD clock. */ if (expires == 0) { if (timer_pending(&device->timer)) del_timer(&device->timer); @@ -1002,8 +997,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, "no memory for dstat...ignoring"); #ifdef ERP_DEBUG /* dump sense data */ - if (device->discipline && device->discipline->dump_sense) - device->discipline->dump_sense(device, cqr); + dasd_log_sense(cqr, irb); #endif switch (era) { case dasd_era_fatal: @@ -1079,8 +1073,11 @@ restart: cqr->status = DASD_CQR_FAILED; cqr->stopclk = get_clock(); } else { - erp_fn = device->discipline->erp_action(cqr); - erp_fn(cqr); + if (cqr->dstat->esw.esw0.erw.cons) { + erp_fn = device->discipline->erp_action(cqr); + erp_fn(cqr); + } else + dasd_default_erp_action(cqr); } goto restart; } @@ -1196,7 +1193,7 @@ __dasd_check_expire(struct dasd_device * device) cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); if (cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) { now = get_clock(); - if (cqr->expires * (TOD_SEC / HZ) + cqr->startclk < now) { + if (time_after_eq(jiffies, cqr->expires + cqr->starttime)) { if (device->discipline->term_IO(cqr) != 0) /* Hmpf, try again in 1/100 sec */ dasd_set_timer(device, 1); @@ -1476,6 +1473,7 @@ _dasd_term_running_cqr(struct dasd_device *device) /* termination successful */ cqr->status = DASD_CQR_QUEUED; cqr->startclk = cqr->stopclk = 0; + cqr->starttime = 0; } return rc; } @@ -1782,9 +1780,19 @@ dasd_generic_set_online (struct ccw_device *cdev, if (IS_ERR(device)) return PTR_ERR(device); - if (device->use_diag_flag) + if (device->use_diag_flag) { + if (!dasd_diag_discipline_pointer) { + printk (KERN_WARNING + "dasd_generic couldn't online device %s " + "- discipline DIAG not available\n", + cdev->dev.bus_id); + dasd_delete_device(device); + return -ENODEV; + } discipline = dasd_diag_discipline_pointer; + } device->discipline = discipline; + rc = discipline->check_device(device); if (rc) { printk (KERN_WARNING @@ -1980,6 +1988,7 @@ EXPORT_SYMBOL(dasd_term_IO); EXPORT_SYMBOL_GPL(dasd_generic_probe); EXPORT_SYMBOL_GPL(dasd_generic_remove); +EXPORT_SYMBOL_GPL(dasd_generic_notify); EXPORT_SYMBOL_GPL(dasd_generic_set_online); EXPORT_SYMBOL_GPL(dasd_generic_set_offline); EXPORT_SYMBOL_GPL(dasd_generic_auto_online); diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index d82f9b4a732e..711f1c17207e 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -5,7 +5,7 @@ * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001 * - * $Revision: 1.26 $ + * $Revision: 1.27 $ */ #include <linux/timer.h> @@ -2129,13 +2129,10 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) /* single program action codes (byte25 bit 0 == '0') */ switch (sense[25]) { - case 0x00: /* success */ - DEV_MESSAGE(KERN_DEBUG, device, - "ERP called for successful request %p" - " - NO ERP necessary", erp); - - erp = dasd_3990_erp_cleanup(erp, DASD_CQR_DONE); - + case 0x00: /* success - use default ERP for retries */ + DEV_MESSAGE(KERN_DEBUG, device, "%s", + "ERP called for successful request" + " - just retry"); break; case 0x01: /* fatal error */ diff --git a/drivers/s390/block/dasd_cmb.c b/drivers/s390/block/dasd_cmb.c new file mode 100644 index 000000000000..8139ffddc89e --- /dev/null +++ b/drivers/s390/block/dasd_cmb.c @@ -0,0 +1,145 @@ +/* + * linux/drivers/s390/block/dasd_cmb.c ($Revision: 1.6 $) + * + * Linux on zSeries Channel Measurement Facility support + * (dasd device driver interface) + * + * Copyright 2000,2003 IBM Corporation + * + * Author: Arnd Bergmann <arndb@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/init.h> +#include <linux/ioctl32.h> +#include <linux/module.h> +#include <asm/ccwdev.h> +#include <asm/cmb.h> + +#include "dasd_int.h" + +static int +dasd_ioctl_cmf_enable(struct block_device *bdev, int no, long args) +{ + struct dasd_device *device; + + device = bdev->bd_disk->private_data; + if (!device) + return -EINVAL; + + return enable_cmf(device->cdev); +} + +static int +dasd_ioctl_cmf_disable(struct block_device *bdev, int no, long args) +{ + struct dasd_device *device; + + device = bdev->bd_disk->private_data; + if (!device) + return -EINVAL; + + return disable_cmf(device->cdev); +} + +static int +dasd_ioctl_readall_cmb(struct block_device *bdev, int no, long args) +{ + struct dasd_device *device; + struct cmbdata * __user udata; + struct cmbdata data; + size_t size; + int ret; + + device = bdev->bd_disk->private_data; + if (!device) + return -EINVAL; + udata = (void *) args; + size = _IOC_SIZE(no); + + if (!access_ok(VERIFY_WRITE, udata, size)) + return -EFAULT; + ret = cmf_readall(device->cdev, &data); + if (ret) + return ret; + if (copy_to_user(udata, &data, min(size, sizeof(*udata)))) + return -EFAULT; + return 0; +} + +/* module initialization below here. dasd already provides a mechanism + * to dynamically register ioctl functions, so we simply use this. */ +static inline int +ioctl_reg(unsigned int no, dasd_ioctl_fn_t handler) +{ + int ret; + ret = dasd_ioctl_no_register(THIS_MODULE, no, handler); +#ifdef CONFIG_COMPAT + if (ret) + return ret; + + ret = register_ioctl32_conversion(no, NULL); + if (ret) + dasd_ioctl_no_unregister(THIS_MODULE, no, handler); +#endif + return ret; +} + +static inline void +ioctl_unreg(unsigned int no, dasd_ioctl_fn_t handler) +{ + dasd_ioctl_no_unregister(THIS_MODULE, no, handler); +#ifdef CONFIG_COMPAT + unregister_ioctl32_conversion(no); +#endif + +} + +static void +dasd_cmf_exit(void) +{ + ioctl_unreg(BIODASDCMFENABLE, dasd_ioctl_cmf_enable); + ioctl_unreg(BIODASDCMFDISABLE, dasd_ioctl_cmf_disable); + ioctl_unreg(BIODASDREADALLCMB, dasd_ioctl_readall_cmb); +} + +static int __init +dasd_cmf_init(void) +{ + int ret; + ret = ioctl_reg (BIODASDCMFENABLE, dasd_ioctl_cmf_enable); + if (ret) + goto err; + ret = ioctl_reg (BIODASDCMFDISABLE, dasd_ioctl_cmf_disable); + if (ret) + goto err; + ret = ioctl_reg (BIODASDREADALLCMB, dasd_ioctl_readall_cmb); + if (ret) + goto err; + + return 0; +err: + dasd_cmf_exit(); + + return ret; +} + +module_init(dasd_cmf_init); +module_exit(dasd_cmf_exit); + +MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("channel measurement facility interface for dasd\n" + "Copyright 2003 IBM Corporation\n"); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 5d1bb98b6a0a..2e42a21d8a02 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -7,7 +7,7 @@ * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.49 $ + * $Revision: 1.50 $ */ #include <linux/config.h> @@ -1420,6 +1420,9 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, "Exception class %x\n", irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); } + } else { + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + "SORRY - NO VALID SENSE AVAILABLE\n"); } MESSAGE(KERN_ERR, "Sense data:\n%s", page); diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c index cefc8713d8a8..62099f5bf582 100644 --- a/drivers/s390/block/dasd_genhd.c +++ b/drivers/s390/block/dasd_genhd.c @@ -9,7 +9,7 @@ * * gendisk related functions for the dasd driver. * - * $Revision: 1.42 $ + * $Revision: 1.44 $ */ #include <linux/config.h> @@ -31,6 +31,7 @@ int dasd_gendisk_alloc(struct dasd_device *device) { struct gendisk *gdp; + int len; /* Make sure the minor for this device exists. */ if (device->devindex >= DASD_PER_MAJOR) @@ -46,8 +47,28 @@ dasd_gendisk_alloc(struct dasd_device *device) gdp->fops = &dasd_device_operations; gdp->driverfs_dev = &device->cdev->dev; - /* Set device name */ - sprintf(gdp->disk_name, "dasd_%s_", device->cdev->dev.bus_id); + /* + * Set device name. + * dasda - dasdz : 26 devices + * dasdaa - dasdzz : 676 devices, added up = 702 + * dasdaaa - dasdzzz : 17576 devices, added up = 18278 + * dasdaaaa - dasdzzzz : 456976 devices, added up = 475252 + */ + len = sprintf(gdp->disk_name, "dasd"); + if (device->devindex > 25) { + if (device->devindex > 701) { + if (device->devindex > 18277) + len += sprintf(gdp->disk_name + len, "%c", + 'a'+(((device->devindex-18278) + /17576)%26)); + len += sprintf(gdp->disk_name + len, "%c", + 'a'+(((device->devindex-702)/676)%26)); + } + len += sprintf(gdp->disk_name + len, "%c", + 'a'+(((device->devindex-26)/26)%26)); + } + len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26)); + sprintf(gdp->devfs_name, "dasd/%s", device->cdev->dev.bus_id); if (device->ro_flag) diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 72855e135815..a16438139405 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -6,7 +6,7 @@ * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.52 $ + * $Revision: 1.54 $ */ #ifndef DASD_INT_H @@ -14,6 +14,10 @@ #ifdef __KERNEL__ +/* erp debugging in dasd.c and dasd_3990_erp.c */ +#define ERP_DEBUG + + /* we keep old device allocation scheme; IOW, minors are still in 0..255 */ #define DASD_PER_MAJOR (1U << (MINORBITS - DASD_PARTN_BITS)) #define DASD_PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) @@ -157,6 +161,7 @@ struct dasd_ccw_req { short retries; /* A retry counter */ /* ... and how */ + unsigned long starttime; /* jiffies time of request start */ int expires; /* expiration period in jiffies */ char lpm; /* logical path mask */ void *data; /* pointer to data area */ @@ -166,6 +171,7 @@ struct dasd_ccw_req { struct dasd_ccw_req *refers; /* ERP-chain queueing. */ void *function; /* originating ERP action */ + /* these are for statistics only */ unsigned long long buildclk; /* TOD-clock of request generation */ unsigned long long startclk; /* TOD-clock of request start */ unsigned long long stopclk; /* TOD-clock of request interrupt */ diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index fe47dbeffd87..753bb94839e1 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -147,6 +147,7 @@ dasd_ioctl_enable(struct block_device *bdev, int no, long args) /* * Disable device. + * Used by dasdfmt. Disable I/O operations but allow ioctls. */ static int dasd_ioctl_disable(struct block_device *bdev, int no, long args) @@ -167,6 +168,13 @@ dasd_ioctl_disable(struct block_device *bdev, int no, long args) * device is DASD_STATE_BASIC that allows to do basic i/o. */ dasd_set_target_state(device, DASD_STATE_BASIC); + /* + * Set i_size to zero, since read, write, etc. check against this + * value. + */ + down(&bdev->bd_sem); + i_size_write(bdev->bd_inode, 0); + up(&bdev->bd_sem); return 0; } @@ -237,9 +245,9 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) if (device->discipline->format_device == NULL) return -EPERM; - if (atomic_read(&device->open_count) > 1) { + if (device->state != DASD_STATE_BASIC) { DEV_MESSAGE(KERN_WARNING, device, "%s", - "dasd_format: device is open! "); + "dasd_format: device is not disabled! "); return -EBUSY; } @@ -248,6 +256,16 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata) fdata->start_unit, fdata->stop_unit, fdata->blksize, fdata->intensity); + /* Since dasdfmt keeps the device open after it was disabled, + * there still exists an inode for this device. We must update i_blkbits, + * otherwise we might get errors when enabling the device later. + */ + if (fdata->start_unit == 0) { + struct block_device *bdev = bdget_disk(device->gdp, 0); + bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize); + bdput(bdev); + } + while (fdata->start_unit <= fdata->stop_unit) { cqr = device->discipline->format_device(device, fdata); if (IS_ERR(cqr)) diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 2ea32dafa915..386379bdc3bf 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -9,7 +9,7 @@ * * /proc interface for the dasd driver. * - * $Revision: 1.24 $ + * $Revision: 1.26 $ */ #include <linux/config.h> @@ -67,10 +67,15 @@ dasd_devices_show(struct seq_file *m, void *v) seq_printf(m, "(none)"); /* Print kdev. */ if (device->gdp) - seq_printf(m, " at (%3d:%7d)", + seq_printf(m, " at (%3d:%6d)", device->gdp->major, device->gdp->first_minor); else - seq_printf(m, " at (???:???????)"); + seq_printf(m, " at (???:??????)"); + /* Print device name. */ + if (device->gdp) + seq_printf(m, " is %-8s", device->gdp->disk_name); + else + seq_printf(m, " is ????????"); /* Print devices features. */ substr = device->ro_flag ? "(ro)" : " "; seq_printf(m, "%4s: ", substr); diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c new file mode 100644 index 000000000000..5b6544bc5a05 --- /dev/null +++ b/drivers/s390/block/dcssblk.c @@ -0,0 +1,705 @@ +/* + * dcssblk.c -- the S/390 block driver for dcss memory + * + * Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer + */ + +#include <linux/module.h> +#include <linux/ctype.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/blkdev.h> +#include <asm/extmem.h> +#include <asm/io.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <asm/ccwdev.h> // for s390_root_dev_(un)register() + +//#define DCSSBLK_DEBUG /* Debug messages on/off */ +#define DCSSBLK_NAME "dcssblk" +#define DCSSBLK_MINORS_PER_DISK 1 + +#ifdef DCSSBLK_DEBUG +#define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x) +#else +#define PRINT_DEBUG(x...) do {} while (0) +#endif +#define PRINT_INFO(x...) printk(KERN_INFO DCSSBLK_NAME " info: " x) +#define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x) +#define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x) + + +static int dcssblk_open(struct inode *inode, struct file *filp); +static int dcssblk_release(struct inode *inode, struct file *filp); +static int dcssblk_make_request(struct request_queue *q, struct bio *bio); + +static int dcssblk_major; +static struct block_device_operations dcssblk_devops = { + .owner = THIS_MODULE, + .open = dcssblk_open, + .release = dcssblk_release, +}; + +static ssize_t dcssblk_add_store(struct device * dev, const char * buf, + size_t count); +static ssize_t dcssblk_remove_store(struct device * dev, const char * buf, + size_t count); +static ssize_t dcssblk_save_store(struct device * dev, const char * buf, + size_t count); +static ssize_t dcssblk_save_show(struct device *dev, char *buf); +static ssize_t dcssblk_shared_store(struct device * dev, const char * buf, + size_t count); +static ssize_t dcssblk_shared_show(struct device *dev, char *buf); + +static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store); +static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store); +static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show, + dcssblk_save_store); +static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show, + dcssblk_shared_store); + +static struct device *dcssblk_root_dev; + +struct dcssblk_dev_info { + struct list_head lh; + struct device dev; + char segment_name[BUS_ID_SIZE]; + atomic_t use_count; + struct gendisk *gd; + unsigned long start; + unsigned long end; + int segment_type; + unsigned char save_pending; + unsigned char is_shared; + struct request_queue *dcssblk_queue; +}; + +static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices); +static rwlock_t dcssblk_devices_lock = RW_LOCK_UNLOCKED; + + +/* + * release function for segment device. + */ +static void +dcssblk_release_segment(struct device *dev) +{ + PRINT_DEBUG("segment release fn called for %s\n", dev->bus_id); + kfree(container_of(dev, struct dcssblk_dev_info, dev)); + module_put(THIS_MODULE); +} + +/* + * get a minor number. needs to be called with + * write_lock(&dcssblk_devices_lock) and the + * device needs to be enqueued before the lock is + * freed. + */ +static inline int +dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info) +{ + int minor, found; + struct dcssblk_dev_info *entry; + + if (dev_info == NULL) + return -EINVAL; + for (minor = 0; minor < (1<<MINORBITS); minor++) { + found = 0; + // test if minor available + list_for_each_entry(entry, &dcssblk_devices, lh) + if (minor == entry->gd->first_minor) + found++; + if (!found) break; // got unused minor + } + if (found) + return -EBUSY; + dev_info->gd->first_minor = minor; + return 0; +} + +/* + * get the struct dcssblk_dev_info from dcssblk_devices + * for the given name. + * read_lock(&dcssblk_devices_lock) must be held. + */ +static struct dcssblk_dev_info * +dcssblk_get_device_by_name(char *name) +{ + struct dcssblk_dev_info *entry; + + list_for_each_entry(entry, &dcssblk_devices, lh) { + if (!strcmp(name, entry->segment_name)) { + return entry; + } + } + return NULL; +} + +/* + * register the device that represents a segment in sysfs, + * also add the attributes for the device + */ +static inline int +dcssblk_register_segment_device(struct device *dev) +{ + int rc; + + rc = device_register(dev); + if (rc) + return rc; + rc = device_create_file(dev, &dev_attr_shared); + if (rc) + goto unregister_dev; + rc = device_create_file(dev, &dev_attr_save); + if (rc) + goto unregister_dev; + return rc; + +unregister_dev: + device_unregister(dev); + return rc; +} + +/* + * device attribute for switching shared/nonshared (exclusive) + * operation (show + store) + */ +static ssize_t +dcssblk_shared_show(struct device *dev, char *buf) +{ + struct dcssblk_dev_info *dev_info; + + dev_info = container_of(dev, struct dcssblk_dev_info, dev); + return sprintf(buf, dev_info->is_shared ? "1\n" : "0\n"); +} + +static ssize_t +dcssblk_shared_store(struct device *dev, const char *inbuf, size_t count) +{ + struct dcssblk_dev_info *dev_info; + int rc; + + if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) { + PRINT_WARN("Invalid value, must be 0 or 1\n"); + return -EINVAL; + } + write_lock(&dcssblk_devices_lock); + dev_info = container_of(dev, struct dcssblk_dev_info, dev); + if (atomic_read(&dev_info->use_count)) { + PRINT_ERR("share: segment %s is busy!\n", + dev_info->segment_name); + write_unlock(&dcssblk_devices_lock); + return -EBUSY; + } + if ((inbuf[0] == '1') && (dev_info->is_shared == 1)) { + PRINT_WARN("Segment %s already loaded in shared mode!\n", + dev_info->segment_name); + write_unlock(&dcssblk_devices_lock); + return count; + } + if ((inbuf[0] == '0') && (dev_info->is_shared == 0)) { + PRINT_WARN("Segment %s already loaded in exclusive mode!\n", + dev_info->segment_name); + write_unlock(&dcssblk_devices_lock); + return count; + } + if (inbuf[0] == '1') { + // reload segment in shared mode + segment_unload(dev_info->segment_name); + rc = segment_load(dev_info->segment_name, SEGMENT_SHARED_RO, + &dev_info->start, &dev_info->end); + if (rc < 0) { + PRINT_ERR("Segment %s not reloaded, rc=%d\n", + dev_info->segment_name, rc); + goto removeseg; + } + dev_info->is_shared = 1; + PRINT_INFO("Segment %s reloaded, shared mode.\n", + dev_info->segment_name); + } else if (inbuf[0] == '0') { + // reload segment in exclusive mode + segment_unload(dev_info->segment_name); + rc = segment_load(dev_info->segment_name, SEGMENT_EXCLUSIVE_RW, + &dev_info->start, &dev_info->end); + if (rc < 0) { + PRINT_ERR("Segment %s not reloaded, rc=%d\n", + dev_info->segment_name, rc); + goto removeseg; + } + dev_info->is_shared = 0; + PRINT_INFO("Segment %s reloaded, exclusive (read-write) mode.\n", + dev_info->segment_name); + } else { + write_unlock(&dcssblk_devices_lock); + PRINT_WARN("Invalid value, must be 0 or 1\n"); + return -EINVAL; + } + dev_info->segment_type = rc; + rc = count; + + switch (dev_info->segment_type) { + case SEGMENT_SHARED_RO: + case SEGMENT_EXCLUSIVE_RO: + set_disk_ro(dev_info->gd, 1); + break; + case SEGMENT_SHARED_RW: + case SEGMENT_EXCLUSIVE_RW: + set_disk_ro(dev_info->gd, 0); + break; + } + if ((inbuf[0] == '1') && + ((dev_info->segment_type == SEGMENT_EXCLUSIVE_RO) || + (dev_info->segment_type == SEGMENT_EXCLUSIVE_RW))) { + PRINT_WARN("Could not get shared copy of segment %s\n", + dev_info->segment_name); + rc = -EPERM; + } + if ((inbuf[0] == '0') && + ((dev_info->segment_type == SEGMENT_SHARED_RO) || + (dev_info->segment_type == SEGMENT_SHARED_RW))) { + PRINT_WARN("Could not get exclusive copy of segment %s\n", + dev_info->segment_name); + rc = -EPERM; + } + write_unlock(&dcssblk_devices_lock); + goto out; + +removeseg: + PRINT_ERR("Could not reload segment %s, removing it now!\n", + dev_info->segment_name); + list_del(&dev_info->lh); + write_unlock(&dcssblk_devices_lock); + + del_gendisk(dev_info->gd); + blk_put_queue(dev_info->dcssblk_queue); + dev_info->gd->queue = NULL; + put_disk(dev_info->gd); + device_unregister(dev); + put_device(dev); +out: + return rc; +} + +/* + * device attribute for save operation on current copy + * of the segment. If the segment is busy, saving will + * become pending until it gets released, which can be + * undone by storing a non-true value to this entry. + * (show + store) + */ +static ssize_t +dcssblk_save_show(struct device *dev, char *buf) +{ + struct dcssblk_dev_info *dev_info; + + dev_info = container_of(dev, struct dcssblk_dev_info, dev); + return sprintf(buf, dev_info->save_pending ? "1\n" : "0\n"); +} + +static ssize_t +dcssblk_save_store(struct device *dev, const char *inbuf, size_t count) +{ + struct dcssblk_dev_info *dev_info; + + if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) { + PRINT_WARN("Invalid value, must be 0 or 1\n"); + return -EINVAL; + } + dev_info = container_of(dev, struct dcssblk_dev_info, dev); + + write_lock(&dcssblk_devices_lock); + if (inbuf[0] == '1') { + if (atomic_read(&dev_info->use_count) == 0) { + // device is idle => we save immediately + PRINT_INFO("Saving segment %s\n", + dev_info->segment_name); + segment_replace(dev_info->segment_name); + } else { + // device is busy => we save it when it becomes + // idle in dcssblk_release + PRINT_INFO("Segment %s is currently busy, it will " + "be saved when it becomes idle...\n", + dev_info->segment_name); + dev_info->save_pending = 1; + } + } else if (inbuf[0] == '0') { + if (dev_info->save_pending) { + // device is busy & the user wants to undo his save + // request + dev_info->save_pending = 0; + PRINT_INFO("Pending save for segment %s deactivated\n", + dev_info->segment_name); + } + } else { + write_unlock(&dcssblk_devices_lock); + PRINT_WARN("Invalid value, must be 0 or 1\n"); + return -EINVAL; + } + write_unlock(&dcssblk_devices_lock); + return count; +} + +/* + * device attribute for adding devices + */ +static ssize_t +dcssblk_add_store(struct device *dev, const char *buf, size_t count) +{ + int rc, i; + struct dcssblk_dev_info *dev_info; + char *local_buf; + unsigned long seg_byte_size; + + dev_info = NULL; + if (dev != dcssblk_root_dev) { + rc = -EINVAL; + goto out_nobuf; + } + local_buf = kmalloc(count + 1, GFP_KERNEL); + if (local_buf == NULL) { + rc = -ENOMEM; + goto out_nobuf; + } + /* + * parse input + */ + for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { + local_buf[i] = toupper(buf[i]); + } + local_buf[i] = '\0'; + if ((i == 0) || (i > 8)) { + rc = -ENAMETOOLONG; + goto out; + } + /* + * already loaded? + */ + read_lock(&dcssblk_devices_lock); + dev_info = dcssblk_get_device_by_name(local_buf); + read_unlock(&dcssblk_devices_lock); + if (dev_info != NULL) { + PRINT_WARN("Segment %s already loaded!\n", local_buf); + rc = -EEXIST; + goto out; + } + /* + * get a struct dcssblk_dev_info + */ + dev_info = kmalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL); + if (dev_info == NULL) { + rc = -ENOMEM; + goto out; + } + memset(dev_info, 0, sizeof(struct dcssblk_dev_info)); + + strcpy(dev_info->segment_name, local_buf); + strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE); + dev_info->dev.release = dcssblk_release_segment; + INIT_LIST_HEAD(&dev_info->lh); + + dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK); + if (dev_info->gd == NULL) { + rc = -ENOMEM; + goto free_dev_info; + } + dev_info->gd->major = dcssblk_major; + dev_info->gd->fops = &dcssblk_devops; + dev_info->dcssblk_queue = blk_alloc_queue(GFP_KERNEL); + dev_info->gd->queue = dev_info->dcssblk_queue; + dev_info->gd->private_data = dev_info; + dev_info->gd->driverfs_dev = &dev_info->dev; + /* + * load the segment + */ + rc = segment_load(local_buf, SEGMENT_SHARED_RO, + &dev_info->start, &dev_info->end); + if (rc < 0) { + PRINT_ERR("Segment %s not loaded, rc=%d\n", local_buf, rc); + goto dealloc_gendisk; + } + seg_byte_size = (dev_info->end - dev_info->start + 1); + set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors + PRINT_INFO("Loaded segment %s from %p to %p, size = %lu Byte, " + "capacity = %lu sectors (512 Byte)\n", local_buf, + (void *) dev_info->start, (void *) dev_info->end, + seg_byte_size, seg_byte_size >> 9); + + dev_info->segment_type = rc; + dev_info->save_pending = 0; + dev_info->is_shared = 1; + dev_info->dev.parent = dcssblk_root_dev; + + /* + * get minor, add to list + */ + write_lock(&dcssblk_devices_lock); + rc = dcssblk_assign_free_minor(dev_info); + if (rc) { + write_unlock(&dcssblk_devices_lock); + PRINT_ERR("No free minor number available! " + "Unloading segment...\n"); + goto unload_seg; + } + sprintf(dev_info->gd->disk_name, "dcssblk%d", + dev_info->gd->first_minor); + list_add_tail(&dev_info->lh, &dcssblk_devices); + /* + * register the device + */ + rc = dcssblk_register_segment_device(&dev_info->dev); + if (rc) { + PRINT_ERR("Segment %s could not be registered RC=%d\n", + local_buf, rc); + goto list_del; + } + + if (!try_module_get(THIS_MODULE)) { + rc = -ENODEV; + goto list_del; + } + + get_device(&dev_info->dev); + add_disk(dev_info->gd); + + blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); + blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); + + switch (dev_info->segment_type) { + case SEGMENT_SHARED_RO: + case SEGMENT_EXCLUSIVE_RO: + set_disk_ro(dev_info->gd,1); + break; + case SEGMENT_SHARED_RW: + case SEGMENT_EXCLUSIVE_RW: + set_disk_ro(dev_info->gd,0); + break; + } + PRINT_DEBUG("Segment %s loaded successfully\n", local_buf); + write_unlock(&dcssblk_devices_lock); + rc = count; + goto out; + +list_del: + list_del(&dev_info->lh); + write_unlock(&dcssblk_devices_lock); +unload_seg: + segment_unload(local_buf); +dealloc_gendisk: + blk_put_queue(dev_info->dcssblk_queue); + dev_info->gd->queue = NULL; + put_disk(dev_info->gd); +free_dev_info: + kfree(dev_info); +out: + kfree(local_buf); +out_nobuf: + return rc; +} + +/* + * device attribute for removing devices + */ +static ssize_t +dcssblk_remove_store(struct device *dev, const char *buf, size_t count) +{ + struct dcssblk_dev_info *dev_info; + int rc, i; + char *local_buf; + + if (dev != dcssblk_root_dev) { + return -EINVAL; + } + local_buf = kmalloc(count + 1, GFP_KERNEL); + if (local_buf == NULL) { + return -ENOMEM; + } + /* + * parse input + */ + for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) { + local_buf[i] = toupper(buf[i]); + } + local_buf[i] = '\0'; + if ((i == 0) || (i > 8)) { + rc = -ENAMETOOLONG; + goto out_buf; + } + + write_lock(&dcssblk_devices_lock); + dev_info = dcssblk_get_device_by_name(local_buf); + if (dev_info == NULL) { + write_unlock(&dcssblk_devices_lock); + PRINT_WARN("Segment %s is not loaded!\n", local_buf); + rc = -ENODEV; + goto out_buf; + } + if (atomic_read(&dev_info->use_count) != 0) { + write_unlock(&dcssblk_devices_lock); + PRINT_WARN("Segment %s is in use!\n", local_buf); + rc = -EBUSY; + goto out_buf; + } + list_del(&dev_info->lh); + write_unlock(&dcssblk_devices_lock); + + del_gendisk(dev_info->gd); + blk_put_queue(dev_info->dcssblk_queue); + dev_info->gd->queue = NULL; + put_disk(dev_info->gd); + device_unregister(&dev_info->dev); + put_device(&dev_info->dev); + segment_unload(dev_info->segment_name); + PRINT_DEBUG("Segment %s unloaded successfully\n", + dev_info->segment_name); + rc = count; +out_buf: + kfree(local_buf); + return rc; +} + +static int +dcssblk_open(struct inode *inode, struct file *filp) +{ + struct dcssblk_dev_info *dev_info; + int rc; + + dev_info = inode->i_bdev->bd_disk->private_data; + if (NULL == dev_info) { + rc = -ENODEV; + goto out; + } + atomic_inc(&dev_info->use_count); + inode->i_bdev->bd_block_size = 4096; + rc = 0; +out: + return rc; +} + +static int +dcssblk_release(struct inode *inode, struct file *filp) +{ + struct dcssblk_dev_info *dev_info; + int rc; + + dev_info = inode->i_bdev->bd_disk->private_data; + if (NULL == dev_info) { + rc = -ENODEV; + goto out; + } + write_lock(&dcssblk_devices_lock); + if (atomic_dec_and_test(&dev_info->use_count) + && (dev_info->save_pending)) { + PRINT_INFO("Segment %s became idle and is being saved now\n", + dev_info->segment_name); + segment_replace(dev_info->segment_name); + dev_info->save_pending = 0; + } + write_unlock(&dcssblk_devices_lock); + rc = 0; +out: + return rc; +} + +static int +dcssblk_make_request(request_queue_t *q, struct bio *bio) +{ + struct dcssblk_dev_info *dev_info; + struct bio_vec *bvec; + unsigned long index; + unsigned long page_addr; + unsigned long source_addr; + unsigned long bytes_done; + int i; + + bytes_done = 0; + dev_info = bio->bi_bdev->bd_disk->private_data; + if (dev_info == NULL) + goto fail; + if ((bio->bi_sector & 3) != 0 || (bio->bi_size & 4095) != 0) + /* Request is not page-aligned. */ + goto fail; + if (((bio->bi_size >> 9) + bio->bi_sector) + > get_capacity(bio->bi_bdev->bd_disk)) { + /* Request beyond end of DCSS segment. */ + goto fail; + } + index = (bio->bi_sector >> 3); + bio_for_each_segment(bvec, bio, i) { + page_addr = (unsigned long) + page_address(bvec->bv_page) + bvec->bv_offset; + source_addr = dev_info->start + (index<<12) + bytes_done; + if (unlikely(page_addr & 4095) != 0 || (bvec->bv_len & 4095) != 0) + // More paranoia. + goto fail; + if (bio_data_dir(bio) == READ) { + memcpy((void*)page_addr, (void*)source_addr, + bvec->bv_len); + } else { + memcpy((void*)source_addr, (void*)page_addr, + bvec->bv_len); + } + bytes_done += bvec->bv_len; + } + bio_endio(bio, bytes_done, 0); + return 0; +fail: + bio_io_error(bio, bytes_done); + return 0; +} + +/* + * The init/exit functions. + */ +static void __exit +dcssblk_exit(void) +{ + int rc; + + PRINT_DEBUG("DCSSBLOCK EXIT...\n"); + s390_root_dev_unregister(dcssblk_root_dev); + rc = unregister_blkdev(dcssblk_major, DCSSBLK_NAME); + if (rc) { + PRINT_ERR("unregister_blkdev() failed!\n"); + } + PRINT_DEBUG("...finished!\n"); +} + +static int __init +dcssblk_init(void) +{ + int rc; + + PRINT_DEBUG("DCSSBLOCK INIT...\n"); + dcssblk_root_dev = s390_root_dev_register("dcssblk"); + if (IS_ERR(dcssblk_root_dev)) { + PRINT_ERR("device_register() failed!\n"); + return PTR_ERR(dcssblk_root_dev); + } + rc = device_create_file(dcssblk_root_dev, &dev_attr_add); + if (rc) { + PRINT_ERR("device_create_file(add) failed!\n"); + s390_root_dev_unregister(dcssblk_root_dev); + return rc; + } + rc = device_create_file(dcssblk_root_dev, &dev_attr_remove); + if (rc) { + PRINT_ERR("device_create_file(remove) failed!\n"); + s390_root_dev_unregister(dcssblk_root_dev); + return rc; + } + rc = register_blkdev(0, DCSSBLK_NAME); + if (rc < 0) { + PRINT_ERR("Can't get dynamic major!\n"); + s390_root_dev_unregister(dcssblk_root_dev); + return rc; + } + dcssblk_major = rc; + PRINT_DEBUG("...finished!\n"); + return 0; +} + +module_init(dcssblk_init); +module_exit(dcssblk_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 29bb9bde62d2..0e724c8133ff 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -454,7 +454,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) memcpy(tty->flip.char_buf_ptr, raw->inbuf, count); if (count < 2 || - (strncmp(raw->inbuf+count-2, "^n", 2) || + (strncmp(raw->inbuf+count-2, "^n", 2) && strncmp(raw->inbuf+count-2, "\252n", 2)) ) { /* don't add the auto \n */ tty->flip.char_buf_ptr[count] = '\n'; diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index d9c5911bf4f9..1035707f860c 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -529,8 +529,8 @@ sclp_tty_input(unsigned char* buf, unsigned int count) /* send (normal) input to line discipline */ memcpy(sclp_tty->flip.char_buf_ptr, buf, count); if (count < 2 || - strncmp ((const char *) buf + count - 2, "^n", 2) || - strncmp ((const char *) buf + count - 2, "\0252n", 2)) { + (strncmp ((const char *) buf + count - 2, "^n", 2) && + strncmp ((const char *) buf + count - 2, "\0252n", 2))) { sclp_tty->flip.char_buf_ptr[count] = '\n'; count++; } else @@ -636,7 +636,7 @@ find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id) { struct gds_vector *vec; - for (vec = start; vec < end; (void *) vec += vec->length) + for (vec = start; vec < end; vec = (void *) vec + vec->length) if (vec->gds_id == id) return vec; return NULL; @@ -648,7 +648,8 @@ find_gds_subvector(struct gds_subvector *start, { struct gds_subvector *subvec; - for (subvec = start; subvec < end; (void *) subvec += subvec->length) + for (subvec = start; subvec < end; + subvec = (void *) subvec + subvec->length) if (subvec->key == key) return subvec; return NULL; @@ -667,7 +668,7 @@ sclp_eval_selfdeftextmsg(struct gds_subvector *start, break; sclp_get_input((unsigned char *)(subvec + 1), (unsigned char *) subvec + subvec->length); - (void *) subvec += subvec->length; + subvec = (void *) subvec + subvec->length; } } @@ -685,7 +686,7 @@ sclp_eval_textcmd(struct gds_subvector *start, break; sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1), (void *)subvec + subvec->length); - (void *) subvec += subvec->length; + subvec = (void *) subvec + subvec->length; } } @@ -701,7 +702,7 @@ sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end) break; sclp_eval_textcmd((struct gds_subvector *)(vec + 1), (void *) vec + vec->length); - (void *) vec += vec->length; + vec = (void *) vec + vec->length; } } diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 0521e254ead8..32d4397263e0 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -32,9 +32,10 @@ #define SCLP_VT220_MAJOR TTY_MAJOR #define SCLP_VT220_MINOR 65 #define SCLP_VT220_DRIVER_NAME "sclp_vt220" -#define SCLP_VT220_DEVICE_NAME "sclp_vt" +#define SCLP_VT220_DEVICE_NAME "ttysclp" #define SCLP_VT220_CONSOLE_NAME "ttyS" #define SCLP_VT220_CONSOLE_INDEX 1 /* console=ttyS1 */ +#define SCLP_VT220_BUF_SIZE 80 /* Representation of a single write request */ struct sclp_vt220_request { @@ -336,12 +337,11 @@ sclp_vt220_chars_stored(struct sclp_vt220_request *request) /* * Add msg to buffer associated with request. Return the number of characters - * added or -EFAULT on error. + * added. */ static int sclp_vt220_add_msg(struct sclp_vt220_request *request, - const unsigned char *msg, int count, int from_user, - int convertlf) + const unsigned char *msg, int count, int convertlf) { struct sclp_vt220_sccb *sccb; void *buffer; @@ -363,11 +363,7 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request, (from < count) && (to < sclp_vt220_space_left(request)); from++) { /* Retrieve character */ - if (from_user) { - if (get_user(c, msg + from) != 0) - return -EFAULT; - } else - c = msg[from]; + c = msg[from]; /* Perform conversion */ if (c == 0x0a) { if (to + 1 < sclp_vt220_space_left(request)) { @@ -383,12 +379,7 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request, sccb->evbuf.length += to; return from; } else { - if (from_user) { - if (copy_from_user(buffer, (void *) msg, count) != 0) - return -EFAULT; - } - else - memcpy(buffer, (const void *) msg, count); + memcpy(buffer, (const void *) msg, count); sccb->header.length += count; sccb->evbuf.length += count; return count; @@ -408,7 +399,7 @@ sclp_vt220_timeout(unsigned long data) /* * Internal implementation of the write function. Write COUNT bytes of data - * from memory at BUF which may reside in user space (specified by FROM_USER) + * from memory at BUF * to the SCLP interface. In case that the data does not fit into the current * write buffer, emit the current one and allocate a new one. If there are no * more empty buffers available, wait until one gets emptied. If DO_SCHEDULE @@ -419,8 +410,8 @@ sclp_vt220_timeout(unsigned long data) * of bytes written. */ static int -__sclp_vt220_write(int from_user, const unsigned char *buf, int count, - int do_schedule, int convertlf) +__sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, + int convertlf) { unsigned long flags; void *page; @@ -451,10 +442,9 @@ __sclp_vt220_write(int from_user, const unsigned char *buf, int count, } /* Try to write the string to the current request buffer */ written = sclp_vt220_add_msg(sclp_vt220_current_request, - buf, count, from_user, convertlf); - if (written > 0) - overall_written += written; - if (written == -EFAULT || written == count) + buf, count, convertlf); + overall_written += written; + if (written == count) break; /* * Not all characters could be written to the current @@ -489,7 +479,29 @@ static int sclp_vt220_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) { - return __sclp_vt220_write(from_user, buf, count, 1, 0); + int length; + int ret; + + if (!from_user) + return __sclp_vt220_write(buf, count, 1, 0); + /* Use intermediate buffer to prevent calling copy_from_user() while + * holding a lock. */ + ret = 0; + while (count > 0) { + length = count < SCLP_VT220_BUF_SIZE ? + count : SCLP_VT220_BUF_SIZE; + length -= copy_from_user(tty->driver_data, buf, length); + if (length == 0) { + if (!ret) + return -EFAULT; + break; + } + length = __sclp_vt220_write(tty->driver_data, length, 1, 0); + buf += length; + count -= length; + ret += length; + } + return ret; } #define SCLP_VT220_SESSION_ENDED 0x01 @@ -541,9 +553,13 @@ sclp_vt220_receiver_fn(struct evbuf_header *evbuf) static int sclp_vt220_open(struct tty_struct *tty, struct file *filp) { - sclp_vt220_tty = tty; - tty->driver_data = NULL; - tty->low_latency = 0; + if (tty->count == 1) { + sclp_vt220_tty = tty; + tty->driver_data = kmalloc(SCLP_VT220_BUF_SIZE, GFP_KERNEL); + if (tty->driver_data == NULL) + return -ENOMEM; + tty->low_latency = 0; + } return 0; } @@ -553,9 +569,11 @@ sclp_vt220_open(struct tty_struct *tty, struct file *filp) static void sclp_vt220_close(struct tty_struct *tty, struct file *filp) { - if (tty->count > 1) - return; - sclp_vt220_tty = NULL; + if (tty->count == 1) { + sclp_vt220_tty = NULL; + kfree(tty->driver_data); + tty->driver_data = NULL; + } } /* @@ -571,7 +589,7 @@ sclp_vt220_close(struct tty_struct *tty, struct file *filp) static void sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch) { - __sclp_vt220_write(0, &ch, 1, 0, 0); + __sclp_vt220_write(&ch, 1, 0, 0); } /* @@ -765,7 +783,7 @@ module_init(sclp_vt220_tty_init); static void sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count) { - __sclp_vt220_write(0, (const unsigned char *) buf, count, 1, 1); + __sclp_vt220_write((const unsigned char *) buf, count, 1, 1); } static struct tty_driver * diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index c0ed46dbe0a4..321ed179ca82 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -5,6 +5,6 @@ obj-y += airq.o blacklist.o chsc.o cio.o css.o requestirq.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o -obj-y += ccw_device.o +obj-y += ccw_device.o cmf.o obj-$(CONFIG_CCWGROUP) += ccwgroup.o obj-$(CONFIG_QDIO) += qdio.o diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index f45f5220b84b..6cfda026d074 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/blacklist.c * S/390 common I/O routines -- blacklisting of specific devices - * $Revision: 1.27 $ + * $Revision: 1.29 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -18,9 +18,11 @@ #include <linux/ctype.h> #include <linux/device.h> +#include <asm/cio.h> #include <asm/uaccess.h> #include "blacklist.h" +#include "cio.h" #include "cio_debug.h" #include "css.h" @@ -199,8 +201,6 @@ is_blacklisted (int devno) } #ifdef CONFIG_PROC_FS - -extern void css_reiterate_subchannels(void); /* * Function: s390_redo_validation * Look for no longer blacklisted devices @@ -208,9 +208,29 @@ extern void css_reiterate_subchannels(void); static inline void s390_redo_validation (void) { - CIO_TRACE_EVENT (0, "redoval"); + unsigned int irq; - css_reiterate_subchannels(); + CIO_TRACE_EVENT (0, "redoval"); + for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) { + int ret; + struct subchannel *sch; + + sch = get_subchannel_by_schid(irq); + if (sch) { + /* Already known. */ + put_device(&sch->dev); + continue; + } + ret = css_probe_device(irq); + if (ret == -ENXIO) + break; /* We're through. */ + if (ret == -ENOMEM) + /* + * Stop validation for now. Bad, but no need for a + * panic. + */ + break; + } } /* diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index cb6e699cf00c..83b416c06ab9 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/ccwgroup.c * bus driver for ccwgroup - * $Revision: 1.19 $ + * $Revision: 1.23 $ * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -164,6 +164,7 @@ ccwgroup_create(struct device *root, return -ENOMEM; memset(gdev, 0, sizeof(*gdev) + argc*sizeof(gdev->cdev[0])); + atomic_set(&gdev->onoff, 0); for (i = 0; i < argc; i++) { gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); @@ -242,18 +243,24 @@ ccwgroup_set_online(struct ccwgroup_device *gdev) struct ccwgroup_driver *gdrv; int ret; - if (gdev->state == CCWGROUP_ONLINE) - return 0; - - if (!gdev->dev.driver) - return -EINVAL; - + if (atomic_compare_and_swap(0, 1, &gdev->onoff)) + return -EAGAIN; + if (gdev->state == CCWGROUP_ONLINE) { + ret = 0; + goto out; + } + if (!gdev->dev.driver) { + ret = -EINVAL; + goto out; + } gdrv = to_ccwgroupdrv (gdev->dev.driver); if ((ret = gdrv->set_online(gdev))) - return ret; + goto out; gdev->state = CCWGROUP_ONLINE; - return 0; + out: + atomic_set(&gdev->onoff, 0); + return ret; } static int @@ -262,18 +269,24 @@ ccwgroup_set_offline(struct ccwgroup_device *gdev) struct ccwgroup_driver *gdrv; int ret; - if (gdev->state == CCWGROUP_OFFLINE) - return 0; - - if (!gdev->dev.driver) - return -EINVAL; - + if (atomic_compare_and_swap(0, 1, &gdev->onoff)) + return -EAGAIN; + if (gdev->state == CCWGROUP_OFFLINE) { + ret = 0; + goto out; + } + if (!gdev->dev.driver) { + ret = -EINVAL; + goto out; + } gdrv = to_ccwgroupdrv (gdev->dev.driver); if ((ret = gdrv->set_offline(gdev))) - return ret; + goto out; gdev->state = CCWGROUP_OFFLINE; - return 0; + out: + atomic_set(&gdev->onoff, 0); + return ret; } static ssize_t @@ -324,7 +337,7 @@ ccwgroup_probe (struct device *dev) if ((ret = device_create_file(dev, &dev_attr_online))) return ret; - pr_debug("%s: device %s\n", __func__, gdev->dev.name); + pr_debug("%s: device %s\n", __func__, gdev->dev.bus_id); ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV; if (ret) device_remove_file(dev, &dev_attr_online); @@ -341,7 +354,7 @@ ccwgroup_remove (struct device *dev) gdev = to_ccwgroupdev(dev); gdrv = to_ccwgroupdrv(dev->driver); - pr_debug("%s: device %s\n", __func__, gdev->dev.name); + pr_debug("%s: device %s\n", __func__, gdev->dev.bus_id); device_remove_file(dev, &dev_attr_online); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 072339236c08..9f5598669c78 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/chsc.c * S/390 common I/O routines -- channel subsystem call - * $Revision: 1.92 $ + * $Revision: 1.105 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -27,30 +27,20 @@ #define CHPID_LONGS (256 / (8 * sizeof(long))) /* 256 chpids */ static struct channel_path *chps[NR_CHPIDS]; -static int new_channel_path(int chpid, int status); +static void *sei_page; -static inline void -set_chp_online(int chp, int onoff) -{ - chps[chp]->state.online = onoff; -} +static int new_channel_path(int chpid); static inline void set_chp_logically_online(int chp, int onoff) { - chps[chp]->state.logically_online = onoff; + chps[chp]->state = onoff; } static int get_chp_status(int chp) { - int ret; - - if (!chps[chp]) - return 0; - ret = chps[chp]->state.online ? CHP_ONLINE : CHP_STANDBY; - return (chps[chp]->state.logically_online ? - ret : ret | CHP_LOGICALLY_OFFLINE); + return (chps[chp] ? chps[chp]->state : -ENODEV); } void @@ -60,13 +50,25 @@ chsc_validate_chpids(struct subchannel *sch) for (chp = 0; chp <= 7; chp++) { mask = 0x80 >> chp; - if (get_chp_status(sch->schib.pmcw.chpid[chp]) - & CHP_LOGICALLY_OFFLINE) + if (!get_chp_status(sch->schib.pmcw.chpid[chp])) /* disable using this path */ sch->opm &= ~mask; } } +void +chpid_is_actually_online(int chp) +{ + int state; + + state = get_chp_status(chp); + if (state < 0) + new_channel_path(chp); + else + WARN_ON(!state); + /* FIXME: should notify other subchannels here */ +} + /* FIXME: this is _always_ called for every subchannel. shouldn't we * process more than one at a time? */ static int @@ -204,8 +206,8 @@ css_get_ssd_info(struct subchannel *sch) /* Allocate channel path structures, if needed. */ for (j = 0; j < 8; j++) { chpid = sch->ssd_info.chpid[j]; - if (chpid && !get_chp_status(chpid)) - new_channel_path(chpid, CHP_ONLINE); + if (chpid && (get_chp_status(chpid) < 0)) + new_channel_path(chpid); } } return ret; @@ -218,6 +220,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) int mask; struct subchannel *sch; __u8 *chpid; + struct schib schib; sch = to_subchannel(dev); chpid = data; @@ -230,7 +233,13 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) mask = 0x80 >> j; spin_lock(&sch->lock); - stsch(sch->irq, &sch->schib); + stsch(sch->irq, &schib); + if (!schib.pmcw.dnv) + goto out_unreg; + memcpy(&sch->schib, &schib, sizeof(struct schib)); + /* Check for single path devices. */ + if (sch->schib.pmcw.pim == 0x80) + goto out_unreg; if (sch->vpm == mask) goto out_unreg; @@ -275,12 +284,9 @@ out_unlock: return 0; out_unreg: spin_unlock(&sch->lock); - if (sch->driver && sch->driver->notify && - sch->driver->notify(&sch->dev, CIO_NO_PATH)) - return 0; - device_unregister(&sch->dev); - sch->schib.pmcw.intparm = 0; - cio_modify(sch); + sch->lpm = 0; + /* We can't block here. */ + device_call_nopath_notify(sch); return 0; } @@ -292,10 +298,8 @@ s390_set_chpid_offline( __u8 chpid) sprintf(dbf_txt, "chpr%x", chpid); CIO_TRACE_EVENT(2, dbf_txt); - if (!get_chp_status(chpid)) - return; /* we didn't know the chpid anyway */ - - set_chp_online(chpid, 0); + if (get_chp_status(chpid) <= 0) + return; bus_for_each_dev(&css_bus_type, NULL, &chpid, s390_subchannel_remove_chpid); @@ -303,16 +307,12 @@ s390_set_chpid_offline( __u8 chpid) static int s390_process_res_acc_sch(u8 chpid, __u16 fla, u32 fla_mask, - struct subchannel *sch, void *page) + struct subchannel *sch) { int found; int chp; int ccode; - /* Update our ssd_info */ - if (chsc_get_sch_desc_irq(sch, page)) - return 0; - found = 0; for (chp = 0; chp <= 7; chp++) /* @@ -340,14 +340,12 @@ s390_process_res_acc_sch(u8 chpid, __u16 fla, u32 fla_mask, return 0x80 >> chp; } -static void +static int s390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask) { struct subchannel *sch; - int irq; - int ret; + int irq, rc; char dbf_txt[15]; - void *page; sprintf(dbf_txt, "accpr%x", chpid); CIO_TRACE_EVENT( 2, dbf_txt); @@ -364,18 +362,17 @@ s390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask) * will we have to do. */ - if (get_chp_status(chpid) & CHP_LOGICALLY_OFFLINE) - return; /* no need to do the rest */ - - page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!page) - return; + if (!get_chp_status(chpid)) + return 0; /* no need to do the rest */ + rc = 0; for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { - int chp_mask; + int chp_mask, old_lpm; sch = get_subchannel_by_schid(irq); if (!sch) { + struct schib schib; + int ret; /* * We don't know the device yet, but since a path * may be available now to the device we'll have @@ -384,18 +381,29 @@ s390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask) * that beast may be on we'll have to do a stsch * on all devices, grr... */ - ret = css_probe_device(irq); - if (ret == -ENXIO) + if (stsch(irq, &schib)) { /* We're through */ + if (need_rescan) + rc = -EAGAIN; break; + } + if (need_rescan) { + rc = -EAGAIN; + continue; + } + /* Put it on the slow path. */ + ret = css_enqueue_subchannel_slow(irq); + if (ret) { + css_clear_subchannel_slow_list(); + need_rescan = 1; + } + rc = -EAGAIN; continue; } spin_lock_irq(&sch->lock); - chp_mask = s390_process_res_acc_sch(chpid, fla, fla_mask, - sch, page); - clear_page(page); + chp_mask = s390_process_res_acc_sch(chpid, fla, fla_mask, sch); if (chp_mask == 0) { @@ -406,21 +414,22 @@ s390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask) else continue; } - + old_lpm = sch->lpm; sch->lpm = ((sch->schib.pmcw.pim & sch->schib.pmcw.pam & sch->schib.pmcw.pom) | chp_mask) & sch->opm; - - if (sch->driver && sch->driver->verify) + spin_unlock_irq(&sch->lock); + if (!old_lpm && sch->lpm) + device_trigger_reprobe(sch); + else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - spin_unlock_irq(&sch->lock); put_device(&sch->dev); if (fla_mask != 0) break; } - free_page((unsigned long)page); + return rc; } static int @@ -453,10 +462,10 @@ __get_chpid_from_lir(void *data) return (u16) (lir->indesc[0]&0x000000ff); } -void +int chsc_process_crw(void) { - int chpid; + int chpid, ret; struct { struct chsc_header request; u32 reserved1; @@ -476,21 +485,20 @@ chsc_process_crw(void) /* ccdf has to be big enough for a link-incident record */ } *sei_area; + if (!sei_page) + return 0; /* * build the chsc request block for store event information * and do the call + * This function is only called by the machine check handler thread, + * so we don't need locking for the sei_page. */ - sei_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - - if (!sei_area) { - CIO_CRW_EVENT(0, "No memory for sei area!\n"); - return; - } + sei_area = sei_page; CIO_TRACE_EVENT( 2, "prcss"); - + ret = 0; do { - int ccode; + int ccode, status; memset(sei_area, 0, sizeof(*sei_area)); sei_area->request = (struct chsc_header) { @@ -500,7 +508,7 @@ chsc_process_crw(void) ccode = chsc(sei_area); if (ccode > 0) - goto out; + return 0; switch (sei_area->response.code) { /* for debug purposes, check for problems */ @@ -511,19 +519,19 @@ chsc_process_crw(void) case 0x0002: CIO_CRW_EVENT(2, "chsc_process_crw: invalid command!\n"); - goto out; + return 0; case 0x0003: CIO_CRW_EVENT(2, "chsc_process_crw: error in chsc " "request block!\n"); - goto out; + return 0; case 0x0005: CIO_CRW_EVENT(2, "chsc_process_crw: no event " "information stored\n"); - goto out; + return 0; default: CIO_CRW_EVENT(2, "chsc_process_crw: chsc response %d\n", sei_area->response.code); - goto out; + return 0; } /* Check if we might have lost some information. */ @@ -561,24 +569,27 @@ chsc_process_crw(void) pr_debug("Validity flags: %x\n", sei_area->vf); /* allocate a new channel path structure, if needed */ - if (chps[sei_area->rsid] == NULL) - new_channel_path(sei_area->rsid, CHP_ONLINE); - else - set_chp_online(sei_area->rsid, 1); - + status = get_chp_status(sei_area->rsid); + if (status < 0) + new_channel_path(sei_area->rsid); + else if (!status) + return 0; if ((sei_area->vf & 0x80) == 0) { pr_debug("chpid: %x\n", sei_area->rsid); - s390_process_res_acc(sei_area->rsid, 0, 0); + ret = s390_process_res_acc(sei_area->rsid, + 0, 0); } else if ((sei_area->vf & 0xc0) == 0x80) { pr_debug("chpid: %x link addr: %x\n", sei_area->rsid, sei_area->fla); - s390_process_res_acc(sei_area->rsid, - sei_area->fla, 0xff00); + ret = s390_process_res_acc(sei_area->rsid, + sei_area->fla, + 0xff00); } else if ((sei_area->vf & 0xc0) == 0xc0) { pr_debug("chpid: %x full link addr: %x\n", sei_area->rsid, sei_area->fla); - s390_process_res_acc(sei_area->rsid, - sei_area->fla, 0xffff); + ret = s390_process_res_acc(sei_area->rsid, + sei_area->fla, + 0xffff); } pr_debug("\n"); @@ -590,33 +601,47 @@ chsc_process_crw(void) break; } } while (sei_area->flags & 0x80); - -out: - free_page((unsigned long)sei_area); + return ret; } -static void +static int chp_add(int chpid) { struct subchannel *sch; - int irq, ret; + int irq, ret, rc; char dbf_txt[15]; - if (get_chp_status(chpid) & CHP_LOGICALLY_OFFLINE) - return; /* no need to do the rest */ + if (!get_chp_status(chpid)) + return 0; /* no need to do the rest */ sprintf(dbf_txt, "cadd%x", chpid); CIO_TRACE_EVENT(2, dbf_txt); + rc = 0; for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { int i; sch = get_subchannel_by_schid(irq); if (!sch) { - ret = css_probe_device(irq); - if (ret == -ENXIO) + struct schib schib; + + if (stsch(irq, &schib)) { /* We're through */ - return; + if (need_rescan) + rc = -EAGAIN; + break; + } + if (need_rescan) { + rc = -EAGAIN; + continue; + } + /* Put it on the slow path. */ + ret = css_enqueue_subchannel_slow(irq); + if (ret) { + css_clear_subchannel_slow_list(); + need_rescan = 1; + } + rc = -EAGAIN; continue; } @@ -626,13 +651,13 @@ chp_add(int chpid) if (stsch(sch->irq, &sch->schib) != 0) { /* Endgame. */ spin_unlock(&sch->lock); - return; + return rc; } break; } if (i==8) { spin_unlock(&sch->lock); - return; + return rc; } sch->lpm = ((sch->schib.pmcw.pim & sch->schib.pmcw.pam & @@ -645,70 +670,80 @@ chp_add(int chpid) spin_unlock(&sch->lock); put_device(&sch->dev); } + return rc; } /* * Handling of crw machine checks with channel path source. */ -void +int chp_process_crw(int chpid, int on) { if (on == 0) { /* Path has gone. We use the link incident routine.*/ s390_set_chpid_offline(chpid); - } else { - /* - * Path has come. Allocate a new channel path structure, - * if needed. - */ - if (chps[chpid] == NULL) - new_channel_path(chpid, CHP_ONLINE); - else - set_chp_online(chpid, 1); - /* Avoid the extra overhead in process_rec_acc. */ - chp_add(chpid); + return 0; /* De-register is async anyway. */ } + /* + * Path has come. Allocate a new channel path structure, + * if needed. + */ + if (get_chp_status(chpid) < 0) + new_channel_path(chpid); + /* Avoid the extra overhead in process_rec_acc. */ + return chp_add(chpid); } -static inline void +static inline int __check_for_io_and_kill(struct subchannel *sch, int index) { int cc; cc = stsch(sch->irq, &sch->schib); if (cc) - return; - if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index)) + return 0; + if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index)) { device_set_waiting(sch); + return 1; + } + return 0; } static inline void __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) { - int chp; + int chp, old_lpm; if (!sch->ssd_info.valid) return; + old_lpm = sch->lpm; for (chp = 0; chp < 8; chp++) { - if (sch->ssd_info.chpid[chp] == chpid) { - if (on) { - sch->opm |= (0x80 >> chp); - sch->lpm |= (0x80 >> chp); - } else { - sch->opm &= ~(0x80 >> chp); - sch->lpm &= ~(0x80 >> chp); - /* - * Give running I/O a grace period in which it - * can successfully terminate, even using the - * just varied off path. Then kill it. - */ - __check_for_io_and_kill(sch, chp); - } - if (sch->driver && sch->driver->verify) + if (sch->ssd_info.chpid[chp] != chpid) + continue; + + if (on) { + sch->opm |= (0x80 >> chp); + sch->lpm |= (0x80 >> chp); + if (!old_lpm) + device_trigger_reprobe(sch); + else if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); + } else { + sch->opm &= ~(0x80 >> chp); + sch->lpm &= ~(0x80 >> chp); + /* + * Give running I/O a grace period in which it + * can successfully terminate, even using the + * just varied off path. Then kill it. + */ + if (!__check_for_io_and_kill(sch, chp) && !sch->lpm) + /* Get over with it now. */ + device_call_nopath_notify(sch); + else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - break; } + break; } } @@ -738,6 +773,11 @@ s390_subchannel_vary_chpid_on(struct device *dev, void *data) return 0; } +extern void css_trigger_slow_path(void); +typedef void (*workfunc)(void *); +static DECLARE_WORK(varyonoff_work, (workfunc)css_trigger_slow_path, + NULL); + /* * Function: s390_vary_chpid * Varies the specified chpid online or offline @@ -746,21 +786,20 @@ static int s390_vary_chpid( __u8 chpid, int on) { char dbf_text[15]; - int status; + int status, irq, ret; + struct subchannel *sch; sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid); CIO_TRACE_EVENT( 2, dbf_text); status = get_chp_status(chpid); - if (!status) { + if (status < 0) { printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid); return -EINVAL; } - if ((on && !(status & CHP_LOGICALLY_OFFLINE)) || - (!on && (status & CHP_LOGICALLY_OFFLINE))) { - printk(KERN_ERR "chpid %x is " - "already %sline\n", chpid, on ? "on" : "off"); + if (!on && !status) { + printk(KERN_ERR "chpid %x is already offline\n", chpid); return -EINVAL; } @@ -773,6 +812,30 @@ s390_vary_chpid( __u8 chpid, int on) bus_for_each_dev(&css_bus_type, NULL, &chpid, on ? s390_subchannel_vary_chpid_on : s390_subchannel_vary_chpid_off); + if (!on) + return 0; + /* Scan for new devices on varied on path. */ + for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { + struct schib schib; + + sch = get_subchannel_by_schid(irq); + if (sch) + continue; + if (stsch(irq, &schib)) + /* We're through */ + break; + if (need_rescan) + continue; + /* Put it on the slow path. */ + ret = css_enqueue_subchannel_slow(irq); + if (ret) { + css_clear_subchannel_slow_list(); + need_rescan = 1; + } + continue; + } + if (need_rescan || css_slow_subchannels_exist()) + schedule_work(&varyonoff_work); return 0; } @@ -783,16 +846,11 @@ static ssize_t chp_status_show(struct device *dev, char *buf) { struct channel_path *chp = container_of(dev, struct channel_path, dev); - int state; if (!chp) return 0; - state = get_chp_status(chp->id); - if (state & CHP_STANDBY) - return sprintf(buf, "n/a\n"); - return (state & CHP_LOGICALLY_OFFLINE) ? - sprintf(buf, "logically offline\n") : - sprintf(buf, "online\n"); + return (get_chp_status(chp->id) ? sprintf(buf, "online\n") : + sprintf(buf, "offline\n")); } static ssize_t @@ -835,7 +893,7 @@ chp_release(struct device *dev) * This replaces /proc/chpids. */ static int -new_channel_path(int chpid, int status) +new_channel_path(int chpid) { struct channel_path *chp; int ret; @@ -849,19 +907,7 @@ new_channel_path(int chpid, int status) /* fill in status, etc. */ chp->id = chpid; - switch (status) { - case CHP_STANDBY: - chp->state.online = 0; - chp->state.logically_online = 1; - break; - case CHP_LOGICALLY_OFFLINE: - chp->state.logically_online = 0; - chp->state.online = 1; - break; - case CHP_ONLINE: - chp->state.online = 1; - chp->state.logically_online = 1; - } + chp->state = 1; chp->dev = (struct device) { .parent = &css_bus_device, .release = chp_release, @@ -882,3 +928,14 @@ new_channel_path(int chpid, int status) return ret; } +static int __init +chsc_alloc_sei_area(void) +{ + sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sei_page) + printk(KERN_WARNING"Can't allocate page for processing of " \ + "chsc machine checks!\n"); + return (sei_page ? 0 : -ENOMEM); +} + +subsys_initcall(chsc_alloc_sei_area); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index e4a2b4f49b4e..6f02ac0c9781 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -3,10 +3,6 @@ #define NR_CHPIDS 256 -#define CHP_STANDBY 1 -#define CHP_LOGICALLY_OFFLINE 2 -#define CHP_ONLINE 4 - #define CHSC_SEI_ACC_CHPID 1 #define CHSC_SEI_ACC_LINKADDR 2 #define CHSC_SEI_ACC_FULLLINKADDR 3 @@ -18,10 +14,7 @@ struct chsc_header { struct channel_path { int id; - struct { - unsigned int online:1; - unsigned int logically_online:1; - }__attribute__((packed)) state; + int state; struct device dev; }; @@ -29,4 +22,6 @@ extern struct channel_path *chps[]; extern void s390_process_css( void ); extern void chsc_validate_chpids(struct subchannel *); +extern void chpid_is_actually_online(int); +extern int is_chpid_online(int); #endif diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index e2041946ef7c..cffeda66520e 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/cio.c * S/390 common I/O routines -- low level i/o calls - * $Revision: 1.114 $ + * $Revision: 1.117 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -173,7 +173,7 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) stsch (sch->irq, &sch->schib); CIO_MSG_EVENT(0, "cio_start: 'not oper' status for " - "subchannel %s!\n", sch->dev.bus_id); + "subchannel %04x!\n", sch->irq); sprintf(dbf_text, "no%s", sch->dev.bus_id); CIO_TRACE_EVENT(0, dbf_text); CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); @@ -572,9 +572,9 @@ cio_validate_subchannel (struct subchannel *sch, unsigned int irq) sch->opm; CIO_DEBUG(KERN_INFO, 0, - "Detected device %04X on subchannel %s" + "Detected device %04X on subchannel %04X" " - PIM = %02X, PAM = %02X, POM = %02X\n", - sch->schib.pmcw.dev, sch->dev.bus_id, sch->schib.pmcw.pim, + sch->schib.pmcw.dev, sch->irq, sch->schib.pmcw.pim, sch->schib.pmcw.pam, sch->schib.pmcw.pom); /* @@ -607,6 +607,7 @@ do_IRQ (struct pt_regs *regs) struct irb *irb; irq_enter (); + asm volatile ("mc 0,0"); if (S390_lowcore.int_clock >= S390_lowcore.jiffy_timer) account_ticks(regs); /* diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 9ce15e92e033..f653fe5e8629 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -37,7 +37,9 @@ struct pmcw { __u8 chpid[8]; /* CHPID 0-7 (if available) */ __u32 unused1 : 8; /* reserved zeros */ __u32 st : 3; /* subchannel type */ - __u32 unused2 : 20; /* reserved zeros */ + __u32 unused2 : 18; /* reserved zeros */ + __u32 mbfc : 1; /* measurement block format control */ + __u32 xmwme : 1; /* extended measurement word mode enable */ __u32 csense : 1; /* concurrent sense; can be enabled ...*/ /* ... per MSCH, however, if facility */ /* ... is not installed, this results */ @@ -50,7 +52,8 @@ struct pmcw { struct schib { struct pmcw pmcw; /* path management control word */ struct scsw scsw; /* subchannel status word */ - __u8 mda[12]; /* model dependent area */ + __u64 mba; /* measurement block address */ + __u8 mda[4]; /* model dependent area */ } __attribute__ ((packed,aligned(4))); /* diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c new file mode 100644 index 000000000000..64184514697a --- /dev/null +++ b/drivers/s390/cio/cmf.c @@ -0,0 +1,1033 @@ +/* + * linux/drivers/s390/cio/cmf.c ($Revision: 1.11 $) + * + * Linux on zSeries Channel Measurement Facility support + * + * Copyright 2000,2003 IBM Corporation + * + * Author: Arnd Bergmann <arndb@de.ibm.com> + * + * original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/bootmem.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <asm/ccwdev.h> +#include <asm/cio.h> +#include <asm/cmb.h> + +#include "cio.h" +#include "css.h" +#include "device.h" +#include "ioasm.h" + +/* parameter to enable cmf during boot, possible uses are: + * "s390cmf" -- enable cmf and allocate 2 MB of ram so measuring can be + * used on any subchannel + * "s390cmf=<num>" -- enable cmf and allocate enough memory to measure + * <num> subchannel, where <num> is an integer + * between 1 and 65535, default is 1024 + */ +#define ARGSTRING "s390cmf" + +/* indices for READCMB */ +enum cmb_index { + /* basic and exended format: */ + cmb_ssch_rsch_count, + cmb_sample_count, + cmb_device_connect_time, + cmb_function_pending_time, + cmb_device_disconnect_time, + cmb_control_unit_queuing_time, + cmb_device_active_only_time, + /* extended format only: */ + cmb_device_busy_time, + cmb_initial_command_response_time, +}; + +/** + * enum cmb_format - types of supported measurement block formats + * + * @CMF_BASIC: traditional channel measurement blocks supported + * by all machines that we run on + * @CMF_EXTENDED: improved format that was introduced with the z990 + * machine + * @CMF_AUTODETECT: default: use extended format when running on a z990 + * or later machine, otherwise fall back to basic format + **/ +enum cmb_format { + CMF_BASIC, + CMF_EXTENDED, + CMF_AUTODETECT = -1, +}; +/** + * format - actual format for all measurement blocks + * + * The format module parameter can be set to a value of 0 (zero) + * or 1, indicating basic or extended format as described for + * enum cmb_format. + */ +static int format = CMF_AUTODETECT; +module_param(format, bool, 0444); + +/** + * struct cmb_operations - functions to use depending on cmb_format + * + * all these functions operate on a struct cmf_device. There is only + * one instance of struct cmb_operations because all cmf_device + * objects are guaranteed to be of the same type. + * + * @alloc: allocate memory for a channel measurement block, + * either with the help of a special pool or with kmalloc + * @free: free memory allocated with @alloc + * @set: enable or disable measurement + * @readall: read a measurement block in a common format + * @reset: clear the data in the associated measurement block and + * reset its time stamp + */ +struct cmb_operations { + int (*alloc) (struct ccw_device*); + void(*free) (struct ccw_device*); + int (*set) (struct ccw_device*, u32); + u64 (*read) (struct ccw_device*, int); + int (*readall)(struct ccw_device*, struct cmbdata *); + void (*reset) (struct ccw_device*); + + struct attribute_group *attr_group; +}; +static struct cmb_operations *cmbops; + +/* our user interface is designed in terms of nanoseconds, + * while the hardware measures total times in its own + * unit.*/ +static inline u64 time_to_nsec(u32 value) +{ + return ((u64)value) * 128000ull; +} + +/* + * Users are usually interested in average times, + * not accumulated time. + * This also helps us with atomicity problems + * when reading sinlge values. + */ +static inline u64 time_to_avg_nsec(u32 value, u32 count) +{ + u64 ret; + + /* no samples yet, avoid division by 0 */ + if (count == 0) + return 0; + + /* value comes in units of 128 5sec */ + ret = time_to_nsec(value); + do_div(ret, count); + + return ret; +} + +/* activate or deactivate the channel monitor. When area is NULL, + * the monitor is deactivated. The channel monitor needs to + * be active in order to measure subchannels, which also need + * to be enabled. */ +static inline void +cmf_activate(void *area, unsigned int onoff) +{ + register void * __gpr2 asm("2"); + register long __gpr1 asm("1"); + + __gpr2 = area; + __gpr1 = onoff ? 2 : 0; + /* activate channel measurement */ + asm("schm" : : "d" (__gpr2), "d" (__gpr1) ); +} + +static int +set_schib(struct ccw_device *cdev, u32 mme, int mbfc, unsigned long address) +{ + int ret; + int retry; + struct subchannel *sch; + struct schib *schib; + + sch = to_subchannel(cdev->dev.parent); + schib = &sch->schib; + /* msch can silently fail, so do it again if necessary */ + for (retry = 0; retry < 3; retry++) { + /* prepare schib */ + stsch(sch->irq, schib); + schib->pmcw.mme = mme; + schib->pmcw.mbfc = mbfc; + /* address can be either a block address or a block index */ + if (mbfc) + schib->mba = address; + else + schib->pmcw.mbi = address; + + /* try to submit it */ + switch(ret = msch_err(sch->irq, schib)) { + case 0: + break; + case 1: + case 2: /* in I/O or status pending */ + ret = -EBUSY; + break; + case 3: /* subchannel is no longer valid */ + ret = -ENODEV; + break; + default: /* msch caught an exception */ + ret = -EINVAL; + break; + } + stsch(sch->irq, schib); /* restore the schib */ + + if (ret) + break; + + /* check if it worked */ + if (schib->pmcw.mme == mme && + schib->pmcw.mbfc == mbfc && + (mbfc ? (schib->mba == address) + : (schib->pmcw.mbi == address))) + return 0; + + ret = -EINVAL; + } + + return ret; +} + +struct set_schib_struct { + u32 mme; + int mbfc; + unsigned long address; + wait_queue_head_t wait; + int ret; +}; + +static int set_schib_wait(struct ccw_device *cdev, u32 mme, + int mbfc, unsigned long address) +{ + struct set_schib_struct s = { + .mme = mme, + .mbfc = mbfc, + .address = address, + .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s.wait), + }; + + spin_lock_irq(cdev->ccwlock); + s.ret = set_schib(cdev, mme, mbfc, address); + if (s.ret != -EBUSY) { + goto out_nowait; + } + + if (cdev->private->state != DEV_STATE_ONLINE) { + s.ret = -EBUSY; + /* if the device is not online, don't even try again */ + goto out_nowait; + } + cdev->private->state = DEV_STATE_CMFCHANGE; + cdev->private->cmb_wait = &s; + s.ret = 1; + + spin_unlock_irq(cdev->ccwlock); + if (wait_event_interruptible(s.wait, s.ret != 1)) { + spin_lock_irq(cdev->ccwlock); + if (s.ret == 1) { + s.ret = -ERESTARTSYS; + cdev->private->cmb_wait = 0; + if (cdev->private->state == DEV_STATE_CMFCHANGE) + cdev->private->state = DEV_STATE_ONLINE; + } + spin_unlock_irq(cdev->ccwlock); + } + return s.ret; + +out_nowait: + spin_unlock_irq(cdev->ccwlock); + return s.ret; +} + +void retry_set_schib(struct ccw_device *cdev) +{ + struct set_schib_struct *s; + + s = cdev->private->cmb_wait; + cdev->private->cmb_wait = 0; + if (!s) { + WARN_ON(1); + return; + } + s->ret = set_schib(cdev, s->mme, s->mbfc, s->address); + wake_up(&s->wait); +} + +/** + * struct cmb_area - container for global cmb data + * + * @mem: pointer to CMBs (only in basic measurement mode) + * @list: contains a linked list of all subchannels + * @lock: protect concurrent access to @mem and @list + */ +struct cmb_area { + struct cmb *mem; + struct list_head list; + int num_channels; + spinlock_t lock; +}; + +static struct cmb_area cmb_area = { + .lock = SPIN_LOCK_UNLOCKED, + .list = LIST_HEAD_INIT(cmb_area.list), + .num_channels = 1024, +}; + + +/* ****** old style CMB handling ********/ + +/** int maxchannels + * + * Basic channel measurement blocks are allocated in one contiguous + * block of memory, which can not be moved as long as any channel + * is active. Therefore, a maximum number of subchannels needs to + * be defined somewhere. This is a module parameter, defaulting to + * a resonable value of 1024, or 32 kb of memory. + * Current kernels don't allow kmalloc with more than 128kb, so the + * maximum is 4096 + */ + +module_param_named(maxchannels, cmb_area.num_channels, uint, 0444); + +/** + * struct cmb - basic channel measurement block + * + * cmb as used by the hardware the fields are described in z/Architecture + * Principles of Operation, chapter 17. + * The area to be a contiguous array and may not be reallocated or freed. + * Only one cmb area can be present in the system. + */ +struct cmb { + u16 ssch_rsch_count; + u16 sample_count; + u32 device_connect_time; + u32 function_pending_time; + u32 device_disconnect_time; + u32 control_unit_queuing_time; + u32 device_active_only_time; + u32 reserved[2]; +}; + +/* insert a single device into the cmb_area list + * called with cmb_area.lock held from alloc_cmb + */ +static inline int +alloc_cmb_single (struct ccw_device *cdev) +{ + struct cmb *cmb; + struct ccw_device_private *node; + int ret; + + spin_lock_irq(cdev->ccwlock); + if (!list_empty(&cdev->private->cmb_list)) { + ret = -EBUSY; + goto out; + } + + /* find first unused cmb in cmb_area.mem. + * this is a little tricky: cmb_area.list + * remains sorted by ->cmb pointers */ + cmb = cmb_area.mem; + list_for_each_entry(node, &cmb_area.list, cmb_list) { + if ((struct cmb*)node->cmb > cmb) + break; + cmb++; + } + if (cmb - cmb_area.mem >= cmb_area.num_channels) { + ret = -ENOMEM; + goto out; + } + + /* insert new cmb */ + list_add_tail(&cdev->private->cmb_list, &node->cmb_list); + cdev->private->cmb = cmb; + ret = 0; +out: + spin_unlock_irq(cdev->ccwlock); + return ret; +} + +static int +alloc_cmb (struct ccw_device *cdev) +{ + int ret; + struct cmb *mem; + ssize_t size; + + spin_lock(&cmb_area.lock); + + if (!cmb_area.mem) { + /* there is no user yet, so we need a new area */ + size = sizeof(struct cmb) * cmb_area.num_channels; + WARN_ON(!list_empty(&cmb_area.list)); + + spin_unlock(&cmb_area.lock); + mem = kmalloc(size, GFP_KERNEL | GFP_DMA); + spin_lock(&cmb_area.lock); + + if (cmb_area.mem) { + /* ok, another thread was faster */ + kfree(mem); + } else if (!mem) { + /* no luck */ + ret = -ENOMEM; + goto out; + } else { + /* everything ok */ + memset(mem, 0, size); + cmb_area.mem = mem; + cmf_activate(cmb_area.mem, 1); + } + } + + /* do the actual allocation */ + ret = alloc_cmb_single(cdev); +out: + spin_unlock(&cmb_area.lock); + + return ret; +} + +static void +free_cmb(struct ccw_device *cdev) +{ + struct ccw_device_private *priv; + + priv = cdev->private; + + spin_lock(&cmb_area.lock); + spin_lock_irq(cdev->ccwlock); + + if (list_empty(&priv->cmb_list)) { + /* already freed */ + goto out; + } + + priv->cmb = NULL; + list_del_init(&priv->cmb_list); + + if (list_empty(&cmb_area.list)) { + cmf_activate(NULL, 0); + kfree(cmb_area.mem); + cmb_area.mem = NULL; + } +out: + spin_unlock_irq(cdev->ccwlock); + spin_unlock(&cmb_area.lock); +} + +static int +set_cmb(struct ccw_device *cdev, u32 mme) +{ + u16 offset; + + if (!cdev->private->cmb) + return -EINVAL; + + offset = mme ? (struct cmb *)cdev->private->cmb - cmb_area.mem : 0; + + return set_schib_wait(cdev, mme, 0, offset); +} + +static u64 +read_cmb (struct ccw_device *cdev, int index) +{ + /* yes, we have to put it on the stack + * because the cmb must only be accessed + * atomically, e.g. with mvc */ + struct cmb cmb; + unsigned long flags; + u32 val; + + spin_lock_irqsave(cdev->ccwlock, flags); + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + return 0; + } + + cmb = *(struct cmb*)cdev->private->cmb; + spin_unlock_irqrestore(cdev->ccwlock, flags); + + switch (index) { + case cmb_ssch_rsch_count: + return cmb.ssch_rsch_count; + case cmb_sample_count: + return cmb.sample_count; + case cmb_device_connect_time: + val = cmb.device_connect_time; + break; + case cmb_function_pending_time: + val = cmb.function_pending_time; + break; + case cmb_device_disconnect_time: + val = cmb.device_disconnect_time; + break; + case cmb_control_unit_queuing_time: + val = cmb.control_unit_queuing_time; + break; + case cmb_device_active_only_time: + val = cmb.device_active_only_time; + break; + default: + return 0; + } + return time_to_avg_nsec(val, cmb.sample_count); +} + +static int +readall_cmb (struct ccw_device *cdev, struct cmbdata *data) +{ + /* yes, we have to put it on the stack + * because the cmb must only be accessed + * atomically, e.g. with mvc */ + struct cmb cmb; + unsigned long flags; + u64 time; + + spin_lock_irqsave(cdev->ccwlock, flags); + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + return -ENODEV; + } + + cmb = *(struct cmb*)cdev->private->cmb; + time = get_clock() - cdev->private->cmb_start_time; + spin_unlock_irqrestore(cdev->ccwlock, flags); + + *data = (struct cmbdata) { + /* we only know values before device_busy_time */ + .size = offsetof(struct cmbdata, device_busy_time), + + /* conver to nanoseconds */ + .elapsed_time = (time * 1000) >> 12, + + /* copy data to new structure */ + .ssch_rsch_count = cmb.ssch_rsch_count, + .sample_count = cmb.sample_count, + + /* time fields are converted to nanoseconds while copying */ + .device_connect_time + = time_to_nsec(cmb.device_connect_time), + .function_pending_time + = time_to_nsec(cmb.function_pending_time), + .device_disconnect_time + = time_to_nsec(cmb.device_disconnect_time), + .control_unit_queuing_time + = time_to_nsec(cmb.control_unit_queuing_time), + .device_active_only_time + = time_to_nsec(cmb.device_active_only_time), + }; + + return 0; +} + +static void +reset_cmb(struct ccw_device *cdev) +{ + struct cmb *cmb; + spin_lock_irq(cdev->ccwlock); + cmb = cdev->private->cmb; + if (cmb) + memset (cmb, 0, sizeof (*cmb)); + cdev->private->cmb_start_time = get_clock(); + spin_unlock_irq(cdev->ccwlock); +} + +static struct attribute_group cmf_attr_group; + +static struct cmb_operations cmbops_basic = { + .alloc = alloc_cmb, + .free = free_cmb, + .set = set_cmb, + .read = read_cmb, + .readall = readall_cmb, + .reset = reset_cmb, + .attr_group = &cmf_attr_group, +}; + +/* ******** extended cmb handling ********/ + +/** + * struct cmbe - extended channel measurement block + * + * cmb as used by the hardware, may be in any 64 bit physical location, + * the fields are described in z/Architecture Principles of Operation, + * third edition, chapter 17. + */ +struct cmbe { + u32 ssch_rsch_count; + u32 sample_count; + u32 device_connect_time; + u32 function_pending_time; + u32 device_disconnect_time; + u32 control_unit_queuing_time; + u32 device_active_only_time; + u32 device_busy_time; + u32 initial_command_response_time; + u32 reserved[7]; +}; + +static int +alloc_cmbe (struct ccw_device *cdev) +{ + struct cmbe *cmbe; + cmbe = kmalloc (sizeof (*cmbe), GFP_KERNEL /* | GFP_DMA ? */); + if (!cmbe) + return -ENOMEM; + + spin_lock_irq(cdev->ccwlock); + if (cdev->private->cmb) { + kfree(cmbe); + spin_unlock_irq(cdev->ccwlock); + return -EBUSY; + } + + cdev->private->cmb = cmbe; + spin_unlock_irq(cdev->ccwlock); + + /* activate global measurement if this is the first channel */ + spin_lock(&cmb_area.lock); + if (list_empty(&cmb_area.list)) + cmf_activate(NULL, 1); + list_add_tail(&cdev->private->cmb_list, &cmb_area.list); + spin_unlock(&cmb_area.lock); + + return 0; +} + +static void +free_cmbe (struct ccw_device *cdev) +{ + spin_lock_irq(cdev->ccwlock); + if (cdev->private->cmb) + kfree(cdev->private->cmb); + cdev->private->cmb = NULL; + spin_unlock_irq(cdev->ccwlock); + + /* deactivate global measurement if this is the last channel */ + spin_lock(&cmb_area.lock); + list_del_init(&cdev->private->cmb_list); + if (list_empty(&cmb_area.list)) + cmf_activate(NULL, 0); + spin_unlock(&cmb_area.lock); +} + +static int +set_cmbe(struct ccw_device *cdev, u32 mme) +{ + unsigned long mba; + + if (!cdev->private->cmb) + return -EINVAL; + mba = mme ? (unsigned long)cdev->private->cmb : 0; + + return set_schib_wait(cdev, mme, 1, mba); +} + + +u64 +read_cmbe (struct ccw_device *cdev, int index) +{ + /* yes, we have to put it on the stack + * because the cmb must only be accessed + * atomically, e.g. with mvc */ + struct cmbe cmb; + unsigned long flags; + u32 val; + + spin_lock_irqsave(cdev->ccwlock, flags); + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + return 0; + } + + cmb = *(struct cmbe*)cdev->private->cmb; + spin_unlock_irqrestore(cdev->ccwlock, flags); + + switch (index) { + case cmb_ssch_rsch_count: + return cmb.ssch_rsch_count; + case cmb_sample_count: + return cmb.sample_count; + case cmb_device_connect_time: + val = cmb.device_connect_time; + break; + case cmb_function_pending_time: + val = cmb.function_pending_time; + break; + case cmb_device_disconnect_time: + val = cmb.device_disconnect_time; + break; + case cmb_control_unit_queuing_time: + val = cmb.control_unit_queuing_time; + break; + case cmb_device_active_only_time: + val = cmb.device_active_only_time; + break; + case cmb_device_busy_time: + val = cmb.device_busy_time; + break; + case cmb_initial_command_response_time: + val = cmb.initial_command_response_time; + break; + default: + return 0; + } + return time_to_avg_nsec(val, cmb.sample_count); +} + +static int +readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) +{ + /* yes, we have to put it on the stack + * because the cmb must only be accessed + * atomically, e.g. with mvc */ + struct cmbe cmb; + unsigned long flags; + u64 time; + + spin_lock_irqsave(cdev->ccwlock, flags); + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + return -ENODEV; + } + + cmb = *(struct cmbe*)cdev->private->cmb; + time = get_clock() - cdev->private->cmb_start_time; + spin_unlock_irqrestore(cdev->ccwlock, flags); + + *data = (struct cmbdata) { + /* we only know values before device_busy_time */ + .size = offsetof(struct cmbdata, device_busy_time), + + /* conver to nanoseconds */ + .elapsed_time = (time * 1000) >> 12, + + /* copy data to new structure */ + .ssch_rsch_count = cmb.ssch_rsch_count, + .sample_count = cmb.sample_count, + + /* time fields are converted to nanoseconds while copying */ + .device_connect_time + = time_to_nsec(cmb.device_connect_time), + .function_pending_time + = time_to_nsec(cmb.function_pending_time), + .device_disconnect_time + = time_to_nsec(cmb.device_disconnect_time), + .control_unit_queuing_time + = time_to_nsec(cmb.control_unit_queuing_time), + .device_active_only_time + = time_to_nsec(cmb.device_active_only_time), + .device_busy_time + = time_to_nsec(cmb.device_busy_time), + .initial_command_response_time + = time_to_nsec(cmb.initial_command_response_time), + }; + + return 0; +} + +static void +reset_cmbe(struct ccw_device *cdev) +{ + struct cmbe *cmb; + spin_lock_irq(cdev->ccwlock); + cmb = cdev->private->cmb; + if (cmb) + memset (cmb, 0, sizeof (*cmb)); + cdev->private->cmb_start_time = get_clock(); + spin_unlock_irq(cdev->ccwlock); +} + +static struct attribute_group cmf_attr_group_ext; + +static struct cmb_operations cmbops_extended = { + .alloc = alloc_cmbe, + .free = free_cmbe, + .set = set_cmbe, + .read = read_cmbe, + .readall = readall_cmbe, + .reset = reset_cmbe, + .attr_group = &cmf_attr_group_ext, +}; + + +static ssize_t +cmb_show_attr(struct device *dev, char *buf, enum cmb_index idx) +{ + return sprintf(buf, "%lld\n", + (unsigned long long) cmf_read(to_ccwdev(dev), idx)); +} + +static ssize_t +cmb_show_avg_sample_interval(struct device *dev, char *buf) +{ + struct ccw_device *cdev; + long interval; + unsigned long count; + + cdev = to_ccwdev(dev); + interval = get_clock() - cdev->private->cmb_start_time; + count = cmf_read(cdev, cmb_sample_count); + if (count) + interval /= count; + else + interval = -1; + return sprintf(buf, "%ld\n", interval); +} + +static ssize_t +cmb_show_avg_utilization(struct device *dev, char *buf) +{ + struct cmbdata data; + u64 utilization; + unsigned long t, u; + int ret; + + ret = cmf_readall(to_ccwdev(dev), &data); + if (ret) + return ret; + + utilization = data.device_connect_time + + data.function_pending_time + + data.device_disconnect_time; + + /* shift to avoid long long division */ + while (-1ul < (data.elapsed_time | utilization)) { + utilization >>= 8; + data.elapsed_time >>= 8; + } + + /* calculate value in 0.1 percent units */ + t = (unsigned long) data.elapsed_time / 1000; + u = (unsigned long) utilization / t; + + return sprintf(buf, "%02ld.%01ld%%\n", u/ 10, u - (u/ 10) * 10); +} + +#define cmf_attr(name) \ +static ssize_t show_ ## name (struct device * dev, char * buf) \ +{ return cmb_show_attr((dev), buf, cmb_ ## name); } \ +static DEVICE_ATTR(name, 0444, show_ ## name, NULL); + +#define cmf_attr_avg(name) \ +static ssize_t show_avg_ ## name (struct device * dev, char * buf) \ +{ return cmb_show_attr((dev), buf, cmb_ ## name); } \ +static DEVICE_ATTR(avg_ ## name, 0444, show_avg_ ## name, NULL); + +cmf_attr(ssch_rsch_count); +cmf_attr(sample_count); +cmf_attr_avg(device_connect_time); +cmf_attr_avg(function_pending_time); +cmf_attr_avg(device_disconnect_time); +cmf_attr_avg(control_unit_queuing_time); +cmf_attr_avg(device_active_only_time); +cmf_attr_avg(device_busy_time); +cmf_attr_avg(initial_command_response_time); + +static DEVICE_ATTR(avg_sample_interval, 0444, cmb_show_avg_sample_interval, NULL); +static DEVICE_ATTR(avg_utilization, 0444, cmb_show_avg_utilization, NULL); + +static struct attribute *cmf_attributes[] = { + &dev_attr_avg_sample_interval.attr, + &dev_attr_avg_utilization.attr, + &dev_attr_ssch_rsch_count.attr, + &dev_attr_sample_count.attr, + &dev_attr_avg_device_connect_time.attr, + &dev_attr_avg_function_pending_time.attr, + &dev_attr_avg_device_disconnect_time.attr, + &dev_attr_avg_control_unit_queuing_time.attr, + &dev_attr_avg_device_active_only_time.attr, + 0, +}; + +static struct attribute_group cmf_attr_group = { + .name = "cmf", + .attrs = cmf_attributes, +}; + +static struct attribute *cmf_attributes_ext[] = { + &dev_attr_avg_sample_interval.attr, + &dev_attr_avg_utilization.attr, + &dev_attr_ssch_rsch_count.attr, + &dev_attr_sample_count.attr, + &dev_attr_avg_device_connect_time.attr, + &dev_attr_avg_function_pending_time.attr, + &dev_attr_avg_device_disconnect_time.attr, + &dev_attr_avg_control_unit_queuing_time.attr, + &dev_attr_avg_device_active_only_time.attr, + &dev_attr_avg_device_busy_time.attr, + &dev_attr_avg_initial_command_response_time.attr, + 0, +}; + +static struct attribute_group cmf_attr_group_ext = { + .name = "cmf", + .attrs = cmf_attributes_ext, +}; + +static ssize_t cmb_enable_show(struct device *dev, char *buf) +{ + return sprintf(buf, "%d\n", to_ccwdev(dev)->private->cmb ? 1 : 0); +} + +static ssize_t cmb_enable_store(struct device *dev, const char *buf, size_t c) +{ + struct ccw_device *cdev; + int ret; + + cdev = to_ccwdev(dev); + + switch (buf[0]) { + case '0': + ret = disable_cmf(cdev); + if (ret) + printk(KERN_INFO "disable_cmf failed (%d)\n", ret); + break; + case '1': + ret = enable_cmf(cdev); + if (ret && ret != -EBUSY) + printk(KERN_INFO "enable_cmf failed (%d)\n", ret); + break; + } + + return c; +} + +DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store); + +/* enable_cmf/disable_cmf: module interface for cmf (de)activation */ +int +enable_cmf(struct ccw_device *cdev) +{ + int ret; + + ret = cmbops->alloc(cdev); + cmbops->reset(cdev); + if (ret) + return ret; + ret = cmbops->set(cdev, 2); + if (ret) { + cmbops->free(cdev); + return ret; + } + ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group); + if (!ret) + return 0; + cmbops->set(cdev, 0); //FIXME: this can fail + cmbops->free(cdev); + return ret; +} + +int +disable_cmf(struct ccw_device *cdev) +{ + int ret; + + ret = cmbops->set(cdev, 0); + if (ret) + return ret; + cmbops->free(cdev); + sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); + return ret; +} + +u64 +cmf_read(struct ccw_device *cdev, int index) +{ + return cmbops->read(cdev, index); +} + +int +cmf_readall(struct ccw_device *cdev, struct cmbdata *data) +{ + return cmbops->readall(cdev, data); +} + +static int __init +init_cmf(void) +{ + char *format_string; + char *detect_string = "parameter"; + + /* We cannot really autoprobe this. If the user did not give a parameter, + see if we are running on z990 or up, otherwise fall back to basic mode. */ + + if (format == CMF_AUTODETECT) { + if (!MACHINE_NEW_STIDP) { + format = CMF_BASIC; + } else { + format = CMF_EXTENDED; + } + detect_string = "autodetected"; + } else { + detect_string = "parameter"; + } + + switch (format) { + case CMF_BASIC: + format_string = "basic"; + cmbops = &cmbops_basic; + if (cmb_area.num_channels > 4096 || cmb_area.num_channels < 1) { + printk(KERN_ERR "Basic channel measurement facility" + " can only use 1 to 4096 devices\n" + KERN_ERR "when the cmf driver is built" + " as a loadable module\n"); + return 1; + } + break; + case CMF_EXTENDED: + format_string = "extended"; + cmbops = &cmbops_extended; + break; + default: + printk(KERN_ERR "Invalid format %d for channel " + "measurement facility\n", format); + return 1; + } + + printk(KERN_INFO "Channel measurement facility using %s format (%s)\n", + format_string, detect_string); + return 0; +} + +module_init(init_cmf); + + +MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("channel measurement facility base driver\n" + "Copyright 2003 IBM Corporation\n"); + +EXPORT_SYMBOL_GPL(enable_cmf); +EXPORT_SYMBOL_GPL(disable_cmf); +EXPORT_SYMBOL_GPL(cmf_read); +EXPORT_SYMBOL_GPL(cmf_readall); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index d64b7b0f6a46..45aa6a0d1dcd 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/css.c * driver for channel subsystem - * $Revision: 1.65 $ + * $Revision: 1.69 $ * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -13,6 +13,7 @@ #include <linux/device.h> #include <linux/slab.h> #include <linux/errno.h> +#include <linux/list.h> #include "css.h" #include "cio.h" @@ -20,6 +21,7 @@ #include "ioasm.h" unsigned int highest_subchannel; +int need_rescan = 0; int css_init_done = 0; struct device css_bus_device = { @@ -130,7 +132,7 @@ __get_subchannel_by_stsch(int irq) struct schib schib; cc = stsch(irq, &schib); - if (cc) + if (cc || !schib.pmcw.dnv) return NULL; sch = (struct subchannel *)(unsigned long)schib.pmcw.intparm; if (!sch) @@ -154,15 +156,18 @@ get_subchannel_by_schid(int irq) sch = __get_subchannel_by_stsch(irq); if (sch) goto out; - if (!get_driver(&io_subchannel_driver.drv)) - goto out; down_read(&css_bus_type.subsys.rwsem); - list_for_each(entry, &io_subchannel_driver.drv.devices) { + list_for_each(entry, &css_bus_type.devices.list) { dev = get_device(container_of(entry, - struct device, driver_list)); + struct device, bus_list)); if (!dev) continue; + /* Skip channel paths. */ + if (dev->release != &css_subchannel_release) { + put_device(dev); + continue; + } sch = to_subchannel(dev); if (sch->irq == irq) break; @@ -170,7 +175,6 @@ get_subchannel_by_schid(int irq) sch = NULL; } up_read(&css_bus_type.subsys.rwsem); - put_driver(&io_subchannel_driver.drv); out: put_bus(&css_bus_type); @@ -188,19 +192,24 @@ css_get_subchannel_status(struct subchannel *sch, int schid) return CIO_GONE; if (!schib.pmcw.dnv) return CIO_GONE; - if (sch && (schib.pmcw.dev != sch->schib.pmcw.dev)) + if (sch && sch->schib.pmcw.dnv && + (schib.pmcw.dev != sch->schib.pmcw.dev)) return CIO_REVALIDATE; return CIO_OPER; } static inline int -css_evaluate_subchannel(int irq) +css_evaluate_subchannel(int irq, int slow) { int event, ret, disc; struct subchannel *sch; sch = get_subchannel_by_schid(irq); disc = sch ? device_is_disconnected(sch) : 0; + if (disc && slow) + return 0; /* Already processed. */ + if (!disc && !slow) + return -EAGAIN; /* Will be done on the slow path. */ event = css_get_subchannel_status(sch, irq); switch (event) { case CIO_GONE: @@ -252,18 +261,13 @@ css_evaluate_subchannel(int irq) return ret; } -/* - * Rescan for new devices. FIXME: This is slow. - * This function is called when we have lost CRWs due to overflows and we have - * to do subchannel housekeeping. - */ -void -css_reiterate_subchannels(void) +static void +css_rescan_devices(void) { int irq, ret; for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) { - ret = css_evaluate_subchannel(irq); + ret = css_evaluate_subchannel(irq, 1); /* No more memory. It doesn't make sense to continue. No * panic because this can happen in midflight and just * because we can't use a new device is no reason to crash @@ -276,20 +280,61 @@ css_reiterate_subchannels(void) } } +static void +css_evaluate_slow_subchannel(unsigned long schid) +{ + css_evaluate_subchannel(schid, 1); +} + +void +css_trigger_slow_path(void) +{ + if (need_rescan) { + need_rescan = 0; + css_rescan_devices(); + return; + } + css_walk_subchannel_slow_list(css_evaluate_slow_subchannel); +} + /* - * Called from the machine check handler for subchannel report words. + * Rescan for new devices. FIXME: This is slow. + * This function is called when we have lost CRWs due to overflows and we have + * to do subchannel housekeeping. */ void +css_reiterate_subchannels(void) +{ + css_clear_subchannel_slow_list(); + need_rescan = 1; +} + +/* + * Called from the machine check handler for subchannel report words. + */ +int css_process_crw(int irq) { + int ret; + CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq); + if (need_rescan) + /* We need to iterate all subchannels anyway. */ + return -EAGAIN; /* * Since we are always presented with IPI in the CRW, we have to * use stsch() to find out if the subchannel in question has come * or gone. */ - css_evaluate_subchannel(irq); + ret = css_evaluate_subchannel(irq, 0); + if (ret == -EAGAIN) { + if (css_enqueue_subchannel_slow(irq)) { + css_clear_subchannel_slow_list(); + need_rescan = 1; + } + } + return ret; } /* @@ -412,6 +457,73 @@ s390_root_dev_unregister(struct device *dev) device_unregister(dev); } +struct slow_subchannel { + struct list_head slow_list; + unsigned long schid; +}; + +static LIST_HEAD(slow_subchannels_head); +static spinlock_t slow_subchannel_lock = SPIN_LOCK_UNLOCKED; + +int +css_enqueue_subchannel_slow(unsigned long schid) +{ + struct slow_subchannel *new_slow_sch; + unsigned long flags; + + new_slow_sch = kmalloc(sizeof(struct slow_subchannel), GFP_ATOMIC); + if (!new_slow_sch) + return -ENOMEM; + new_slow_sch->schid = schid; + spin_lock_irqsave(&slow_subchannel_lock, flags); + list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); + return 0; +} + +void +css_clear_subchannel_slow_list(void) +{ + unsigned long flags; + + spin_lock_irqsave(&slow_subchannel_lock, flags); + while (!list_empty(&slow_subchannels_head)) { + struct slow_subchannel *slow_sch = + list_entry(slow_subchannels_head.next, + struct slow_subchannel, slow_list); + + list_del_init(slow_subchannels_head.next); + kfree(slow_sch); + } + spin_unlock_irqrestore(&slow_subchannel_lock, flags); +} + +void +css_walk_subchannel_slow_list(void (*fn)(unsigned long)) +{ + unsigned long flags; + + spin_lock_irqsave(&slow_subchannel_lock, flags); + while (!list_empty(&slow_subchannels_head)) { + struct slow_subchannel *slow_sch = + list_entry(slow_subchannels_head.next, + struct slow_subchannel, slow_list); + + list_del_init(slow_subchannels_head.next); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); + fn(slow_sch->schid); + spin_lock_irqsave(&slow_subchannel_lock, flags); + kfree(slow_sch); + } + spin_unlock_irqrestore(&slow_subchannel_lock, flags); +} + +int +css_slow_subchannels_exist(void) +{ + return (!list_empty(&slow_subchannels_head)); +} + MODULE_LICENSE("GPL"); EXPORT_SYMBOL(css_bus_type); EXPORT_SYMBOL(s390_root_dev_register); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 56ee5d4cb97c..8f905dbf6ce4 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -65,6 +65,7 @@ struct senseid { struct ccw_device_private { int state; /* device state */ + atomic_t onoff; __u16 devno; /* device number */ __u16 irq; /* subchannel number */ __u8 imask; /* lpm mask for SNID/SID/SPGID */ @@ -90,6 +91,10 @@ struct ccw_device_private { struct work_struct kick_work; wait_queue_head_t wait_q; struct timer_list timer; + void *cmb; /* measurement information */ + struct list_head cmb_list; /* list of measured devices */ + u64 cmb_start_time; /* clock value of cmb reset */ + void *cmb_wait; /* deferred cmb enable/disable */ }; /* @@ -127,6 +132,14 @@ int device_is_disconnected(struct subchannel *); void device_set_disconnected(struct subchannel *); void device_trigger_reprobe(struct subchannel *); -/* Helper function for vary on/off. */ +/* Helper functions for vary on/off. */ void device_set_waiting(struct subchannel *); +void device_call_nopath_notify(struct subchannel *); + +/* Helper functions to build lists for the slow path. */ +int css_enqueue_subchannel_slow(unsigned long schid); +void css_walk_subchannel_slow_list(void (*fn)(unsigned long)); +void css_clear_subchannel_slow_list(void); +int css_slow_subchannels_exist(void); +extern int need_rescan; #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index ce9ddcf1b0c5..333768d600a8 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/device.c * bus driver for ccw devices - * $Revision: 1.85 $ + * $Revision: 1.103 $ * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -240,6 +240,22 @@ online_show (struct device *dev, char *buf) return sprintf(buf, cdev->online ? "1\n" : "0\n"); } +static void +ccw_device_remove_disconnected(struct ccw_device *cdev) +{ + struct subchannel *sch; + /* + * Forced offline in disconnected state means + * 'throw away device'. + */ + sch = to_subchannel(cdev->dev.parent); + device_unregister(&sch->dev); + /* Reset intparm to zeroes. */ + sch->schib.pmcw.intparm = 0; + cio_modify(sch); + put_device(&sch->dev); +} + int ccw_device_set_offline(struct ccw_device *cdev) { @@ -250,20 +266,6 @@ ccw_device_set_offline(struct ccw_device *cdev) if (!cdev->online || !cdev->drv) return -EINVAL; - if (cdev->private->state == DEV_STATE_DISCONNECTED) { - struct subchannel *sch; - /* - * Forced offline in disconnected state means - * 'throw away device'. - */ - sch = to_subchannel(cdev->dev.parent); - device_unregister(&sch->dev); - /* Reset intparm to zeroes. */ - sch->schib.pmcw.intparm = 0; - cio_modify(sch); - put_device(&sch->dev); - return 0; - } if (cdev->drv->set_offline) { ret = cdev->drv->set_offline(cdev); if (ret != 0) @@ -280,7 +282,7 @@ ccw_device_set_offline(struct ccw_device *cdev) ret, cdev->dev.bus_id); cdev->online = 1; } - return ret; + return ret; } int @@ -329,15 +331,19 @@ online_store (struct device *dev, const char *buf, size_t count) if (!cdev->drv) return count; + if (atomic_compare_and_swap(0, 1, &cdev->private->onoff)) + return -EAGAIN; i = simple_strtoul(buf, &tmp, 16); if (i == 1 && cdev->drv->set_online) ccw_device_set_online(cdev); - else if (i == 0 && cdev->drv->set_offline) - ccw_device_set_offline(cdev); - else - return -EINVAL; - + else if (i == 0 && cdev->drv->set_offline) { + if (cdev->private->state == DEV_STATE_DISCONNECTED) + ccw_device_remove_disconnected(cdev); + else + ccw_device_set_offline(cdev); + } + atomic_set(&cdev->private->onoff, 0); return count; } @@ -369,12 +375,37 @@ stlck_store(struct device *dev, const char *buf, size_t count) return count; } +static ssize_t +available_show (struct device *dev, char *buf) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct subchannel *sch; + + switch (cdev->private->state) { + case DEV_STATE_BOXED: + return sprintf(buf, "boxed\n"); + case DEV_STATE_DISCONNECTED: + case DEV_STATE_DISCONNECTED_SENSE_ID: + case DEV_STATE_NOT_OPER: + sch = to_subchannel(dev->parent); + if (!sch->lpm) + return sprintf(buf, "no path\n"); + else + return sprintf(buf, "no device\n"); + default: + /* All other states considered fine. */ + return sprintf(buf, "good\n"); + } +} + static DEVICE_ATTR(chpids, 0444, chpids_show, NULL); static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL); static DEVICE_ATTR(devtype, 0444, devtype_show, NULL); static DEVICE_ATTR(cutype, 0444, cutype_show, NULL); static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(steal_lock, 0200, NULL, stlck_store); +extern struct device_attribute dev_attr_cmb_enable; +static DEVICE_ATTR(availability, 0444, available_show, NULL); /* A device has been unboxed. Start device recognition. */ static void @@ -419,6 +450,8 @@ static struct attribute * ccwdev_attrs[] = { &dev_attr_devtype.attr, &dev_attr_cutype.attr, &dev_attr_online.attr, + &dev_attr_cmb_enable.attr, + &dev_attr_availability.attr, NULL, }; @@ -468,7 +501,7 @@ ccw_device_register(struct ccw_device *cdev) return ret; if ((ret = device_add_files(dev))) - device_unregister(dev); + device_del(dev); return ret; } @@ -521,6 +554,7 @@ io_subchannel_register(void *data) if (ret) { printk (KERN_WARNING "%s: could not register %s\n", __func__, cdev->dev.bus_id); + put_device(&cdev->dev); sch->dev.driver_data = 0; kfree (cdev->private); kfree (cdev); @@ -533,10 +567,25 @@ io_subchannel_register(void *data) __func__, sch->dev.bus_id); if (cdev->private->state == DEV_STATE_BOXED) device_create_file(&cdev->dev, &dev_attr_steal_lock); + put_device(&cdev->dev); out: put_device(&sch->dev); } +static void +device_call_sch_unregister(void *data) +{ + struct ccw_device *cdev = data; + struct subchannel *sch; + + sch = to_subchannel(cdev->dev.parent); + device_unregister(&sch->dev); + /* Reset intparm to zeroes. */ + sch->schib.pmcw.intparm = 0; + cio_modify(sch); + put_device(&cdev->dev); +} + /* * subchannel recognition done. Called from the state machine. */ @@ -550,11 +599,12 @@ io_subchannel_recog_done(struct ccw_device *cdev) switch (cdev->private->state) { case DEV_STATE_NOT_OPER: /* Remove device found not operational. */ + if (!get_device(&cdev->dev)) + break; sch = to_subchannel(cdev->dev.parent); - sch->dev.driver_data = 0; - put_device(&sch->dev); - if (cdev->dev.release) - cdev->dev.release(&cdev->dev); + INIT_WORK(&cdev->private->kick_work, + device_call_sch_unregister, (void *) cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); break; case DEV_STATE_BOXED: /* Device did not respond in time. */ @@ -563,6 +613,8 @@ io_subchannel_recog_done(struct ccw_device *cdev) * We can't register the device in interrupt context so * we schedule a work item. */ + if (!get_device(&cdev->dev)) + break; INIT_WORK(&cdev->private->kick_work, io_subchannel_register, (void *) cdev); queue_work(ccw_device_work, &cdev->private->kick_work); @@ -584,6 +636,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) .devno = sch->schib.pmcw.dev, .irq = sch->irq, .state = DEV_STATE_NOT_OPER, + .cmb_list = LIST_HEAD_INIT(cdev->private->cmb_list), }; init_waitqueue_head(&cdev->private->wait_q); init_timer(&cdev->private->timer); @@ -647,6 +700,7 @@ io_subchannel_probe (struct device *pdev) return -ENOMEM; } memset(cdev->private, 0, sizeof(struct ccw_device_private)); + atomic_set(&cdev->private->onoff, 0); cdev->dev = (struct device) { .parent = pdev, .release = ccw_device_release, @@ -657,18 +711,17 @@ io_subchannel_probe (struct device *pdev) if (!get_device(&sch->dev)) { if (cdev->dev.release) cdev->dev.release(&cdev->dev); - return 0; + return -ENODEV; } rc = io_subchannel_recog(cdev, to_subchannel(pdev)); if (rc) { sch->dev.driver_data = 0; - put_device(&sch->dev); if (cdev->dev.release) cdev->dev.release(&cdev->dev); } - return 0; + return rc; } static int @@ -680,8 +733,14 @@ io_subchannel_remove (struct device *dev) return 0; cdev = dev->driver_data; /* Set ccw device to not operational and drop reference. */ - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - put_device(&cdev->dev); + cdev->private->state = DEV_STATE_NOT_OPER; + /* + * Careful here. Our ccw device might be yet unregistered when + * de-registering its subchannel (machine check during device + * recognition). Better look if the subchannel has children. + */ + if (!list_empty(&dev->children)) + device_unregister(&cdev->dev); dev->driver_data = NULL; return 0; } @@ -860,10 +919,7 @@ ccw_device_remove (struct device *dev) struct ccw_driver *cdrv = cdev->drv; int ret; - pr_debug("removing device %s, sch %d, devno %x\n", - cdev->dev.bus_id, - cdev->private->irq, - cdev->private->devno); + pr_debug("removing device %s\n", cdev->dev.bus_id); if (cdrv->remove) cdrv->remove(cdev); if (cdev->online) { @@ -879,6 +935,7 @@ ccw_device_remove (struct device *dev) pr_debug("ccw_device_offline returned %d, device %s\n", ret, cdev->dev.bus_id); } + cdev->drv = 0; return 0; } diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 9ab02ac8465a..901857e5badb 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -21,6 +21,7 @@ enum dev_state { /* special states for devices gone not operational */ DEV_STATE_DISCONNECTED, DEV_STATE_DISCONNECTED_SENSE_ID, + DEV_STATE_CMFCHANGE, /* last element! */ NR_DEV_STATES }; @@ -106,4 +107,6 @@ int ccw_device_stlck(struct ccw_device *); /* qdio needs this. */ void ccw_device_set_timeout(struct ccw_device *, int); + +void retry_set_schib(struct ccw_device *cdev); #endif diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index b6b9f100ae36..65b820ba1a3b 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -19,6 +19,7 @@ #include "cio_debug.h" #include "css.h" #include "device.h" +#include "chsc.h" #include "ioasm.h" #include "qdio.h" @@ -42,6 +43,7 @@ device_set_disconnected(struct subchannel *sch) if (!sch->dev.driver_data) return; cdev = sch->dev.driver_data; + ccw_device_set_timeout(cdev, 0); cdev->private->state = DEV_STATE_DISCONNECTED; } @@ -78,8 +80,7 @@ void ccw_device_set_timeout(struct ccw_device *cdev, int expires) { if (expires == 0) { - if (timer_pending(&cdev->private->timer)) - del_timer(&cdev->private->timer); + del_timer(&cdev->private->timer); return; } if (timer_pending(&cdev->private->timer)) { @@ -167,18 +168,53 @@ ccw_device_handle_oper(struct ccw_device *cdev) } /* + * The machine won't give us any notification by machine check if a chpid has + * been varied online on the SE so we have to find out by magic (i. e. driving + * the channel subsystem to device selection and updating our path masks). + */ +static inline void +__recover_lost_chpids(struct subchannel *sch, int old_lpm) +{ + int mask, i; + + for (i = 0; i<8; i++) { + mask = 0x80 >> i; + if (!(sch->lpm & mask)) + continue; + if (old_lpm & mask) + continue; + chpid_is_actually_online(sch->schib.pmcw.chpid[i]); + } +} + +/* * Stop device recognition. */ static void ccw_device_recog_done(struct ccw_device *cdev, int state) { struct subchannel *sch; - int notify; + int notify, old_lpm; sch = to_subchannel(cdev->dev.parent); ccw_device_set_timeout(cdev, 0); cio_disable_subchannel(sch); + /* + * Now that we tried recognition, we have performed device selection + * through ssch() and the path information is up to date. + */ + old_lpm = sch->lpm; + stsch(sch->irq, &sch->schib); + sch->lpm = sch->schib.pmcw.pim & + sch->schib.pmcw.pam & + sch->schib.pmcw.pom & + sch->opm; + if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) + /* Force reprobe on all chpids. */ + old_lpm = 0; + if (sch->lpm != old_lpm) + __recover_lost_chpids(sch, old_lpm); if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) { if (state == DEV_STATE_NOT_OPER) { cdev->private->state = DEV_STATE_DISCONNECTED; @@ -190,8 +226,8 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) switch (state) { case DEV_STATE_NOT_OPER: CIO_DEBUG(KERN_WARNING, 2, - "SenseID : unknown device %s on subchannel %s\n", - cdev->dev.bus_id, sch->dev.bus_id); + "SenseID : unknown device %04x on subchannel %04x\n", + cdev->private->devno, sch->irq); break; case DEV_STATE_OFFLINE: if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) @@ -204,16 +240,16 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) .dev_model = cdev->private->senseid.dev_model, }; /* Issue device info message. */ - CIO_DEBUG(KERN_INFO, 2, "SenseID : device %s reports: " + CIO_DEBUG(KERN_INFO, 2, "SenseID : device %04x reports: " "CU Type/Mod = %04X/%02X, Dev Type/Mod = " - "%04X/%02X\n", cdev->dev.bus_id, + "%04X/%02X\n", cdev->private->devno, cdev->id.cu_type, cdev->id.cu_model, cdev->id.dev_type, cdev->id.dev_model); break; case DEV_STATE_BOXED: CIO_DEBUG(KERN_WARNING, 2, - "SenseID : boxed device %s on subchannel %s\n", - cdev->dev.bus_id, sch->dev.bus_id); + "SenseID : boxed device %04x on subchannel %04x\n", + cdev->private->devno, sch->irq); break; } cdev->private->state = state; @@ -283,8 +319,8 @@ ccw_device_done(struct ccw_device *cdev, int state) if (state == DEV_STATE_BOXED) { CIO_DEBUG(KERN_WARNING, 2, - "Boxed device %s on subchannel %s\n", - cdev->dev.bus_id, sch->dev.bus_id); + "Boxed device %04x on subchannel %04x\n", + cdev->private->devno, sch->irq); INIT_WORK(&cdev->private->kick_work, ccw_device_add_stlck, (void *) cdev); queue_work(ccw_device_work, &cdev->private->kick_work); @@ -387,6 +423,46 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) } +static void +ccw_device_nopath_notify(void *data) +{ + struct ccw_device *cdev; + struct subchannel *sch; + int ret; + + cdev = (struct ccw_device *)data; + sch = to_subchannel(cdev->dev.parent); + /* Extra sanity. */ + if (sch->lpm) + return; + ret = (sch->driver && sch->driver->notify) ? + sch->driver->notify(&sch->dev, CIO_NO_PATH) : 0; + if (!ret) { + /* Driver doesn't want to keep device. */ + device_unregister(&sch->dev); + sch->schib.pmcw.intparm = 0; + cio_modify(sch); + } else { + ccw_device_set_timeout(cdev, 0); + cdev->private->state = DEV_STATE_DISCONNECTED; + wake_up(&cdev->private->wait_q); + } +} + +void +device_call_nopath_notify(struct subchannel *sch) +{ + struct ccw_device *cdev; + + if (!sch->dev.driver_data) + return; + cdev = sch->dev.driver_data; + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify, (void *)cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); +} + + void ccw_device_verify_done(struct ccw_device *cdev, int err) { @@ -399,6 +475,9 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) ccw_device_done(cdev, DEV_STATE_BOXED); break; default: + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify, (void *)cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } @@ -508,10 +587,7 @@ ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event) static void ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) { - if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) - cdev->private->state = DEV_STATE_DISCONNECTED; - else - ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); + ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); } /* @@ -520,8 +596,13 @@ ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) static void ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event) { + struct subchannel *sch; + cdev->private->state = DEV_STATE_NOT_OPER; - device_unregister(&cdev->dev); + sch = to_subchannel(cdev->dev.parent); + device_unregister(&sch->dev); + sch->schib.pmcw.intparm = 0; + cio_modify(sch); wake_up(&cdev->private->wait_q); } @@ -540,7 +621,9 @@ ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event) // FIXME: not-oper indication to device driver ? ccw_device_call_handler(cdev); } - device_unregister(&cdev->dev); + device_unregister(&sch->dev); + sch->schib.pmcw.intparm = 0; + cio_modify(sch); wake_up(&cdev->private->wait_q); } @@ -553,7 +636,9 @@ ccw_device_disconnected_notoper(struct ccw_device *cdev, sch = to_subchannel(cdev->dev.parent); cdev->private->state = DEV_STATE_NOT_OPER; cio_disable_subchannel(sch); - device_unregister(&cdev->dev); + device_unregister(&sch->dev); + sch->schib.pmcw.intparm = 0; + cio_modify(sch); wake_up(&cdev->private->wait_q); } @@ -692,11 +777,21 @@ ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event) static void ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) { + struct subchannel *sch; + + sch = to_subchannel(cdev->dev.parent); /* OK, i/o is dead now. Call interrupt handler. */ cdev->private->state = DEV_STATE_ONLINE; if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-ETIMEDOUT)); + if (!sch->lpm) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify, (void *)cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); + } else if (cdev->private->flags.doverify) + /* Start delayed path verification. */ + ccw_device_online_verify(cdev, 0); } static void @@ -710,6 +805,14 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) return; } if (ret == -ENODEV) { + struct subchannel *sch; + + sch = to_subchannel(cdev->dev.parent); + if (!sch->lpm) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify, (void *)cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); + } dev_fsm_event(cdev, DEV_EVENT_NOTOPER); return; } @@ -751,7 +854,12 @@ ccw_device_wait4io_irq(struct ccw_device *cdev, enum dev_event dev_event) if (sch->schib.scsw.actl == 0) ccw_device_set_timeout(cdev, 0); /* Call the handler. */ - if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify) + ccw_device_call_handler(cdev); + if (!sch->lpm) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify, (void *)cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); + } else if (cdev->private->flags.doverify) ccw_device_online_verify(cdev, 0); } @@ -759,6 +867,7 @@ static void ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event) { int ret; + struct subchannel *sch; ccw_device_set_timeout(cdev, 0); ret = ccw_device_cancel_halt_clear(cdev); @@ -767,11 +876,24 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event) cdev->private->state = DEV_STATE_TIMEOUT_KILL; return; } - if (ret == -ENODEV) + if (ret == -ENODEV) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify, (void *)cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - else if (cdev->handler) + return; + } + if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-ETIMEDOUT)); + sch = to_subchannel(cdev->dev.parent); + if (!sch->lpm) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify, (void *)cdev); + queue_work(ccw_device_work, &cdev->private->kick_work); + } else if (cdev->private->flags.doverify) + /* Start delayed path verification. */ + ccw_device_online_verify(cdev, 0); } static void @@ -831,9 +953,24 @@ device_trigger_reprobe(struct subchannel *sch) if (!sch->dev.driver_data) return; cdev = sch->dev.driver_data; - if (cdev->private->state != DEV_STATE_DISCONNECTED) - return; spin_lock_irqsave(&sch->lock, flags); + if (cdev->private->state != DEV_STATE_DISCONNECTED) { + spin_unlock_irqrestore(&sch->lock, flags); + return; + } + /* Update some values. */ + if (stsch(sch->irq, &sch->schib)) { + spin_unlock_irqrestore(&sch->lock, flags); + return; + } + /* + * The pim, pam, pom values may not be accurate, but they are the best + * we have before performing device selection :/ + */ + sch->lpm = sch->schib.pmcw.pim & + sch->schib.pmcw.pam & + sch->schib.pmcw.pom & + sch->opm; /* Re-set some bits in the pmcw that were lost. */ sch->schib.pmcw.isc = 3; sch->schib.pmcw.csense = 1; @@ -858,6 +995,15 @@ ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event) cio_disable_subchannel(sch); } +static void +ccw_device_change_cmfstate(struct ccw_device *cdev, enum dev_event dev_event) +{ + retry_set_schib(cdev); + cdev->private->state = DEV_STATE_ONLINE; + dev_fsm_event(cdev, dev_event); +} + + /* * No operation action. This is used e.g. to ignore a timeout event in * state offline. @@ -968,6 +1114,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_TIMEOUT] ccw_device_recog_timeout, [DEV_EVENT_VERIFY] ccw_device_nop, }, + [DEV_STATE_CMFCHANGE] { + [DEV_EVENT_NOTOPER] ccw_device_change_cmfstate, + [DEV_EVENT_INTERRUPT] ccw_device_change_cmfstate, + [DEV_EVENT_TIMEOUT] ccw_device_change_cmfstate, + [DEV_EVENT_VERIFY] ccw_device_change_cmfstate, + }, }; /* diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 77463eaedd5c..aae28c3bb748 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -195,7 +195,7 @@ __ccw_device_sense_id_start(struct ccw_device *cdev) /* Try on every path. */ ret = -ENODEV; while (cdev->private->imask != 0) { - if ((sch->lpm & cdev->private->imask) != 0 && + if ((sch->opm & cdev->private->imask) != 0 && cdev->private->iretry > 0) { cdev->private->iretry--; ret = cio_start (sch, cdev->private->iccws, @@ -246,22 +246,26 @@ ccw_device_check_sense_id(struct ccw_device *cdev) /* Check the error cases. */ if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) return -ETIME; - if (irb->esw.esw0.erw.cons && - (irb->ecw[0] & (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ))) { + if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) { /* * if the device doesn't support the SenseID * command further retries wouldn't help ... + * NB: We don't check here for intervention required like we + * did before, because tape devices with no tape inserted + * may present this status *in conjunction with* the + * sense id information. So, for intervention required, + * we use the "whack it until it talks" strategy... */ - CIO_MSG_EVENT(2, "SenseID : device %s on Subchannel %s " - "reports cmd reject or intervention required\n", - cdev->dev.bus_id, sch->dev.bus_id); + CIO_MSG_EVENT(2, "SenseID : device %04x on Subchannel %04x " + "reports cmd reject\n", + cdev->private->devno, sch->irq); return -EOPNOTSUPP; } if (irb->esw.esw0.erw.cons) { - CIO_MSG_EVENT(2, "SenseID : UC on dev %s, " + CIO_MSG_EVENT(2, "SenseID : UC on dev %04x, " "lpum %02X, cnt %02d, sns :" " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->dev.bus_id, + cdev->private->devno, irb->esw.esw0.sublog.lpum, irb->esw.esw0.erw.scnt, irb->ecw[0], irb->ecw[1], @@ -271,15 +275,18 @@ ccw_device_check_sense_id(struct ccw_device *cdev) return -EAGAIN; } if (irb->scsw.cc == 3) { - CIO_MSG_EVENT(2, "SenseID : path %02X for device %s on " - "subchannel %s is 'not operational'\n", - sch->orb.lpm, cdev->dev.bus_id, sch->dev.bus_id); + if ((sch->orb.lpm & + sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) + CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x on" + " subchannel %04x is 'not operational'\n", + sch->orb.lpm, cdev->private->devno, + sch->irq); return -EACCES; } /* Hmm, whatever happened, try again. */ - CIO_MSG_EVENT(2, "SenseID : start_IO() for device %s on " - "subchannel %s returns status %02X%02X\n", - cdev->dev.bus_id, sch->dev.bus_id, + CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on " + "subchannel %04x returns status %02X%02X\n", + cdev->private->devno, sch->irq, irb->scsw.dstat, irb->scsw.cstat); return -EAGAIN; } diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index aedf040220c9..5befaa3e68d6 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -154,6 +154,7 @@ ccw_device_call_handler(struct ccw_device *cdev) { struct subchannel *sch; unsigned int stctl; + int ending_status; sch = to_subchannel(cdev->dev.parent); @@ -166,7 +167,10 @@ ccw_device_call_handler(struct ccw_device *cdev) * - unsolicited interrupts */ stctl = cdev->private->irb.scsw.stctl; - if (sch->schib.scsw.actl != 0 && + ending_status = (stctl & SCSW_STCTL_SEC_STATUS) || + (stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) || + (stctl == SCSW_STCTL_STATUS_PEND); + if (!ending_status && !cdev->private->options.repall && !(stctl & SCSW_STCTL_INTER_STATUS) && !(cdev->private->options.fast && @@ -469,6 +473,7 @@ ccw_device_stlck(struct ccw_device *cdev) cio_disable_subchannel(sch); //FIXME: return code? goto out_unlock; } + cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND; spin_unlock_irqrestore(&sch->lock, flags); wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0); spin_lock_irqsave(&sch->lock, flags); diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 8bdfa2347abb..379872faa133 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -55,10 +55,10 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev) /* ret is 0, -EBUSY, -EACCES or -ENODEV */ if (ret != -EACCES) return ret; - CIO_MSG_EVENT(2, "SNID - Device %s on Subchannel " - "%s, lpm %02X, became 'not " + CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel " + "%04x, lpm %02X, became 'not " "operational'\n", - cdev->dev.bus_id, sch->dev.bus_id, + cdev->private->devno, sch->irq, cdev->private->imask); } @@ -105,10 +105,10 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) return -EOPNOTSUPP; } if (irb->esw.esw0.erw.cons) { - CIO_MSG_EVENT(2, "SNID - device %s, unit check, " + CIO_MSG_EVENT(2, "SNID - device %04x, unit check, " "lpum %02X, cnt %02d, sns : " "%02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->dev.bus_id, + cdev->private->devno, irb->esw.esw0.sublog.lpum, irb->esw.esw0.erw.scnt, irb->ecw[0], irb->ecw[1], @@ -118,15 +118,15 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) return -EAGAIN; } if (irb->scsw.cc == 3) { - CIO_MSG_EVENT(2, "SNID - Device %s on Subchannel " - "%s, lpm %02X, became 'not operational'\n", - cdev->dev.bus_id, sch->dev.bus_id, sch->orb.lpm); + CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel " + "%04x, lpm %02X, became 'not operational'\n", + cdev->private->devno, sch->irq, sch->orb.lpm); return -EACCES; } if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { - CIO_MSG_EVENT(2, "SNID - Device %s on Subchannel %s " + CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel %04x " "is reserved by someone else\n", - cdev->dev.bus_id, sch->dev.bus_id); + cdev->private->devno, sch->irq); return -EUSERS; } return 0; @@ -233,9 +233,9 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) /* PGID command failed on this path. Switch it off. */ sch->lpm &= ~cdev->private->imask; sch->vpm &= ~cdev->private->imask; - CIO_MSG_EVENT(2, "SPID - Device %s on Subchannel " - "%s, lpm %02X, became 'not operational'\n", - cdev->dev.bus_id, sch->dev.bus_id, cdev->private->imask); + CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel " + "%04x, lpm %02X, became 'not operational'\n", + cdev->private->devno, sch->irq, cdev->private->imask); return ret; } @@ -257,9 +257,9 @@ __ccw_device_check_pgid(struct ccw_device *cdev) if (irb->ecw[0] & SNS0_CMD_REJECT) return -EOPNOTSUPP; /* Hmm, whatever happened, try again. */ - CIO_MSG_EVENT(2, "SPID - device %s, unit check, cnt %02d, " + CIO_MSG_EVENT(2, "SPID - device %04x, unit check, cnt %02d, " "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->dev.bus_id, irb->esw.esw0.erw.scnt, + cdev->private->devno, irb->esw.esw0.erw.scnt, irb->ecw[0], irb->ecw[1], irb->ecw[2], irb->ecw[3], irb->ecw[4], irb->ecw[5], @@ -267,9 +267,9 @@ __ccw_device_check_pgid(struct ccw_device *cdev) return -EAGAIN; } if (irb->scsw.cc == 3) { - CIO_MSG_EVENT(2, "SPID - Device %s on Subchannel " - "%s, lpm %02X, became 'not operational'\n", - cdev->dev.bus_id, sch->dev.bus_id, + CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel " + "%04x, lpm %02X, became 'not operational'\n", + cdev->private->devno, sch->irq, cdev->private->imask); return -EACCES; } diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index 2166dd9fe8dc..8521c178ecce 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -62,8 +62,8 @@ ccw_device_path_notoper(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); stsch (sch->irq, &sch->schib); - CIO_MSG_EVENT(0, "%s(%s) - path(s) %02x are " - "not operational \n", __FUNCTION__, sch->dev.bus_id, + CIO_MSG_EVENT(0, "%s(%04x) - path(s) %02x are " + "not operational \n", __FUNCTION__, sch->irq, sch->schib.pmcw.pnom); sch->lpm &= ~sch->schib.pmcw.pnom; @@ -228,8 +228,8 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) cdev_irb->scsw.key = irb->scsw.key; /* Copy suspend control bit. */ cdev_irb->scsw.sctl = irb->scsw.sctl; - /* Copy deferred condition code. */ - cdev_irb->scsw.cc = irb->scsw.cc; + /* Accumulate deferred condition code. */ + cdev_irb->scsw.cc |= irb->scsw.cc; /* Copy ccw format bit. */ cdev_irb->scsw.fmt = irb->scsw.fmt; /* Copy prefetch bit. */ diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 5bf1218e9f6e..cee14ac747aa 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -6,8 +6,8 @@ * version 2 * * Copyright 2000,2002 IBM Corporation - * Author(s): Utz Bacher <utz.bacher@de.ibm.com> - * Cornelia Huck <cohuck@de.ibm.com> + * Author(s): Utz Bacher <utz.bacher@de.ibm.com> + * 2.6 cio integration by Cornelia Huck <cohuck@de.ibm.com> * * Restriction: only 63 iqdio subchannels would have its own indicator, * after that, subsequent subchannels share one indicator @@ -56,7 +56,7 @@ #include "ioasm.h" #include "chsc.h" -#define VERSION_QDIO_C "$Revision: 1.67 $" +#define VERSION_QDIO_C "$Revision: 1.74 $" /****************** MODULE PARAMETER VARIABLES ********************/ MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>"); @@ -76,6 +76,7 @@ static struct qdio_perf_stats perf_stats; #endif /* QDIO_PERFORMANCE_STATS */ static int hydra_thinints; +static int omit_svs; static int indicator_used[INDICATORS_PER_CACHELINE]; static __u32 * volatile indicators; @@ -114,7 +115,7 @@ qdio_min(int a,int b) static inline volatile __u64 qdio_get_micros(void) { - return (get_clock() >> 12); /* time>>12 is microseconds */ + return (get_clock() >> 10); /* time>>12 is microseconds */ } /* @@ -530,7 +531,6 @@ qdio_has_outbound_q_moved(struct qdio_q *q) if ( (i!=GET_SAVED_FRONTIER(q)) || (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) { SAVE_FRONTIER(q,i); - SAVE_TIMESTAMP(q); QDIO_DBF_TEXT4(0,trace,"oqhasmvd"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); return 1; @@ -596,8 +596,8 @@ qdio_kick_outbound_handler(struct qdio_q *q) q->error_status_flags=0; } -static void -qdio_outbound_processing(struct qdio_q *q) +static inline void +__qdio_outbound_processing(struct qdio_q *q) { QDIO_DBF_TEXT4(0,trace,"qoutproc"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); @@ -639,6 +639,12 @@ qdio_outbound_processing(struct qdio_q *q) qdio_release_q(q); } +static void +qdio_outbound_processing(struct qdio_q *q) +{ + __qdio_outbound_processing(q); +} + /************************* INBOUND ROUTINES *******************************/ @@ -997,7 +1003,7 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) perf_stats.tl_runs--; #endif /* QDIO_PERFORMANCE_STATS */ if (!qdio_is_outbound_q_done(oq)) - qdio_outbound_processing(oq); + __qdio_outbound_processing(oq); } } @@ -1024,8 +1030,8 @@ tiqdio_inbound_processing(struct qdio_q *q) __tiqdio_inbound_processing(q, atomic_read(&spare_indicator_usecount)); } -static void -qdio_inbound_processing(struct qdio_q *q) +static inline void +__qdio_inbound_processing(struct qdio_q *q) { int q_laps=0; @@ -1067,6 +1073,12 @@ again: qdio_release_q(q); } +static void +qdio_inbound_processing(struct qdio_q *q) +{ + __qdio_inbound_processing(q); +} + /************************* MAIN ROUTINES *******************************/ #ifdef QDIO_USE_PROCESSING_STATE @@ -1211,8 +1223,7 @@ next: kfree(irq_ptr->output_qs[i]); } - if (irq_ptr->qdr) - kfree(irq_ptr->qdr); + kfree(irq_ptr->qdr); kfree(irq_ptr); } @@ -1493,8 +1504,11 @@ tiqdio_thinint_handler(void) perf_stats.thinints++; perf_stats.start_time_inbound=NOW; #endif /* QDIO_PERFORMANCE_STATS */ - /* VM will do the SVS for us */ - if (!MACHINE_IS_VM) + + /* SVS only when needed: + * issue SVS to benefit from iqdio interrupt avoidance + * (SVS clears AISOI)*/ + if (!omit_svs) tiqdio_clear_global_summary(); tiqdio_inbound_checks(); @@ -1554,7 +1568,7 @@ qdio_handle_pci(struct qdio_irq *irq_ptr) #ifdef QDIO_PERFORMANCE_STATS perf_stats.tl_runs--; #endif /* QDIO_PERFORMANCE_STATS */ - qdio_inbound_processing(q); + __qdio_inbound_processing(q); } } if (!irq_ptr->hydra_gives_outbound_pcis) @@ -1568,7 +1582,7 @@ qdio_handle_pci(struct qdio_irq *irq_ptr) continue; if (!irq_ptr->sync_done_on_outb_pcis) SYNC_MEMORY; - qdio_outbound_processing(q); + __qdio_outbound_processing(q); } } @@ -1700,7 +1714,6 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) case -EIO: QDIO_PRINT_ERR("i/o error on device %s\n", cdev->dev.bus_id); - //FIXME: hm? return; case -ETIMEDOUT: qdio_timeout_handler(cdev); @@ -1817,12 +1830,13 @@ qdio_check_siga_needs(int sch) u8 ocnt; } *ssqd_area; - /* FIXME make this GFP_KERNEL */ - ssqd_area = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA); + ssqd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!ssqd_area) { QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \ "SIGAs for sch x%x.\n", sch); - return -1; /* all flags set */ + return CHSC_FLAG_SIGA_INPUT_NECESSARY || + CHSC_FLAG_SIGA_OUTPUT_NECESSARY || + CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ } ssqd_area->request = (struct chsc_header) { .length = 0x0010, @@ -1838,7 +1852,9 @@ qdio_check_siga_needs(int sch) QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \ "SIGAs for sch x%x.\n", result,sch); - qdioac = -1; /* all flags set */ + qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || + CHSC_FLAG_SIGA_OUTPUT_NECESSARY || + CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ goto out; } @@ -1846,7 +1862,9 @@ qdio_check_siga_needs(int sch) QDIO_PRINT_WARN("response upon checking SIGA needs " \ "is 0x%x. Using all SIGAs for sch x%x.\n", ssqd_area->response.code, sch); - qdioac = -1; /* all flags set */ + qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || + CHSC_FLAG_SIGA_OUTPUT_NECESSARY || + CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ goto out; } if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) || @@ -1930,6 +1948,13 @@ tiqdio_check_chsc_availability(void) == 0x10000000); sprintf(dbf_text,"hydrati%1x", hydra_thinints); QDIO_DBF_TEXT0(0,setup,dbf_text); + + /* Check for aif time delay disablement fac (bit 56). If installed, + * omit svs even under lpar (good point by rick again) */ + omit_svs = ((scsc_area->general_char[1] & 0x00000080) + == 0x00000080); + sprintf(dbf_text,"omitsvs%1x", omit_svs); + QDIO_DBF_TEXT0(0,setup,dbf_text); exit: free_page ((unsigned long) scsc_area); return result; @@ -2122,7 +2147,7 @@ qdio_shutdown(struct ccw_device *cdev, int how) int result = 0; unsigned long flags; int timeout; - char dbf_text[15]="12345678"; + char dbf_text[15]; irq_ptr = cdev->private->qdio_data; if (!irq_ptr) @@ -2152,13 +2177,6 @@ qdio_shutdown(struct ccw_device *cdev, int how) use_count), QDIO_NO_USE_COUNT_TIMEOUT); if (atomic_read(&irq_ptr->input_qs[i]->use_count)) - /* - * FIXME: - * nobody cares about such retval, - * does a timeout make sense at all? - * can this case be eliminated? - * mutex should be released anyway, shouldn't it? - */ result=-EINPROGRESS; } @@ -2170,13 +2188,6 @@ qdio_shutdown(struct ccw_device *cdev, int how) use_count), QDIO_NO_USE_COUNT_TIMEOUT); if (atomic_read(&irq_ptr->output_qs[i]->use_count)) - /* - * FIXME: - * nobody cares about such retval, - * does a timeout make sense at all? - * can this case be eliminated? - * mutex should be released anyway, shouldn't it? - */ result=-EINPROGRESS; } @@ -2260,11 +2271,10 @@ qdio_free(struct ccw_device *cdev) static inline void qdio_allocate_do_dbf(struct qdio_initialize *init_data) { - char dbf_text[20]; /* if a printf would print out more than 8 chars */ + char dbf_text[20]; /* if a printf printed out more than 8 chars */ sprintf(dbf_text,"qfmt:%x",init_data->q_format); QDIO_DBF_TEXT0(0,setup,dbf_text); - QDIO_DBF_TEXT0(0,setup,init_data->adapter_name); QDIO_DBF_HEX0(0,setup,init_data->adapter_name,8); sprintf(dbf_text,"qpff%4x",init_data->qib_param_field_format); QDIO_DBF_TEXT0(0,setup,dbf_text); @@ -2510,7 +2520,6 @@ qdio_allocate(struct qdio_initialize *init_data) irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); if (!(irq_ptr->qdr)) { - kfree(irq_ptr->qdr); kfree(irq_ptr); QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n"); return -ENOMEM; @@ -2660,8 +2669,6 @@ int qdio_fill_irq(struct qdio_initialize *init_data) irq_ptr->original_int_handler = init_data->cdev->handler; init_data->cdev->handler = qdio_handler; - up(&irq_ptr->setting_up_sema); - return 0; } @@ -2692,7 +2699,7 @@ qdio_establish(struct qdio_initialize *init_data) result = tiqdio_set_subchannel_ind(irq_ptr,0); if (result) { up(&irq_ptr->setting_up_sema); - qdio_cleanup(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); + qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); return result; } tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET); @@ -2740,23 +2747,23 @@ qdio_establish(struct qdio_initialize *init_data) return result; } - /* FIXME: don't wait forever if hardware is broken */ - wait_event(cdev->private->wait_q, - irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED || - irq_ptr->state == QDIO_IRQ_STATE_ERR); + wait_event_interruptible_timeout(cdev->private->wait_q, + irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED || + irq_ptr->state == QDIO_IRQ_STATE_ERR, + QDIO_ESTABLISH_TIMEOUT); if (irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED) result = 0; else { + up(&irq_ptr->setting_up_sema); qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); - result = -EIO; + return -EIO; } - if (MACHINE_IS_VM) - irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq); - else - irq_ptr->qdioac=CHSC_FLAG_SIGA_INPUT_NECESSARY - | CHSC_FLAG_SIGA_OUTPUT_NECESSARY; + irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq); + /* if this gets set once, we're running under VM and can omit SVSes */ + if (irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY) + omit_svs=1; sprintf(dbf_text,"qdioac%2x",irq_ptr->qdioac); QDIO_DBF_TEXT2(0,setup,dbf_text); @@ -2864,7 +2871,9 @@ qdio_activate(struct ccw_device *cdev, int flags) switch (irq_ptr->state) { case QDIO_IRQ_STATE_STOPPED: case QDIO_IRQ_STATE_ERR: + up(&irq_ptr->setting_up_sema); qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); + down(&irq_ptr->setting_up_sema); result = -EIO; break; default: @@ -2878,7 +2887,7 @@ qdio_activate(struct ccw_device *cdev, int flags) } /* buffers filled forwards again to make Rick happy */ -static void +static inline void qdio_do_qdio_fill_input(struct qdio_q *q, unsigned int qidx, unsigned int count, struct qdio_buffer *buffers) { @@ -2972,7 +2981,7 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, while (count--) qdio_kick_outbound_q(q); - qdio_outbound_processing(q); + __qdio_outbound_processing(q); } else { /* under VM, we do a SIGA sync unconditionally */ SYNC_MEMORY; @@ -2998,7 +3007,7 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, * the upper layer module could do a lot of * traffic in that time */ - qdio_outbound_processing(q); + __qdio_outbound_processing(q); } #ifdef QDIO_PERFORMANCE_STATS diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig index 92bbaa5a8a64..ab38a0631795 100644 --- a/drivers/s390/net/Kconfig +++ b/drivers/s390/net/Kconfig @@ -23,12 +23,32 @@ config CTC called ctc.ko. If you do not know what it is, it's safe to say "Y". config IUCV - tristate "IUCV device support (VM only)" - depends on NETDEVICES + tristate "IUCV support (VM only)" + help + Select this option if you want to use inter-user communication + under VM or VIF. If unsure, say "Y" to enable a fast communication + link between VM guests. At boot time the user ID of the guest needs + to be passed to the kernel. Note that both kernels need to be + compiled with this option and both need to be booted with the user ID + of the other VM guest. + +config NETIUCV + tristate "IUCV network device support (VM only)" + depends on IUCV && NETDEVICES help Select this option if you want to use inter-user communication - vehicle networking under VM or VIF. This option is also available - as a module which will be called iucv.ko. If unsure, say "Y". + vehicle networking under VM or VIF. It enables a fast communication + link between VM guests. Using ifconfig a point-to-point connection + can be established to the Linux for zSeries and S7390 system + running on the other VM guest. This option is also available + as a module which will be called netiucv.ko. If unsure, say "Y". + +config SMSGIUCV + tristate "IUCV special message support (VM only)" + depends on IUCV + help + Select this option if you want to be able to receive SMSG messages + from other VM guest systems. config QETH diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 0e4e3834a2af..e2fbc362dfba 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile @@ -4,9 +4,10 @@ ctc-objs := ctcmain.o ctctty.o -obj-$(CONFIG_IUCV) += iucv.o fsm.o +obj-$(CONFIG_IUCV) += iucv.o +obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o +obj-$(CONFIG_SMSGIUCV) += smsgiucv.o obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o -obj-$(CONFIG_IUCV) += netiucv.o obj-$(CONFIG_LCS) += lcs.o cu3088.o qeth_mod-objs := qeth.o qeth_mpc.o obj-$(CONFIG_QETH) += qeth_mod.o diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c index 19892aa07f4e..3ba7d53dbcdf 100644 --- a/drivers/s390/net/ctcmain.c +++ b/drivers/s390/net/ctcmain.c @@ -1,5 +1,5 @@ /* - * $Id: ctcmain.c,v 1.50 2003/12/02 15:18:50 cohuck Exp $ + * $Id: ctcmain.c,v 1.54 2004/02/18 12:35:59 ptiedem Exp $ * * CTC / ESCON network driver * @@ -36,7 +36,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.50 $ + * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.54 $ * */ @@ -204,15 +204,60 @@ struct channel { struct ctc_profile prof; unsigned char *trans_skb_data; + + __u16 logflags; }; #define CHANNEL_FLAGS_READ 0 #define CHANNEL_FLAGS_WRITE 1 #define CHANNEL_FLAGS_INUSE 2 #define CHANNEL_FLAGS_BUFSIZE_CHANGED 4 +#define CHANNEL_FLAGS_FAILED 8 +#define CHANNEL_FLAGS_WAITIRQ 16 #define CHANNEL_FLAGS_RWMASK 1 #define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK) +#define LOG_FLAG_ILLEGALPKT 1 +#define LOG_FLAG_ILLEGALSIZE 2 +#define LOG_FLAG_OVERRUN 4 +#define LOG_FLAG_NOMEM 8 + +#define CTC_LOGLEVEL_INFO 1 +#define CTC_LOGLEVEL_NOTICE 2 +#define CTC_LOGLEVEL_WARN 4 +#define CTC_LOGLEVEL_EMERG 8 +#define CTC_LOGLEVEL_ERR 16 +#define CTC_LOGLEVEL_DEBUG 32 +#define CTC_LOGLEVEL_CRIT 64 + +#define CTC_LOGLEVEL_DEFAULT \ +(CTC_LOGLEVEL_INFO | CTC_LOGLEVEL_NOTICE | CTC_LOGLEVEL_WARN | CTC_LOGLEVEL_CRIT) + +#define CTC_LOGLEVEL_MAX ((CTC_LOGLEVEL_CRIT<<1)-1) + +static int loglevel = CTC_LOGLEVEL_DEFAULT; + +#define ctc_pr_debug(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_DEBUG) printk(KERN_DEBUG fmt,##arg); } while (0) + +#define ctc_pr_info(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_INFO) printk(KERN_INFO fmt,##arg); } while (0) + +#define ctc_pr_notice(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_NOTICE) printk(KERN_NOTICE fmt,##arg); } while (0) + +#define ctc_pr_warn(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_WARN) printk(KERN_WARNING fmt,##arg); } while (0) + +#define ctc_pr_emerg(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_EMERG) printk(KERN_EMERG fmt,##arg); } while (0) + +#define ctc_pr_err(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_ERR) printk(KERN_ERR fmt,##arg); } while (0) + +#define ctc_pr_crit(fmt, arg...) \ +do { if (loglevel & CTC_LOGLEVEL_CRIT) printk(KERN_CRIT fmt,##arg); } while (0) + /** * Linked list of all detected channels. */ @@ -255,13 +300,15 @@ static __inline__ void ctc_clear_busy(struct net_device * dev) { clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy)); - netif_wake_queue(dev); + if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY) + netif_wake_queue(dev); } static __inline__ int ctc_test_and_set_busy(struct net_device * dev) { - netif_stop_queue(dev); + if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY) + netif_stop_queue(dev); return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy); } @@ -272,7 +319,7 @@ static void print_banner(void) { static int printed = 0; - char vbuf[] = "$Revision: 1.50 $"; + char vbuf[] = "$Revision: 1.54 $"; char *version = vbuf; if (printed) @@ -285,9 +332,9 @@ print_banner(void) version = " ??? "; printk(KERN_INFO "CTC driver Version%s" #ifdef DEBUG - " (DEBUG-VERSION, " __DATE__ __TIME__ ")" + " (DEBUG-VERSION, " __DATE__ __TIME__ ")" #endif - " initialized\n", version); + " initialized\n", version); printed = 1; } @@ -426,11 +473,11 @@ enum ch_events { }; static const char *ch_event_names[] = { - "do_IO success", - "do_IO busy", - "do_IO enodev", - "do_IO ioerr", - "do_IO unknown", + "ccw_device success", + "ccw_device busy", + "ccw_device enodev", + "ccw_device ioerr", + "ccw_device unknown", "Status ATTN & BUSY", "Status ATTN", @@ -528,6 +575,8 @@ ctc_dump_skb(struct sk_buff *skb, int offset) struct ll_header *header; int i; + if (!(loglevel & CTC_LOGLEVEL_DEBUG)) + return; p += offset; bl = *((__u16 *) p); p += 2; @@ -580,53 +629,88 @@ ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb) skb_pull(pskb, LL_HEADER_LENGTH); if ((ch->protocol == CTC_PROTO_S390) && (header->type != ETH_P_IP)) { - /** - * Check packet type only if we stick strictly - * to S/390's protocol of OS390. This only - * supports IP. Otherwise allow any packet - * type. - */ - printk(KERN_WARNING - "%s Illegal packet type 0x%04x " - "received, dropping\n", dev->name, header->type); + +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) { +#endif + /** + * Check packet type only if we stick strictly + * to S/390's protocol of OS390. This only + * supports IP. Otherwise allow any packet + * type. + */ + ctc_pr_warn( + "%s Illegal packet type 0x%04x received, dropping\n", + dev->name, header->type); + ch->logflags |= LOG_FLAG_ILLEGALPKT; +#ifndef DEBUG + } +#endif +#ifdef DEBUG ctc_dump_skb(pskb, -6); +#endif privptr->stats.rx_dropped++; privptr->stats.rx_frame_errors++; return; } pskb->protocol = ntohs(header->type); - header->length -= LL_HEADER_LENGTH; - if ((header->length == 0) || - (header->length > skb_tailroom(pskb)) || - (header->length > len)) { - printk(KERN_WARNING - "%s Illegal packet size %d " - "received (MTU=%d blocklen=%d), " - "dropping\n", dev->name, header->length, - dev->mtu, len); + if (header->length <= LL_HEADER_LENGTH) { +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) { +#endif + ctc_pr_warn( + "%s Illegal packet size %d " + "received (MTU=%d blocklen=%d), " + "dropping\n", dev->name, header->length, + dev->mtu, len); + ch->logflags |= LOG_FLAG_ILLEGALSIZE; +#ifndef DEBUG + } +#endif +#ifdef DEBUG ctc_dump_skb(pskb, -6); +#endif privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; return; } - if (header->length > skb_tailroom(pskb)) { - printk(KERN_WARNING - "%s Illegal packet size %d " - "(beyond the end of received data), " - "dropping\n", dev->name, header->length); + header->length -= LL_HEADER_LENGTH; + len -= LL_HEADER_LENGTH; + if ((header->length > skb_tailroom(pskb)) || + (header->length > len)) { +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_OVERRUN)) { +#endif + ctc_pr_warn( + "%s Illegal packet size %d " + "(beyond the end of received data), " + "dropping\n", dev->name, header->length); + ch->logflags |= LOG_FLAG_OVERRUN; +#ifndef DEBUG + } +#endif +#ifdef DEBUG ctc_dump_skb(pskb, -6); +#endif privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; return; } skb_put(pskb, header->length); pskb->mac.raw = pskb->data; - len -= (LL_HEADER_LENGTH + header->length); + len -= header->length; skb = dev_alloc_skb(pskb->len); if (!skb) { - printk(KERN_WARNING - "%s Out of memory in ctc_unpack_skb\n", - dev->name); +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_NOMEM)) { +#endif + ctc_pr_warn( + "%s Out of memory in ctc_unpack_skb\n", + dev->name); + ch->logflags |= LOG_FLAG_NOMEM; +#ifndef DEBUG + } +#endif privptr->stats.rx_dropped++; return; } @@ -639,47 +723,64 @@ ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb) ctc_tty_netif_rx(skb); else netif_rx(skb); + /** + * Successful rx; reset logflags + */ + ch->logflags = 0; dev->last_rx = jiffies; privptr->stats.rx_packets++; privptr->stats.rx_bytes += skb->len; if (len > 0) { skb_pull(pskb, header->length); + if (skb_tailroom(pskb) < LL_HEADER_LENGTH) { +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_OVERRUN)) { +#endif + ctc_pr_warn( + "%s Overrun in ctc_unpack_skb\n", + dev->name); + ch->logflags |= LOG_FLAG_OVERRUN; +#ifndef DEBUG + } +#endif + return; + } skb_put(pskb, LL_HEADER_LENGTH); } } } /** - * Check return code of a preceeding do_IO, halt_IO etc... + * Check return code of a preceeding ccw_device call, halt_IO etc... * * @param ch The channel, the error belongs to. * @param return_code The error code to inspect. */ static void inline -ccw_check_return_code(struct channel *ch, int return_code) +ccw_check_return_code(struct channel *ch, int return_code, char *msg) { switch (return_code) { - case 0: - fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch); - break; - case -EBUSY: - printk(KERN_INFO "%s: Busy !\n", ch->id); - fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch); - break; - case -ENODEV: - printk(KERN_EMERG - "%s: Invalid device called for IO\n", ch->id); - fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch); - break; - case -EIO: - printk(KERN_EMERG "%s: Status pending... \n", ch->id); - fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch); - break; - default: - printk(KERN_EMERG - "%s: Unknown error in do_IO %04x\n", - ch->id, return_code); - fsm_event(ch->fsm, CH_EVENT_IO_UNKNOWN, ch); + case 0: + fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch); + break; + case -EBUSY: + ctc_pr_warn("%s (%s): Busy !\n", ch->id, msg); + fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch); + break; + case -ENODEV: + ctc_pr_emerg("%s (%s): Invalid device called for IO\n", + ch->id, msg); + fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch); + break; + case -EIO: + ctc_pr_emerg("%s (%s): Status pending... \n", + ch->id, msg); + fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch); + break; + default: + ctc_pr_emerg("%s (%s): Unknown error in do_IO %04x\n", + ch->id, msg, return_code); + fsm_event(ch->fsm, CH_EVENT_IO_UNKNOWN, ch); } } @@ -695,47 +796,39 @@ ccw_unit_check(struct channel *ch, unsigned char sense) if (sense & SNS0_INTERVENTION_REQ) { if (sense & 0x01) { if (ch->protocol != CTC_PROTO_LINUX_TTY) - printk(KERN_DEBUG - "%s: Interface disc. or Sel. reset " - "(remote)\n", ch->id); + ctc_pr_debug("%s: Interface disc. or Sel. reset " + "(remote)\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch); } else { - printk(KERN_DEBUG "%s: System reset (remote)\n", - ch->id); + ctc_pr_debug("%s: System reset (remote)\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_RSRESET, ch); } } else if (sense & SNS0_EQUIPMENT_CHECK) { if (sense & SNS0_BUS_OUT_CHECK) { - printk(KERN_WARNING - "%s: Hardware malfunction (remote)\n", - ch->id); + ctc_pr_warn("%s: Hardware malfunction (remote)\n", + ch->id); fsm_event(ch->fsm, CH_EVENT_UC_HWFAIL, ch); } else { - printk(KERN_WARNING - "%s: Read-data parity error (remote)\n", - ch->id); + ctc_pr_warn("%s: Read-data parity error (remote)\n", + ch->id); fsm_event(ch->fsm, CH_EVENT_UC_RXPARITY, ch); } } else if (sense & SNS0_BUS_OUT_CHECK) { if (sense & 0x04) { - printk(KERN_WARNING - "%s: Data-streaming timeout)\n", ch->id); + ctc_pr_warn("%s: Data-streaming timeout)\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_TXTIMEOUT, ch); } else { - printk(KERN_WARNING - "%s: Data-transfer parity error\n", - ch->id); + ctc_pr_warn("%s: Data-transfer parity error\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_TXPARITY, ch); } } else if (sense & SNS0_CMD_REJECT) { - printk(KERN_WARNING "%s: Command reject\n", ch->id); + ctc_pr_warn("%s: Command reject\n", ch->id); } else if (sense == 0) { - printk(KERN_DEBUG "%s: Unit check ZERO\n", ch->id); + ctc_pr_debug("%s: Unit check ZERO\n", ch->id); fsm_event(ch->fsm, CH_EVENT_UC_ZERO, ch); } else { - printk(KERN_WARNING - "%s: Unit Check with sense code: %02x\n", - ch->id, sense); + ctc_pr_warn("%s: Unit Check with sense code: %02x\n", + ch->id, sense); fsm_event(ch->fsm, CH_EVENT_UC_UNKNOWN, ch); } } @@ -763,11 +856,11 @@ ctc_checkalloc_buffer(struct channel *ch, int warn) GFP_ATOMIC | GFP_DMA); if (ch->trans_skb == NULL) { if (warn) - printk(KERN_WARNING - "%s: Couldn't alloc %s trans_skb\n", - ch->id, - (CHANNEL_DIRECTION(ch->flags) == READ) ? - "RX" : "TX"); + ctc_pr_warn( + "%s: Couldn't alloc %s trans_skb\n", + ch->id, + (CHANNEL_DIRECTION(ch->flags) == READ) ? + "RX" : "TX"); return -ENOMEM; } ch->ccw[1].count = ch->max_bufsize; @@ -775,12 +868,12 @@ ctc_checkalloc_buffer(struct channel *ch, int warn) dev_kfree_skb(ch->trans_skb); ch->trans_skb = NULL; if (warn) - printk(KERN_WARNING - "%s: set_normalized_cda for %s " - "trans_skb failed, dropping packets\n", - ch->id, - (CHANNEL_DIRECTION(ch->flags) == READ) ? - "RX" : "TX"); + ctc_pr_warn( + "%s: set_normalized_cda for %s " + "trans_skb failed, dropping packets\n", + ch->id, + (CHANNEL_DIRECTION(ch->flags) == READ) ? + "RX" : "TX"); return -ENOMEM; } ch->ccw[1].count = 0; @@ -829,9 +922,8 @@ ch_action_txdone(fsm_instance * fi, int event, void *arg) ch->prof.tx_time = duration; if (ch->irb->scsw.count != 0) - printk(KERN_DEBUG "%s: TX not complete, remaining %d bytes\n", - dev->name, ch->irb->scsw.count); - + ctc_pr_debug("%s: TX not complete, remaining %d bytes\n", + dev->name, ch->irb->scsw.count); fsm_deltimer(&ch->timer); while ((skb = skb_dequeue(&ch->io_queue))) { privptr->stats.tx_packets++; @@ -881,7 +973,7 @@ ch_action_txdone(fsm_instance * fi, int event, void *arg) privptr->stats.tx_dropped += i; privptr->stats.tx_errors += i; fsm_deltimer(&ch->timer); - ccw_check_return_code(ch, rc); + ccw_check_return_code(ch, rc, "chained TX"); } } else { spin_unlock(&ch->collect_lock); @@ -932,15 +1024,15 @@ ch_action_rx(fsm_instance * fi, int event, void *arg) fsm_deltimer(&ch->timer); if (len < 8) { - printk(KERN_DEBUG "%s: got packet with length %d < 8\n", - dev->name, len); + ctc_pr_debug("%s: got packet with length %d < 8\n", + dev->name, len); privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; goto again; } if (len > ch->max_bufsize) { - printk(KERN_DEBUG "%s: got packet with length %d > %d\n", - dev->name, len, ch->max_bufsize); + ctc_pr_debug("%s: got packet with length %d > %d\n", + dev->name, len, ch->max_bufsize); privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; goto again; @@ -950,18 +1042,20 @@ ch_action_rx(fsm_instance * fi, int event, void *arg) * VM TCP seems to have a bug sending 2 trailing bytes of garbage. */ switch (ch->protocol) { - case CTC_PROTO_S390: - case CTC_PROTO_OS390: - check_len = block_len + 2; - break; - default: - check_len = block_len; - break; + case CTC_PROTO_S390: + case CTC_PROTO_OS390: + check_len = block_len + 2; + break; + default: + check_len = block_len; + break; } if ((len < block_len) || (len > check_len)) { - printk(KERN_DEBUG "%s: got block length %d != rx length %d\n", - dev->name, block_len, len); + ctc_pr_debug("%s: got block length %d != rx length %d\n", + dev->name, block_len, len); +#ifdef DEBUG ctc_dump_skb(skb, 0); +#endif *((__u16 *) skb->data) = len; privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; @@ -972,7 +1066,7 @@ ch_action_rx(fsm_instance * fi, int event, void *arg) *((__u16 *) skb->data) = block_len; ctc_unpack_skb(ch, skb); } - again: + again: skb->data = skb->tail = ch->trans_skb_data; skb->len = 0; if (ctc_checkalloc_buffer(ch, 1)) @@ -980,7 +1074,7 @@ ch_action_rx(fsm_instance * fi, int event, void *arg) ch->ccw[1].count = ch->max_bufsize; rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0); if (rc != 0) - ccw_check_return_code(ch, rc); + ccw_check_return_code(ch, rc, "normal RX"); } static void ch_action_rxidle(fsm_instance * fi, int event, void *arg); @@ -999,8 +1093,7 @@ ch_action_firstio(fsm_instance * fi, int event, void *arg) int rc; if (fsm_getstate(fi) == CH_STATE_TXIDLE) - printk(KERN_DEBUG "%s: remote side issued READ?, " - "init ...\n", ch->id); + ctc_pr_debug("%s: remote side issued READ?, init ...\n", ch->id); fsm_deltimer(&ch->timer); if (ctc_checkalloc_buffer(ch, 1)) return; @@ -1039,7 +1132,7 @@ ch_action_firstio(fsm_instance * fi, int event, void *arg) if (rc != 0) { fsm_deltimer(&ch->timer); fsm_newstate(fi, CH_STATE_SETUPWAIT); - ccw_check_return_code(ch, rc); + ccw_check_return_code(ch, rc, "init IO"); } /** * If in compatibility mode since we don´t setup a timer, we @@ -1075,7 +1168,9 @@ ch_action_rxidle(fsm_instance * fi, int event, void *arg) fsm_deltimer(&ch->timer); buflen = *((__u16 *) ch->trans_skb->data); - pr_debug("%s: Initial RX count %d\n", dev->name, buflen); +#ifdef DEBUG + ctc_pr_debug("%s: Initial RX count %d\n", dev->name, buflen); +#endif if (buflen >= CTC_INITIAL_BLOCKLEN) { if (ctc_checkalloc_buffer(ch, 1)) return; @@ -1085,13 +1180,13 @@ ch_action_rxidle(fsm_instance * fi, int event, void *arg) (unsigned long) ch, 0xff, 0); if (rc != 0) { fsm_newstate(fi, CH_STATE_RXINIT); - ccw_check_return_code(ch, rc); + ccw_check_return_code(ch, rc, "initial RX"); } else fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXUP, dev); } else { - printk(KERN_DEBUG "%s: Initial RX count %d not %d\n", - dev->name, buflen, CTC_INITIAL_BLOCKLEN); + ctc_pr_debug("%s: Initial RX count %d not %d\n", + dev->name, buflen, CTC_INITIAL_BLOCKLEN); ch_action_firstio(fi, event, arg); } } @@ -1121,7 +1216,7 @@ ch_action_setmode(fsm_instance * fi, int event, void *arg) if (rc != 0) { fsm_deltimer(&ch->timer); fsm_newstate(fi, CH_STATE_STARTWAIT); - ccw_check_return_code(ch, rc); + ccw_check_return_code(ch, rc, "set Mode"); } else ch->retry = 0; } @@ -1142,18 +1237,19 @@ ch_action_start(fsm_instance * fi, int event, void *arg) struct net_device *dev; if (ch == NULL) { - printk(KERN_WARNING "ch_action_start ch=NULL\n"); + ctc_pr_warn("ch_action_start ch=NULL\n"); return; } if (ch->netdev == NULL) { - printk(KERN_WARNING "ch_action_start dev=NULL, id=%s\n", - ch->id); + ctc_pr_warn("ch_action_start dev=NULL, id=%s\n", ch->id); return; } dev = ch->netdev; - pr_debug("%s: %s channel start\n", dev->name, - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); +#ifdef DEBUG + ctc_pr_debug("%s: %s channel start\n", dev->name, + (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); +#endif if (ch->trans_skb != NULL) { clear_normalized_cda(&ch->ccw[1]); @@ -1169,12 +1265,13 @@ ch_action_start(fsm_instance * fi, int event, void *arg) ch->ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC; ch->ccw[1].count = 0; } - if (ctc_checkalloc_buffer(ch, 0)) - printk(KERN_NOTICE - "%s: Could not allocate %s trans_skb, delaying " - "allocation until first transfer\n", - dev->name, - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); + if (ctc_checkalloc_buffer(ch, 0)) { + ctc_pr_notice( + "%s: Could not allocate %s trans_skb, delaying " + "allocation until first transfer\n", + dev->name, + (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); + } ch->ccw[0].cmd_code = CCW_CMD_PREPARE; ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC; @@ -1194,10 +1291,13 @@ ch_action_start(fsm_instance * fi, int event, void *arg) rc = ccw_device_halt(ch->cdev, (unsigned long) ch); spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); if (rc != 0) { - fsm_deltimer(&ch->timer); - ccw_check_return_code(ch, rc); + if (rc != -EBUSY) + fsm_deltimer(&ch->timer); + ccw_check_return_code(ch, rc, "initial HaltIO"); } - pr_debug("ctc: %s(): leaving\n", __FUNCTION__); +#ifdef DEBUG + ctc_pr_debug("ctc: %s(): leaving\n", __func__); +#endif } /** @@ -1225,9 +1325,11 @@ ch_action_haltio(fsm_instance * fi, int event, void *arg) if (event == CH_EVENT_STOP) spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); if (rc != 0) { - fsm_deltimer(&ch->timer); - fsm_newstate(fi, oldstate); - ccw_check_return_code(ch, rc); + if (rc != -EBUSY) { + fsm_deltimer(&ch->timer); + fsm_newstate(fi, oldstate); + } + ccw_check_return_code(ch, rc, "HaltIO in ch_action_haltio"); } } @@ -1340,15 +1442,16 @@ ch_action_setuperr(fsm_instance * fi, int event, void *arg) if (CHANNEL_DIRECTION(ch->flags) == READ) { int rc = ccw_device_halt(ch->cdev, (unsigned long) ch); if (rc != 0) - ccw_check_return_code(ch, rc); + ccw_check_return_code( + ch, rc, "HaltIO in ch_action_setuperr"); } return; } - printk(KERN_DEBUG "%s: Error %s during %s channel setup state=%s\n", - dev->name, ch_event_names[event], - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX", - fsm_getstate_str(fi)); + ctc_pr_debug("%s: Error %s during %s channel setup state=%s\n", + dev->name, ch_event_names[event], + (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX", + fsm_getstate_str(fi)); if (CHANNEL_DIRECTION(ch->flags) == READ) { fsm_newstate(fi, CH_STATE_RXERR); fsm_event(((struct ctc_priv *) dev->priv)->fsm, @@ -1378,8 +1481,8 @@ ch_action_restart(fsm_instance * fi, int event, void *arg) struct net_device *dev = ch->netdev; fsm_deltimer(&ch->timer); - printk(KERN_DEBUG "%s: %s channel restart\n", dev->name, - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); + ctc_pr_debug("%s: %s channel restart\n", dev->name, + (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); oldstate = fsm_getstate(fi); fsm_newstate(fi, CH_STATE_STARTWAIT); @@ -1389,9 +1492,11 @@ ch_action_restart(fsm_instance * fi, int event, void *arg) if (event == CH_EVENT_TIMER) spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); if (rc != 0) { - fsm_deltimer(&ch->timer); - fsm_newstate(fi, oldstate); - ccw_check_return_code(ch, rc); + if (rc != -EBUSY) { + fsm_deltimer(&ch->timer); + fsm_newstate(fi, oldstate); + } + ccw_check_return_code(ch, rc, "HaltIO in ch_action_restart"); } } @@ -1411,8 +1516,7 @@ ch_action_rxiniterr(fsm_instance * fi, int event, void *arg) if (event == CH_EVENT_TIMER) { fsm_deltimer(&ch->timer); - printk(KERN_DEBUG "%s: Timeout during RX init handshake\n", - dev->name); + ctc_pr_debug("%s: Timeout during RX init handshake\n", dev->name); if (ch->retry++ < 3) ch_action_restart(fi, event, arg); else { @@ -1421,8 +1525,7 @@ ch_action_rxiniterr(fsm_instance * fi, int event, void *arg) DEV_EVENT_RXDOWN, dev); } } else - printk(KERN_WARNING "%s: Error during RX init handshake\n", - dev->name); + ctc_pr_warn("%s: Error during RX init handshake\n", dev->name); } /** @@ -1440,8 +1543,8 @@ ch_action_rxinitfail(fsm_instance * fi, int event, void *arg) struct net_device *dev = ch->netdev; fsm_newstate(fi, CH_STATE_RXERR); - printk(KERN_WARNING "%s: RX initialization failed\n", dev->name); - printk(KERN_WARNING "%s: RX <-> RX connection detected\n", dev->name); + ctc_pr_warn("%s: RX initialization failed\n", dev->name); + ctc_pr_warn("%s: RX <-> RX connection detected\n", dev->name); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); } @@ -1460,8 +1563,8 @@ ch_action_rxdisc(fsm_instance * fi, int event, void *arg) struct net_device *dev = ch->netdev; fsm_deltimer(&ch->timer); - printk(KERN_DEBUG "%s: Got remote disconnect, re-initializing ...\n", - dev->name); + ctc_pr_debug("%s: Got remote disconnect, re-initializing ...\n", + dev->name); /** * Notify device statemachine @@ -1492,8 +1595,7 @@ ch_action_txiniterr(fsm_instance * fi, int event, void *arg) if (event == CH_EVENT_TIMER) { fsm_deltimer(&ch->timer); - printk(KERN_DEBUG "%s: Timeout during TX init handshake\n", - dev->name); + ctc_pr_debug("%s: Timeout during TX init handshake\n", dev->name); if (ch->retry++ < 3) ch_action_restart(fi, event, arg); else { @@ -1502,8 +1604,7 @@ ch_action_txiniterr(fsm_instance * fi, int event, void *arg) DEV_EVENT_TXDOWN, dev); } } else - printk(KERN_WARNING "%s: Error during TX init handshake\n", - dev->name); + ctc_pr_warn("%s: Error during TX init handshake\n", dev->name); } /** @@ -1522,23 +1623,24 @@ ch_action_txretry(fsm_instance * fi, int event, void *arg) fsm_deltimer(&ch->timer); if (ch->retry++ > 3) { - printk(KERN_DEBUG "%s: TX retry failed, restarting channel\n", - dev->name); + ctc_pr_debug("%s: TX retry failed, restarting channel\n", + dev->name); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); ch_action_restart(fi, event, arg); } else { struct sk_buff *skb; - printk(KERN_DEBUG "%s: TX retry %d\n", dev->name, ch->retry); + ctc_pr_debug("%s: TX retry %d\n", dev->name, ch->retry); if ((skb = skb_peek(&ch->io_queue))) { int rc = 0; clear_normalized_cda(&ch->ccw[4]); ch->ccw[4].count = skb->len; if (set_normalized_cda(&ch->ccw[4], skb->data)) { - printk(KERN_DEBUG "%s: IDAL alloc failed, " - "restarting channel\n", dev->name); + ctc_pr_debug( + "%s: IDAL alloc failed, chan restart\n", + dev->name); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); ch_action_restart(fi, event, arg); @@ -1555,7 +1657,7 @@ ch_action_txretry(fsm_instance * fi, int event, void *arg) saveflags); if (rc != 0) { fsm_deltimer(&ch->timer); - ccw_check_return_code(ch, rc); + ccw_check_return_code(ch, rc, "TX in ch_action_txretry"); ctc_purge_skb_queue(&ch->io_queue); } } @@ -1578,12 +1680,12 @@ ch_action_iofatal(fsm_instance * fi, int event, void *arg) fsm_deltimer(&ch->timer); if (CHANNEL_DIRECTION(ch->flags) == READ) { - printk(KERN_DEBUG "%s: RX I/O error\n", dev->name); + ctc_pr_debug("%s: RX I/O error\n", dev->name); fsm_newstate(fi, CH_STATE_RXERR); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); } else { - printk(KERN_DEBUG "%s: TX I/O error\n", dev->name); + ctc_pr_debug("%s: TX I/O error\n", dev->name); fsm_newstate(fi, CH_STATE_TXERR); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); @@ -1606,109 +1708,109 @@ ch_action_reinit(fsm_instance *fi, int event, void *arg) * The statemachine for a channel. */ static const fsm_node ch_fsm[] = { - {CH_STATE_STOPPED, CH_EVENT_STOP, fsm_action_nop}, - {CH_STATE_STOPPED, CH_EVENT_START, ch_action_start}, - {CH_STATE_STOPPED, CH_EVENT_FINSTAT, fsm_action_nop}, - {CH_STATE_STOPPED, CH_EVENT_MC_FAIL, fsm_action_nop}, - - {CH_STATE_NOTOP, CH_EVENT_STOP, ch_action_stop}, - {CH_STATE_NOTOP, CH_EVENT_START, fsm_action_nop}, - {CH_STATE_NOTOP, CH_EVENT_FINSTAT, fsm_action_nop}, - {CH_STATE_NOTOP, CH_EVENT_MC_FAIL, fsm_action_nop}, - {CH_STATE_NOTOP, CH_EVENT_MC_GOOD, ch_action_start}, - - {CH_STATE_STARTWAIT, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_STARTWAIT, CH_EVENT_START, fsm_action_nop}, - {CH_STATE_STARTWAIT, CH_EVENT_FINSTAT, ch_action_setmode}, - {CH_STATE_STARTWAIT, CH_EVENT_TIMER, ch_action_setuperr}, - {CH_STATE_STARTWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal}, - {CH_STATE_STARTWAIT, CH_EVENT_IO_EIO, ch_action_reinit}, - {CH_STATE_STARTWAIT, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_STARTRETRY, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_STARTRETRY, CH_EVENT_TIMER, ch_action_setmode}, - {CH_STATE_STARTRETRY, CH_EVENT_FINSTAT, fsm_action_nop}, - {CH_STATE_STARTRETRY, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_SETUPWAIT, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_SETUPWAIT, CH_EVENT_START, fsm_action_nop}, - {CH_STATE_SETUPWAIT, CH_EVENT_FINSTAT, ch_action_firstio}, - {CH_STATE_SETUPWAIT, CH_EVENT_UC_RCRESET, ch_action_setuperr}, - {CH_STATE_SETUPWAIT, CH_EVENT_UC_RSRESET, ch_action_setuperr}, - {CH_STATE_SETUPWAIT, CH_EVENT_TIMER, ch_action_setmode}, - {CH_STATE_SETUPWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal}, - {CH_STATE_SETUPWAIT, CH_EVENT_IO_EIO, ch_action_reinit}, - {CH_STATE_SETUPWAIT, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_RXINIT, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_RXINIT, CH_EVENT_START, fsm_action_nop}, - {CH_STATE_RXINIT, CH_EVENT_FINSTAT, ch_action_rxidle}, - {CH_STATE_RXINIT, CH_EVENT_UC_RCRESET, ch_action_rxiniterr}, - {CH_STATE_RXINIT, CH_EVENT_UC_RSRESET, ch_action_rxiniterr}, - {CH_STATE_RXINIT, CH_EVENT_TIMER, ch_action_rxiniterr}, - {CH_STATE_RXINIT, CH_EVENT_ATTNBUSY, ch_action_rxinitfail}, - {CH_STATE_RXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal}, - {CH_STATE_RXINIT, CH_EVENT_IO_EIO, ch_action_reinit}, - {CH_STATE_RXINIT, CH_EVENT_UC_ZERO, ch_action_firstio}, - {CH_STATE_RXINIT, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_RXIDLE, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_RXIDLE, CH_EVENT_START, fsm_action_nop}, - {CH_STATE_RXIDLE, CH_EVENT_FINSTAT, ch_action_rx}, - {CH_STATE_RXIDLE, CH_EVENT_UC_RCRESET, ch_action_rxdisc}, -// { CH_STATE_RXIDLE, CH_EVENT_UC_RSRESET, ch_action_rxretry }, - {CH_STATE_RXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal}, - {CH_STATE_RXIDLE, CH_EVENT_IO_EIO, ch_action_reinit}, - {CH_STATE_RXIDLE, CH_EVENT_MC_FAIL, ch_action_fail}, - {CH_STATE_RXIDLE, CH_EVENT_UC_ZERO, ch_action_rx}, - - {CH_STATE_TXINIT, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_TXINIT, CH_EVENT_START, fsm_action_nop}, - {CH_STATE_TXINIT, CH_EVENT_FINSTAT, ch_action_txidle}, - {CH_STATE_TXINIT, CH_EVENT_UC_RCRESET, ch_action_txiniterr}, - {CH_STATE_TXINIT, CH_EVENT_UC_RSRESET, ch_action_txiniterr}, - {CH_STATE_TXINIT, CH_EVENT_TIMER, ch_action_txiniterr}, - {CH_STATE_TXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal}, - {CH_STATE_TXINIT, CH_EVENT_IO_EIO, ch_action_reinit}, - {CH_STATE_TXINIT, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_TXIDLE, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_TXIDLE, CH_EVENT_START, fsm_action_nop}, - {CH_STATE_TXIDLE, CH_EVENT_FINSTAT, ch_action_firstio}, - {CH_STATE_TXIDLE, CH_EVENT_UC_RCRESET, fsm_action_nop}, - {CH_STATE_TXIDLE, CH_EVENT_UC_RSRESET, fsm_action_nop}, - {CH_STATE_TXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal}, - {CH_STATE_TXIDLE, CH_EVENT_IO_EIO, ch_action_reinit}, - {CH_STATE_TXIDLE, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_TERM, CH_EVENT_STOP, fsm_action_nop}, - {CH_STATE_TERM, CH_EVENT_START, ch_action_restart}, - {CH_STATE_TERM, CH_EVENT_FINSTAT, ch_action_stopped}, - {CH_STATE_TERM, CH_EVENT_UC_RCRESET, fsm_action_nop}, - {CH_STATE_TERM, CH_EVENT_UC_RSRESET, fsm_action_nop}, - {CH_STATE_TERM, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_DTERM, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_DTERM, CH_EVENT_START, ch_action_restart}, - {CH_STATE_DTERM, CH_EVENT_FINSTAT, ch_action_setmode}, - {CH_STATE_DTERM, CH_EVENT_UC_RCRESET, fsm_action_nop}, - {CH_STATE_DTERM, CH_EVENT_UC_RSRESET, fsm_action_nop}, - {CH_STATE_DTERM, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_TX, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_TX, CH_EVENT_START, fsm_action_nop}, - {CH_STATE_TX, CH_EVENT_FINSTAT, ch_action_txdone}, - {CH_STATE_TX, CH_EVENT_UC_RCRESET, ch_action_txretry}, - {CH_STATE_TX, CH_EVENT_UC_RSRESET, ch_action_txretry}, - {CH_STATE_TX, CH_EVENT_TIMER, ch_action_txretry}, - {CH_STATE_TX, CH_EVENT_IO_ENODEV, ch_action_iofatal}, - {CH_STATE_TX, CH_EVENT_IO_EIO, ch_action_reinit}, - {CH_STATE_TX, CH_EVENT_MC_FAIL, ch_action_fail}, - - {CH_STATE_RXERR, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_TXERR, CH_EVENT_STOP, ch_action_haltio}, - {CH_STATE_TXERR, CH_EVENT_MC_FAIL, ch_action_fail}, - {CH_STATE_RXERR, CH_EVENT_MC_FAIL, ch_action_fail}, + {CH_STATE_STOPPED, CH_EVENT_STOP, fsm_action_nop }, + {CH_STATE_STOPPED, CH_EVENT_START, ch_action_start }, + {CH_STATE_STOPPED, CH_EVENT_FINSTAT, fsm_action_nop }, + {CH_STATE_STOPPED, CH_EVENT_MC_FAIL, fsm_action_nop }, + + {CH_STATE_NOTOP, CH_EVENT_STOP, ch_action_stop }, + {CH_STATE_NOTOP, CH_EVENT_START, fsm_action_nop }, + {CH_STATE_NOTOP, CH_EVENT_FINSTAT, fsm_action_nop }, + {CH_STATE_NOTOP, CH_EVENT_MC_FAIL, fsm_action_nop }, + {CH_STATE_NOTOP, CH_EVENT_MC_GOOD, ch_action_start }, + + {CH_STATE_STARTWAIT, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_STARTWAIT, CH_EVENT_START, fsm_action_nop }, + {CH_STATE_STARTWAIT, CH_EVENT_FINSTAT, ch_action_setmode }, + {CH_STATE_STARTWAIT, CH_EVENT_TIMER, ch_action_setuperr }, + {CH_STATE_STARTWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, + {CH_STATE_STARTWAIT, CH_EVENT_IO_EIO, ch_action_reinit }, + {CH_STATE_STARTWAIT, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_STARTRETRY, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_STARTRETRY, CH_EVENT_TIMER, ch_action_setmode }, + {CH_STATE_STARTRETRY, CH_EVENT_FINSTAT, fsm_action_nop }, + {CH_STATE_STARTRETRY, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_SETUPWAIT, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_SETUPWAIT, CH_EVENT_START, fsm_action_nop }, + {CH_STATE_SETUPWAIT, CH_EVENT_FINSTAT, ch_action_firstio }, + {CH_STATE_SETUPWAIT, CH_EVENT_UC_RCRESET, ch_action_setuperr }, + {CH_STATE_SETUPWAIT, CH_EVENT_UC_RSRESET, ch_action_setuperr }, + {CH_STATE_SETUPWAIT, CH_EVENT_TIMER, ch_action_setmode }, + {CH_STATE_SETUPWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, + {CH_STATE_SETUPWAIT, CH_EVENT_IO_EIO, ch_action_reinit }, + {CH_STATE_SETUPWAIT, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_RXINIT, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_RXINIT, CH_EVENT_START, fsm_action_nop }, + {CH_STATE_RXINIT, CH_EVENT_FINSTAT, ch_action_rxidle }, + {CH_STATE_RXINIT, CH_EVENT_UC_RCRESET, ch_action_rxiniterr }, + {CH_STATE_RXINIT, CH_EVENT_UC_RSRESET, ch_action_rxiniterr }, + {CH_STATE_RXINIT, CH_EVENT_TIMER, ch_action_rxiniterr }, + {CH_STATE_RXINIT, CH_EVENT_ATTNBUSY, ch_action_rxinitfail }, + {CH_STATE_RXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, + {CH_STATE_RXINIT, CH_EVENT_IO_EIO, ch_action_reinit }, + {CH_STATE_RXINIT, CH_EVENT_UC_ZERO, ch_action_firstio }, + {CH_STATE_RXINIT, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_RXIDLE, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_RXIDLE, CH_EVENT_START, fsm_action_nop }, + {CH_STATE_RXIDLE, CH_EVENT_FINSTAT, ch_action_rx }, + {CH_STATE_RXIDLE, CH_EVENT_UC_RCRESET, ch_action_rxdisc }, +// {CH_STATE_RXIDLE, CH_EVENT_UC_RSRESET, ch_action_rxretry }, + {CH_STATE_RXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal }, + {CH_STATE_RXIDLE, CH_EVENT_IO_EIO, ch_action_reinit }, + {CH_STATE_RXIDLE, CH_EVENT_MC_FAIL, ch_action_fail }, + {CH_STATE_RXIDLE, CH_EVENT_UC_ZERO, ch_action_rx }, + + {CH_STATE_TXINIT, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_TXINIT, CH_EVENT_START, fsm_action_nop }, + {CH_STATE_TXINIT, CH_EVENT_FINSTAT, ch_action_txidle }, + {CH_STATE_TXINIT, CH_EVENT_UC_RCRESET, ch_action_txiniterr }, + {CH_STATE_TXINIT, CH_EVENT_UC_RSRESET, ch_action_txiniterr }, + {CH_STATE_TXINIT, CH_EVENT_TIMER, ch_action_txiniterr }, + {CH_STATE_TXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, + {CH_STATE_TXINIT, CH_EVENT_IO_EIO, ch_action_reinit }, + {CH_STATE_TXINIT, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_TXIDLE, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_TXIDLE, CH_EVENT_START, fsm_action_nop }, + {CH_STATE_TXIDLE, CH_EVENT_FINSTAT, ch_action_firstio }, + {CH_STATE_TXIDLE, CH_EVENT_UC_RCRESET, fsm_action_nop }, + {CH_STATE_TXIDLE, CH_EVENT_UC_RSRESET, fsm_action_nop }, + {CH_STATE_TXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal }, + {CH_STATE_TXIDLE, CH_EVENT_IO_EIO, ch_action_reinit }, + {CH_STATE_TXIDLE, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_TERM, CH_EVENT_STOP, fsm_action_nop }, + {CH_STATE_TERM, CH_EVENT_START, ch_action_restart }, + {CH_STATE_TERM, CH_EVENT_FINSTAT, ch_action_stopped }, + {CH_STATE_TERM, CH_EVENT_UC_RCRESET, fsm_action_nop }, + {CH_STATE_TERM, CH_EVENT_UC_RSRESET, fsm_action_nop }, + {CH_STATE_TERM, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_DTERM, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_DTERM, CH_EVENT_START, ch_action_restart }, + {CH_STATE_DTERM, CH_EVENT_FINSTAT, ch_action_setmode }, + {CH_STATE_DTERM, CH_EVENT_UC_RCRESET, fsm_action_nop }, + {CH_STATE_DTERM, CH_EVENT_UC_RSRESET, fsm_action_nop }, + {CH_STATE_DTERM, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_TX, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_TX, CH_EVENT_START, fsm_action_nop }, + {CH_STATE_TX, CH_EVENT_FINSTAT, ch_action_txdone }, + {CH_STATE_TX, CH_EVENT_UC_RCRESET, ch_action_txretry }, + {CH_STATE_TX, CH_EVENT_UC_RSRESET, ch_action_txretry }, + {CH_STATE_TX, CH_EVENT_TIMER, ch_action_txretry }, + {CH_STATE_TX, CH_EVENT_IO_ENODEV, ch_action_iofatal }, + {CH_STATE_TX, CH_EVENT_IO_EIO, ch_action_reinit }, + {CH_STATE_TX, CH_EVENT_MC_FAIL, ch_action_fail }, + + {CH_STATE_RXERR, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_TXERR, CH_EVENT_STOP, ch_action_haltio }, + {CH_STATE_TXERR, CH_EVENT_MC_FAIL, ch_action_fail }, + {CH_STATE_RXERR, CH_EVENT_MC_FAIL, ch_action_fail }, }; static const int CH_FSM_LEN = sizeof (ch_fsm) / sizeof (fsm_node); @@ -1720,16 +1822,16 @@ static const int CH_FSM_LEN = sizeof (ch_fsm) / sizeof (fsm_node); static inline int less_than(char *id1, char *id2) { - int dev1,dev2,i; + int dev1, dev2, i; - for (i=0;i<5;i++) { + for (i = 0; i < 5; i++) { id1++; id2++; } dev1 = simple_strtoul(id1, &id1, 16); dev2 = simple_strtoul(id2, &id2, 16); - return (dev1<dev2); + return (dev1 < dev2); } /** @@ -1750,14 +1852,14 @@ add_channel(struct ccw_device *cdev, enum channel_types type) if ((ch = (struct channel *) kmalloc(sizeof (struct channel), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING "ctc: Out of memory in add_channel\n"); + ctc_pr_warn("ctc: Out of memory in add_channel\n"); return -1; } memset(ch, 0, sizeof (struct channel)); if ((ch->ccw = (struct ccw1 *) kmalloc(sizeof (struct ccw1) * 8, GFP_KERNEL | GFP_DMA)) == NULL) { kfree(ch); - printk(KERN_WARNING "ctc: Out of memory in add_channel\n"); + ctc_pr_warn("ctc: Out of memory in add_channel\n"); return -1; } @@ -1793,19 +1895,19 @@ add_channel(struct ccw_device *cdev, enum channel_types type) ch->cdev = cdev; snprintf(ch->id, CTC_ID_SIZE, "ch-%s", cdev->dev.bus_id); ch->type = type; + loglevel = CTC_LOGLEVEL_DEFAULT; ch->fsm = init_fsm(ch->id, ch_state_names, ch_event_names, NR_CH_STATES, NR_CH_EVENTS, ch_fsm, CH_FSM_LEN, GFP_KERNEL); if (ch->fsm == NULL) { - printk(KERN_WARNING - "ctc: Could not create FSM in add_channel\n"); + ctc_pr_warn("ctc: Could not create FSM in add_channel\n"); kfree(ch); return -1; } fsm_newstate(ch->fsm, CH_STATE_IDLE); if ((ch->irb = (struct irb *) kmalloc(sizeof (struct irb), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING "ctc: Out of memory in add_channel\n"); + ctc_pr_warn("ctc: Out of memory in add_channel\n"); kfree_fsm(ch->fsm); kfree(ch); return -1; @@ -1814,9 +1916,9 @@ add_channel(struct ccw_device *cdev, enum channel_types type) while (*c && less_than((*c)->id, ch->id)) c = &(*c)->next; if (!strncmp((*c)->id, ch->id, CTC_ID_SIZE)) { - printk(KERN_DEBUG - "ctc: add_channel: device %s already in list, " - "using old entry\n", (*c)->id); + ctc_pr_debug( + "ctc: add_channel: device %s already in list, " + "using old entry\n", (*c)->id); kfree(ch->irb); kfree_fsm(ch->fsm); kfree(ch); @@ -1888,20 +1990,26 @@ channel_get(enum channel_types type, char *id, int direction) { struct channel *ch = channels; - pr_debug("ctc: %s(): searching for ch with id %d and type %d\n", - __FUNCTION__, id, type); +#ifdef DEBUG + ctc_pr_debug("ctc: %s(): searching for ch with id %s and type %d\n", + __func__, id, type); +#endif while (ch && ((strncmp(ch->id, id, CTC_ID_SIZE)) || (ch->type != type))) { - pr_debug("ctc: %s(): ch=0x%p (id=%s, type=%d\n", - __FUNCTION__, ch, ch->id, ch->type); +#ifdef DEBUG + ctc_pr_debug("ctc: %s(): ch=0x%p (id=%s, type=%d\n", + __func__, ch, ch->id, ch->type); +#endif ch = ch->next; } - pr_debug("ctc: %s(): ch=0x%pq (id=%s, type=%d\n", - __FUNCTION__, ch, ch->id, ch->type); +#ifdef DEBUG + ctc_pr_debug("ctc: %s(): ch=0x%pq (id=%s, type=%d\n", + __func__, ch, ch->id, ch->type); +#endif if (!ch) { - printk(KERN_WARNING "ctc: %s(): channel with id %s " - "and type %d not found in channel list\n", - __FUNCTION__, id, type); + ctc_pr_warn("ctc: %s(): channel with id %s " + "and type %d not found in channel list\n", + __func__, id, type); } else { if (ch->flags & CHANNEL_FLAGS_INUSE) ch = NULL; @@ -1953,10 +2061,9 @@ ctc_irq_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* Check for unsolicited interrupts. */ if (!cdev->dev.driver_data) { - printk(KERN_WARNING - "ctc: Got unsolicited irq: %s c-%02x d-%02x\n", - cdev->dev.bus_id, irb->scsw.cstat, - irb->scsw.dstat); + ctc_pr_warn("ctc: Got unsolicited irq: %s c-%02x d-%02x\n", + cdev->dev.bus_id, irb->scsw.cstat, + irb->scsw.dstat); return; } @@ -1968,22 +2075,22 @@ ctc_irq_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) else if (priv->channel[WRITE]->cdev == cdev) ch = priv->channel[WRITE]; else { - printk(KERN_ERR - "ctc: Can't determine channel for interrupt, " - "device %s\n", cdev->dev.bus_id); + ctc_pr_err("ctc: Can't determine channel for interrupt, " + "device %s\n", cdev->dev.bus_id); return; } dev = (struct net_device *) (ch->netdev); if (dev == NULL) { - printk(KERN_CRIT - "ctc: ctc_irq_handler dev = NULL bus_id=%s, ch=0x%p\n", - cdev->dev.bus_id, ch); + ctc_pr_crit("ctc: ctc_irq_handler dev=NULL bus_id=%s, ch=0x%p\n", + cdev->dev.bus_id, ch); return; } - pr_debug("%s: interrupt for device: %s received c-%02x d-%02x\n" - dev->name, ch->id, irb->scsw.cstat, irb->scsw.dstat); +#ifdef DEBUG + ctc_pr_debug("%s: interrupt for device: %s received c-%02x d-%02x\n", + dev->name, ch->id, irb->scsw.cstat, irb->scsw.dstat); +#endif /* Copy interruption response block. */ memcpy(ch->irb, irb, sizeof(struct irb)); @@ -1991,10 +2098,9 @@ ctc_irq_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* Check for good subchannel return code, otherwise error message */ if (ch->irb->scsw.cstat) { fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch); - printk(KERN_WARNING - "%s: subchannel check for device: %s - %02x %02x\n", - dev->name, ch->id, ch->irb->scsw.cstat, - ch->irb->scsw.dstat); + ctc_pr_warn("%s: subchannel check for device: %s - %02x %02x\n", + dev->name, ch->id, ch->irb->scsw.cstat, + ch->irb->scsw.dstat); return; } @@ -2076,7 +2182,7 @@ dev_action_restart(fsm_instance *fi, int event, void *arg) struct net_device *dev = (struct net_device *)arg; struct ctc_priv *privptr = dev->priv; - printk(KERN_DEBUG "%s: Restarting\n", dev->name); + ctc_pr_debug("%s: Restarting\n", dev->name); dev_action_stop(fi, event, arg); fsm_event(privptr->fsm, DEV_EVENT_STOP, dev); fsm_addtimer(&privptr->restart_timer, CTC_TIMEOUT_5SEC, @@ -2098,40 +2204,40 @@ dev_action_chup(fsm_instance * fi, int event, void *arg) struct ctc_priv *privptr = dev->priv; switch (fsm_getstate(fi)) { - case DEV_STATE_STARTWAIT_RXTX: - if (event == DEV_EVENT_RXUP) - fsm_newstate(fi, DEV_STATE_STARTWAIT_TX); - else - fsm_newstate(fi, DEV_STATE_STARTWAIT_RX); - break; - case DEV_STATE_STARTWAIT_RX: - if (event == DEV_EVENT_RXUP) { - fsm_newstate(fi, DEV_STATE_RUNNING); - printk(KERN_INFO - "%s: connected with remote side\n", dev->name); - if (privptr->protocol == CTC_PROTO_LINUX_TTY) - ctc_tty_setcarrier(dev, 1); - ctc_clear_busy(dev); - } - break; - case DEV_STATE_STARTWAIT_TX: - if (event == DEV_EVENT_TXUP) { - fsm_newstate(fi, DEV_STATE_RUNNING); - printk(KERN_INFO - "%s: connected with remote side\n", dev->name); - if (privptr->protocol == CTC_PROTO_LINUX_TTY) - ctc_tty_setcarrier(dev, 1); - ctc_clear_busy(dev); - } - break; - case DEV_STATE_STOPWAIT_TX: - if (event == DEV_EVENT_RXUP) - fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); - break; - case DEV_STATE_STOPWAIT_RX: - if (event == DEV_EVENT_TXUP) - fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); - break; + case DEV_STATE_STARTWAIT_RXTX: + if (event == DEV_EVENT_RXUP) + fsm_newstate(fi, DEV_STATE_STARTWAIT_TX); + else + fsm_newstate(fi, DEV_STATE_STARTWAIT_RX); + break; + case DEV_STATE_STARTWAIT_RX: + if (event == DEV_EVENT_RXUP) { + fsm_newstate(fi, DEV_STATE_RUNNING); + ctc_pr_info("%s: connected with remote side\n", + dev->name); + if (privptr->protocol == CTC_PROTO_LINUX_TTY) + ctc_tty_setcarrier(dev, 1); + ctc_clear_busy(dev); + } + break; + case DEV_STATE_STARTWAIT_TX: + if (event == DEV_EVENT_TXUP) { + fsm_newstate(fi, DEV_STATE_RUNNING); + ctc_pr_info("%s: connected with remote side\n", + dev->name); + if (privptr->protocol == CTC_PROTO_LINUX_TTY) + ctc_tty_setcarrier(dev, 1); + ctc_clear_busy(dev); + } + break; + case DEV_STATE_STOPWAIT_TX: + if (event == DEV_EVENT_RXUP) + fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); + break; + case DEV_STATE_STOPWAIT_RX: + if (event == DEV_EVENT_TXUP) + fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); + break; } } @@ -2150,84 +2256,84 @@ dev_action_chdown(fsm_instance * fi, int event, void *arg) struct ctc_priv *privptr = dev->priv; switch (fsm_getstate(fi)) { - case DEV_STATE_RUNNING: - if (privptr->protocol == CTC_PROTO_LINUX_TTY) - ctc_tty_setcarrier(dev, 0); - if (event == DEV_EVENT_TXDOWN) - fsm_newstate(fi, DEV_STATE_STARTWAIT_TX); - else - fsm_newstate(fi, DEV_STATE_STARTWAIT_RX); - break; - case DEV_STATE_STARTWAIT_RX: - if (event == DEV_EVENT_TXDOWN) - fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); - break; - case DEV_STATE_STARTWAIT_TX: - if (event == DEV_EVENT_RXDOWN) - fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); - break; - case DEV_STATE_STOPWAIT_RXTX: - if (event == DEV_EVENT_TXDOWN) - fsm_newstate(fi, DEV_STATE_STOPWAIT_RX); - else - fsm_newstate(fi, DEV_STATE_STOPWAIT_TX); - break; - case DEV_STATE_STOPWAIT_RX: - if (event == DEV_EVENT_RXDOWN) - fsm_newstate(fi, DEV_STATE_STOPPED); - break; - case DEV_STATE_STOPWAIT_TX: - if (event == DEV_EVENT_TXDOWN) - fsm_newstate(fi, DEV_STATE_STOPPED); - break; + case DEV_STATE_RUNNING: + if (privptr->protocol == CTC_PROTO_LINUX_TTY) + ctc_tty_setcarrier(dev, 0); + if (event == DEV_EVENT_TXDOWN) + fsm_newstate(fi, DEV_STATE_STARTWAIT_TX); + else + fsm_newstate(fi, DEV_STATE_STARTWAIT_RX); + break; + case DEV_STATE_STARTWAIT_RX: + if (event == DEV_EVENT_TXDOWN) + fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); + break; + case DEV_STATE_STARTWAIT_TX: + if (event == DEV_EVENT_RXDOWN) + fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); + break; + case DEV_STATE_STOPWAIT_RXTX: + if (event == DEV_EVENT_TXDOWN) + fsm_newstate(fi, DEV_STATE_STOPWAIT_RX); + else + fsm_newstate(fi, DEV_STATE_STOPWAIT_TX); + break; + case DEV_STATE_STOPWAIT_RX: + if (event == DEV_EVENT_RXDOWN) + fsm_newstate(fi, DEV_STATE_STOPPED); + break; + case DEV_STATE_STOPWAIT_TX: + if (event == DEV_EVENT_TXDOWN) + fsm_newstate(fi, DEV_STATE_STOPPED); + break; } } static const fsm_node dev_fsm[] = { {DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start}, - {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start}, - {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown}, - {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown}, - {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start}, - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup}, - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup}, - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown}, - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start}, - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup}, - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup}, - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown}, - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop}, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup}, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup}, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown}, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown}, + {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start }, + {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, + {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, + {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, + + {DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start }, + {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, + {DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, + {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown }, + {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, + + {DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start }, + {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, + {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, + {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown }, + {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, + + {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop }, + {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup }, + {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup }, + {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, + {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop}, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup}, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup}, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown}, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop}, - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup}, - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup}, - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown}, - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop}, - {DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown}, - {DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown}, - {DEV_STATE_RUNNING, DEV_EVENT_TXUP, fsm_action_nop}, - {DEV_STATE_RUNNING, DEV_EVENT_RXUP, fsm_action_nop}, - {DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart }, + {DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop }, + {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, + {DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, + {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown }, + {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, + + {DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop }, + {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, + {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, + {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown }, + {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, + + {DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop }, + {DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown }, + {DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown }, + {DEV_STATE_RUNNING, DEV_EVENT_TXUP, fsm_action_nop }, + {DEV_STATE_RUNNING, DEV_EVENT_RXUP, fsm_action_nop }, + {DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart }, }; static const int DEV_FSM_LEN = sizeof (dev_fsm) / sizeof (fsm_node); @@ -2349,7 +2455,7 @@ transmit_skb(struct channel *ch, struct sk_buff *skb) ch->prof.doios_single++; if (rc != 0) { fsm_deltimer(&ch->timer); - ccw_check_return_code(ch, rc); + ccw_check_return_code(ch, rc, "single skb TX"); if (ccw_idx == 3) skb_dequeue_tail(&ch->io_queue); /** @@ -2426,14 +2532,13 @@ ctc_tx(struct sk_buff *skb, struct net_device * dev) * Some sanity checks ... */ if (skb == NULL) { - printk(KERN_WARNING "%s: NULL sk_buff passed\n", dev->name); + ctc_pr_warn("%s: NULL sk_buff passed\n", dev->name); privptr->stats.tx_dropped++; return 0; } if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) { - printk(KERN_WARNING - "%s: Got sk_buff with head room < %ld bytes\n", - dev->name, LL_HEADER_LENGTH + 2); + ctc_pr_warn("%s: Got sk_buff with head room < %ld bytes\n", + dev->name, LL_HEADER_LENGTH + 2); dev_kfree_skb(skb); privptr->stats.tx_dropped++; return 0; @@ -2505,8 +2610,6 @@ ctc_stats(struct net_device * dev) /* * sysfs attributes */ -#define CTRL_BUFSIZE 40 - static ssize_t buffer_show(struct device *dev, char *buf) { @@ -2553,120 +2656,115 @@ buffer_write(struct device *dev, const char *buf, size_t count) } -static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write); - -static int -ctc_add_attributes(struct device *dev) +static ssize_t +loglevel_show(struct device *dev, char *buf) { - return device_create_file(dev, &dev_attr_buffer); + struct ctc_priv *priv; + priv = dev->driver_data; + if (!priv) + return -ENODEV; + return sprintf(buf, "%d\n", loglevel); } -static void -ctc_remove_attributes(struct device *dev) +static ssize_t +loglevel_write(struct device *dev, const char *buf, size_t count) { - device_remove_file(dev, &dev_attr_buffer); - -} + struct ctc_priv *priv; + int ll1; -#if 0 -/* FIXME: This has to be converted to another interface, as we can only have one - * value per file and can't have atomicity then */ -#define STATS_BUFSIZE 2048 + priv = dev->driver_data; + if (!priv) + return -ENODEV; + sscanf(buf, "%i", &ll1); -static int -ctc_stat_open(struct inode *inode, struct file *file) -{ - file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL); - if (file->private_data == NULL) - return -ENOMEM; - return 0; + if ((ll1 > CTC_LOGLEVEL_MAX) || (ll1 < 0)) + return -EINVAL; + loglevel = ll1; + return count; } -static int -ctc_stat_close(struct inode *inode, struct file *file) +static void +ctc_print_statistics(struct ctc_priv *priv) { - kfree(file->private_data); - return 0; + char *sbuf; + char *p; + + if (!priv) + return; + sbuf = (char *)kmalloc(2048, GFP_KERNEL); + if (sbuf == NULL) + return; + p = sbuf; + + p += sprintf(p, " Device FSM state: %s\n", + fsm_getstate_str(priv->fsm)); + p += sprintf(p, " RX channel FSM state: %s\n", + fsm_getstate_str(priv->channel[READ]->fsm)); + p += sprintf(p, " TX channel FSM state: %s\n", + fsm_getstate_str(priv->channel[WRITE]->fsm)); + p += sprintf(p, " Max. TX buffer used: %ld\n", + priv->channel[WRITE]->prof.maxmulti); + p += sprintf(p, " Max. chained SKBs: %ld\n", + priv->channel[WRITE]->prof.maxcqueue); + p += sprintf(p, " TX single write ops: %ld\n", + priv->channel[WRITE]->prof.doios_single); + p += sprintf(p, " TX multi write ops: %ld\n", + priv->channel[WRITE]->prof.doios_multi); + p += sprintf(p, " Netto bytes written: %ld\n", + priv->channel[WRITE]->prof.txlen); + p += sprintf(p, " Max. TX IO-time: %ld\n", + priv->channel[WRITE]->prof.tx_time); + + ctc_pr_debug("Statistics for %s:\n%s", + priv->channel[WRITE]->netdev->name, sbuf); + kfree(sbuf); + return; } static ssize_t -ctc_stat_write(struct file *file, const char *buf, size_t count, loff_t * off) +stats_show(struct device *dev, char *buf) { - struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode); - struct net_device *dev; - struct ctc_priv *privptr; - - if (!(dev = find_netdev_by_ino(pde))) + struct ctc_priv *priv = dev->driver_data; + if (!priv) return -ENODEV; - privptr = (struct ctc_priv *) dev->priv; - privptr->channel[WRITE]->prof.maxmulti = 0; - privptr->channel[WRITE]->prof.maxcqueue = 0; - privptr->channel[WRITE]->prof.doios_single = 0; - privptr->channel[WRITE]->prof.doios_multi = 0; - privptr->channel[WRITE]->prof.txlen = 0; - privptr->channel[WRITE]->prof.tx_time = 0; - return count; + ctc_print_statistics(priv); + return sprintf(buf, "0\n"); } static ssize_t -ctc_stat_read(struct file *file, char *buf, size_t count, loff_t * off) +stats_write(struct device *dev, const char *buf, size_t count) { - struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode); - char *sbuf = (char *) file->private_data; - struct net_device *dev; - struct ctc_priv *privptr; - ssize_t ret = 0; - char *p = sbuf; - int l; - - if (!(dev = find_netdev_by_ino(pde))) + struct ctc_priv *priv = dev->driver_data; + if (!priv) return -ENODEV; - if (off != &file->f_pos) - return -ESPIPE; + /* Reset statistics */ + memset(&priv->channel[WRITE]->prof, 0, + sizeof(priv->channel[WRITE]->prof)); + return count; +} - privptr = (struct ctc_priv *) dev->priv; +static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write); +static DEVICE_ATTR(loglevel, 0644, loglevel_show, loglevel_write); +static DEVICE_ATTR(stats, 0644, stats_show, stats_write); - if (file->f_pos == 0) { - p += sprintf(p, "Device FSM state: %s\n", - fsm_getstate_str(privptr->fsm)); - p += sprintf(p, "RX channel FSM state: %s\n", - fsm_getstate_str(privptr->channel[READ]->fsm)); - p += sprintf(p, "TX channel FSM state: %s\n", - fsm_getstate_str(privptr->channel[WRITE]->fsm)); - p += sprintf(p, "Max. TX buffer used: %ld\n", - privptr->channel[WRITE]->prof.maxmulti); - p += sprintf(p, "Max. chained SKBs: %ld\n", - privptr->channel[WRITE]->prof.maxcqueue); - p += sprintf(p, "TX single write ops: %ld\n", - privptr->channel[WRITE]->prof.doios_single); - p += sprintf(p, "TX multi write ops: %ld\n", - privptr->channel[WRITE]->prof.doios_multi); - p += sprintf(p, "Netto bytes written: %ld\n", - privptr->channel[WRITE]->prof.txlen); - p += sprintf(p, "Max. TX IO-time: %ld\n", - privptr->channel[WRITE]->prof.tx_time); - } - l = strlen(sbuf); - p = sbuf; - if (file->f_pos < l) { - p += file->f_pos; - l = strlen(p); - ret = (count > l) ? l : count; - if (copy_to_user(buf, p, ret)) - return -EFAULT; - } - file->f_pos += ret; - return ret; +static int +ctc_add_attributes(struct device *dev) +{ + device_create_file(dev, &dev_attr_buffer); + device_create_file(dev, &dev_attr_loglevel); + device_create_file(dev, &dev_attr_stats); + return 0; +} + +static void +ctc_remove_attributes(struct device *dev) +{ + device_remove_file(dev, &dev_attr_stats); + device_remove_file(dev, &dev_attr_loglevel); + device_remove_file(dev, &dev_attr_buffer); } -static struct file_operations ctc_stat_fops = { - .read = ctc_stat_read, - .write = ctc_stat_write, - .open = ctc_stat_open, - .release = ctc_stat_close, -}; -#endif static void ctc_netdev_unregister(struct net_device * dev) @@ -2772,11 +2870,14 @@ ctc_proto_store(struct device *dev, const char *buf, size_t count) struct ctc_priv *priv; int value; + pr_debug("%s() called\n", __FUNCTION__); + priv = dev->driver_data; if (!priv) return -ENODEV; sscanf(buf, "%u", &value); - /* TODO: sanity checks */ + if ((value < 0) || (value > CTC_PROTO_MAX)) + return -EINVAL; priv->protocol = value; return count; @@ -2811,12 +2912,16 @@ static struct attribute_group ctc_attr_group = { static int ctc_add_files(struct device *dev) { + pr_debug("%s() called\n", __FUNCTION__); + return sysfs_create_group(&dev->kobj, &ctc_attr_group); } static void ctc_remove_files(struct device *dev) { + pr_debug("%s() called\n", __FUNCTION__); + sysfs_remove_group(&dev->kobj, &ctc_attr_group); } @@ -2835,12 +2940,14 @@ ctc_probe_device(struct ccwgroup_device *cgdev) struct ctc_priv *priv; int rc; + pr_debug("%s() called\n", __FUNCTION__); + if (!get_device(&cgdev->dev)) return -ENODEV; priv = kmalloc(sizeof (struct ctc_priv), GFP_KERNEL); if (!priv) { - printk(KERN_ERR "%s: Out of memory\n", __func__); + ctc_pr_err("%s: Out of memory\n", __func__); put_device(&cgdev->dev); return -ENOMEM; } @@ -2879,6 +2986,9 @@ ctc_new_device(struct ccwgroup_device *cgdev) enum channel_types type; struct ctc_priv *privptr; struct net_device *dev; + int ret; + + pr_debug("%s() called\n", __FUNCTION__); privptr = cgdev->dev.driver_data; if (!privptr) @@ -2894,13 +3004,22 @@ ctc_new_device(struct ccwgroup_device *cgdev) if (add_channel(cgdev->cdev[1], type)) return -ENOMEM; - ccw_device_set_online(cgdev->cdev[0]); - ccw_device_set_online(cgdev->cdev[1]); + ret = ccw_device_set_online(cgdev->cdev[0]); + if (ret != 0) { + printk(KERN_WARNING + "ccw_device_set_online (cdev[0]) failed with ret = %d\n", ret); + } + + ret = ccw_device_set_online(cgdev->cdev[1]); + if (ret != 0) { + printk(KERN_WARNING + "ccw_device_set_online (cdev[1]) failed with ret = %d\n", ret); + } dev = ctc_init_netdevice(NULL, 1, privptr); if (!dev) { - printk(KERN_WARNING "ctc_init_netdevice failed\n"); + ctc_pr_warn("ctc_init_netdevice failed\n"); goto out; } @@ -2928,6 +3047,22 @@ ctc_new_device(struct ccwgroup_device *cgdev) ctc_free_netdevice(dev, 1); goto out; } + /* Create symlinks. */ + if (sysfs_create_link(&cgdev->dev.kobj, &dev->class_dev.kobj, + dev->name)) { + ctc_netdev_unregister(dev); + dev->priv = 0; + ctc_free_netdevice(dev, 1); + goto out; + } + if (sysfs_create_link(&dev->class_dev.kobj, &cgdev->dev.kobj, + cgdev->dev.bus_id)) { + sysfs_remove_link(&cgdev->dev.kobj, dev->name); + ctc_netdev_unregister(dev); + dev->priv = 0; + ctc_free_netdevice(dev, 1); + goto out; + } ctc_add_attributes(&cgdev->dev); @@ -2935,10 +3070,9 @@ ctc_new_device(struct ccwgroup_device *cgdev) print_banner(); - printk(KERN_INFO - "%s: read: %s, write: %s, proto: %d\n", - dev->name, privptr->channel[READ]->id, - privptr->channel[WRITE]->id, privptr->protocol); + ctc_pr_info("%s: read: %s, write: %s, proto: %d\n", + dev->name, privptr->channel[READ]->id, + privptr->channel[WRITE]->id, privptr->protocol); return 0; out: @@ -2962,32 +3096,48 @@ ctc_shutdown_device(struct ccwgroup_device *cgdev) struct net_device *ndev; + pr_debug("%s() called\n", __FUNCTION__); + priv = cgdev->dev.driver_data; + ndev = NULL; if (!priv) return -ENODEV; - ndev = priv->channel[READ]->netdev; - /* Close the device */ - ctc_close(ndev); - ndev->flags &=~IFF_RUNNING; + if (priv->channel[READ]) { + ndev = priv->channel[READ]->netdev; - ctc_remove_attributes(&cgdev->dev); + /* Close the device */ + ctc_close(ndev); + ndev->flags &=~IFF_RUNNING; - channel_free(priv->channel[READ]); - channel_free(priv->channel[WRITE]); + ctc_remove_attributes(&cgdev->dev); - ctc_netdev_unregister(ndev); - ndev->priv = NULL; - ctc_free_netdevice(ndev, 1); + channel_free(priv->channel[READ]); + } + if (priv->channel[WRITE]) + channel_free(priv->channel[WRITE]); + + if (ndev) { + sysfs_remove_link(&ndev->class_dev.kobj, cgdev->dev.bus_id); + sysfs_remove_link(&cgdev->dev.kobj, ndev->name); + ctc_netdev_unregister(ndev); + ndev->priv = NULL; + ctc_free_netdevice(ndev, 1); + } - kfree_fsm(priv->fsm); + if (priv->fsm) + kfree_fsm(priv->fsm); ccw_device_set_offline(cgdev->cdev[1]); ccw_device_set_offline(cgdev->cdev[0]); - channel_remove(priv->channel[READ]); - channel_remove(priv->channel[WRITE]); + if (priv->channel[READ]) + channel_remove(priv->channel[READ]); + if (priv->channel[WRITE]) + channel_remove(priv->channel[WRITE]); + priv->channel[READ] = priv->channel[WRITE] = NULL; + return 0; } @@ -2997,6 +3147,8 @@ ctc_remove_device(struct ccwgroup_device *cgdev) { struct ctc_priv *priv; + pr_debug("%s() called\n", __FUNCTION__); + priv = cgdev->dev.driver_data; if (!priv) return; @@ -3033,7 +3185,7 @@ ctc_exit(void) { unregister_cu3088_discipline(&ctc_group_driver); ctc_tty_cleanup(); - printk(KERN_INFO "CTC driver unloaded\n"); + ctc_pr_info("CTC driver unloaded\n"); } /** diff --git a/drivers/s390/net/ctctty.c b/drivers/s390/net/ctctty.c index 1be60e7bbc4e..e9d81d4ebc47 100644 --- a/drivers/s390/net/ctctty.c +++ b/drivers/s390/net/ctctty.c @@ -1,5 +1,5 @@ /* - * $Id: ctctty.c,v 1.15 2004/01/26 10:21:01 mschwide Exp $ + * $Id: ctctty.c,v 1.16 2004/02/05 12:39:55 felfert Exp $ * * CTC / ESCON network driver, tty interface. * @@ -655,14 +655,19 @@ ctc_tty_get_lsr_info(ctc_tty_info * info, uint * value) } -static int -ctc_tty_get_ctc_tty_info(ctc_tty_info * info, uint * value) +static int ctc_tty_tiocmget(struct tty_struct *tty, struct file *file) { + ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; u_char control, status; uint result; ulong flags; + if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl")) + return -ENODEV; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + control = info->mcr; spin_lock_irqsave(&ctc_tty_lock, flags); status = info->msr; @@ -673,51 +678,31 @@ ctc_tty_get_ctc_tty_info(ctc_tty_info * info, uint * value) | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); - put_user(result, (uint *) value); - return 0; + return result; } static int -ctc_tty_set_ctc_tty_info(ctc_tty_info * info, uint cmd, uint * value) +ctc_tty_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) { - uint arg; - int old_mcr = info->mcr & (UART_MCR_RTS | UART_MCR_DTR); + ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; - get_user(arg, (uint *) value); - switch (cmd) { - case TIOCMBIS: -#ifdef CTC_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "%s%d ioctl TIOCMBIS\n", CTC_TTY_NAME, - info->line); -#endif - if (arg & TIOCM_RTS) - info->mcr |= UART_MCR_RTS; - if (arg & TIOCM_DTR) - info->mcr |= UART_MCR_DTR; - break; - case TIOCMBIC: -#ifdef CTC_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "%s%d ioctl TIOCMBIC\n", CTC_TTY_NAME, - info->line); -#endif - if (arg & TIOCM_RTS) - info->mcr &= ~UART_MCR_RTS; - if (arg & TIOCM_DTR) - info->mcr &= ~UART_MCR_DTR; - break; - case TIOCMSET: -#ifdef CTC_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "%s%d ioctl TIOCMSET\n", CTC_TTY_NAME, - info->line); -#endif - info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR)) - | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) - | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); - break; - default: - return -EINVAL; - } - if ((info->mcr & (UART_MCR_RTS | UART_MCR_DTR)) != old_mcr) + if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl")) + return -ENODEV; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + + if (set & TIOCM_RTS) + info->mcr |= UART_MCR_RTS; + if (set & TIOCM_DTR) + info->mcr |= UART_MCR_DTR; + + if (clear & TIOCM_RTS) + info->mcr &= ~UART_MCR_RTS; + if (clear & TIOCM_DTR) + info->mcr &= ~UART_MCR_DTR; + + if ((set | clear) & (TIOCM_RTS|TIOCM_DTR)) ctc_tty_transmit_status(info); return 0; } @@ -772,22 +757,6 @@ ctc_tty_ioctl(struct tty_struct *tty, struct file *file, ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; - case TIOCMGET: -#ifdef CTC_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "%s%d ioctl TIOCMGET\n", CTC_TTY_NAME, - info->line); -#endif - error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint)); - if (error) - return error; - return ctc_tty_get_ctc_tty_info(info, (uint *) arg); - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint)); - if (error) - return error; - return ctc_tty_set_ctc_tty_info(info, cmd, (uint *) arg); case TIOCSERGETLSR: /* Get line status register */ #ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TIOCSERGETLSR\n", CTC_TTY_NAME, @@ -1139,6 +1108,8 @@ static struct tty_operations ctc_ops = { .unthrottle = ctc_tty_unthrottle, .set_termios = ctc_tty_set_termios, .hangup = ctc_tty_hangup, + .tiocmget = ctc_tty_tiocmget, + .tiocmset = ctc_tty_tiocmset, }; int @@ -1259,9 +1230,9 @@ ctc_tty_cleanup(void) { spin_lock_irqsave(&ctc_tty_lock, saveflags); ctc_tty_shuttingdown = 1; + spin_unlock_irqrestore(&ctc_tty_lock, saveflags); tty_unregister_driver(driver->ctc_tty_device); kfree(driver); put_tty_driver(driver->ctc_tty_device); driver = NULL; - spin_unlock_irqrestore(&ctc_tty_lock, saveflags); } diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c index 7a6e4eaf34e1..b1b62823c2e6 100644 --- a/drivers/s390/net/iucv.c +++ b/drivers/s390/net/iucv.c @@ -1,5 +1,5 @@ /* - * $Id: iucv.c,v 1.19 2003/12/18 15:28:49 braunu Exp $ + * $Id: iucv.c,v 1.24 2004/02/05 14:16:01 braunu Exp $ * * IUCV network driver * @@ -29,10 +29,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.19 $ + * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.24 $ * */ +/* #define DEBUG */ + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/config.h> @@ -53,8 +55,6 @@ #include <asm/ebcdic.h> #include <asm/ccwdev.h> //for root device stuff -#define DEBUG - /* FLAGS: * All flags are defined in the field IPFLAGS1 of each function * and can be found in CP Programming Services. @@ -104,6 +104,8 @@ static iucv_GeneralInterrupt *iucv_external_int_buffer; static spinlock_t iucv_lock = SPIN_LOCK_UNLOCKED; +static int messagesDisabled = 0; + /***************INTERRUPT HANDLING ***************/ typedef struct { @@ -349,7 +351,7 @@ do { \ static void iucv_banner(void) { - char vbuf[] = "$Revision: 1.19 $"; + char vbuf[] = "$Revision: 1.24 $"; char *version = vbuf; if ((version = strchr(version, ':'))) { @@ -433,10 +435,13 @@ iucv_init(void) * * Frees everything allocated from iucv_init. */ +static int iucv_retrieve_buffer (void); + static void iucv_exit(void) { - if (iucv_external_int_buffer) + iucv_retrieve_buffer(); + if (iucv_external_int_buffer) kfree(iucv_external_int_buffer); if (iucv_param_pool) kfree(iucv_param_pool); @@ -716,7 +721,6 @@ iucv_remove_handler(handler *handler) spin_lock_irqsave (&iucv_lock, flags); list_del(&handler->list); if (list_empty(&iucv_handler_table)) { - iucv_retrieve_buffer(); if (register_flag) { unregister_external_interrupt(0x4000, iucv_irq_handler); register_flag = 0; @@ -1028,6 +1032,8 @@ iucv_accept(__u16 pathid, __u16 msglim_reqstd, b2f0_result = b2f0(ACCEPT, parm); if (b2f0_result == 0) { + if (msglim) + *msglim = parm->ipmsglim; if (pgm_data) h->pgm_data = pgm_data; if (flags1_out) @@ -1083,6 +1089,7 @@ iucv_connect (__u16 *pathid, __u16 msglim_reqstd, iucv_handle_t handle, void *pgm_data) { iparml_control *parm; + iparml_control local_parm; struct list_head *lh; ulong b2f0_result = 0; ulong flags; @@ -1139,27 +1146,53 @@ iucv_connect (__u16 *pathid, __u16 msglim_reqstd, EBC_TOUPPER(parm->iptarget, sizeof(parm->iptarget)); } + /* In order to establish an IUCV connection, the procedure is: + * + * b2f0(CONNECT) + * take the ippathid from the b2f0 call + * register the handler to the ippathid + * + * Unfortunately, the ConnectionEstablished message gets sent after the + * b2f0(CONNECT) call but before the register is handled. + * + * In order for this race condition to be eliminated, the IUCV Control + * Interrupts must be disabled for the above procedure. + * + * David Kennedy <dkennedy@linuxcare.com> + */ + + /* Enable everything but IUCV Control messages */ + iucv_setmask(~(AllInterrupts)); + messagesDisabled = 1; + spin_lock_irqsave (&iucv_lock, flags); parm->ipflags1 = (__u8)flags1; b2f0_result = b2f0(CONNECT, parm); + memcpy(&local_parm, parm, sizeof(local_parm)); + release_param(parm); + parm = &local_parm; if (b2f0_result == 0) add_pathid_result = __iucv_add_pathid(parm->ippathid, h); spin_unlock_irqrestore (&iucv_lock, flags); if (b2f0_result) { - release_param(parm); + iucv_setmask(~0); + messagesDisabled = 0; return b2f0_result; } *pathid = parm->ippathid; + /* Enable everything again */ + iucv_setmask(IUCVControlInterruptsFlag); + if (msglim) *msglim = parm->ipmsglim; if (flags1_out) *flags1_out = (parm->ipflags1 & IPPRTY) ? IPPRTY : 0; if (add_pathid_result) { - iucv_sever(parm->ippathid, no_memory); + iucv_sever(*pathid, no_memory); printk(KERN_WARNING "%s: add_pathid failed with rc =" " %d\n", __FUNCTION__, add_pathid_result); return(add_pathid_result); @@ -2142,6 +2175,24 @@ iucv_send2way_prmmsg_array (__u16 pathid, return b2f0_result; } +void +iucv_setmask_cpu0 (void *result) +{ + iparml_set_mask *parm; + + if (smp_processor_id() != 0) + return; + + iucv_debug(1, "entering"); + parm = (iparml_set_mask *)grab_param(); + parm->ipmask = *((__u8*)result); + *((ulong *)result) = b2f0(SETMASK, parm); + release_param(parm); + + iucv_debug(1, "b2f0_result = %ld", *((ulong *)result)); + iucv_debug(1, "exiting"); +} + /* * Name: iucv_setmask * Purpose: This function enables or disables the following IUCV @@ -2152,28 +2203,25 @@ iucv_send2way_prmmsg_array (__u16 pathid, * 0x40 - Priority_MessagePendingInterruptsFlag * 0x20 - Nonpriority_MessageCompletionInterruptsFlag * 0x10 - Priority_MessageCompletionInterruptsFlag + * 0x08 - IUCVControlInterruptsFlag * Output: NA * Return: b2f0_result - return code from CP */ int iucv_setmask (int SetMaskFlag) { - iparml_set_mask *parm; - ulong b2f0_result = 0; - - iucv_debug(1, "entering"); - - parm = (iparml_set_mask *)grab_param(); - - parm->ipmask = (__u8)SetMaskFlag; - - b2f0_result = b2f0(SETMASK, parm); - release_param(parm); + union { + ulong result; + __u8 param; + } u; - iucv_debug(1, "b2f0_result = %ld", b2f0_result); - iucv_debug(1, "exiting"); + u.param = SetMaskFlag; + if (smp_processor_id() == 0) + iucv_setmask_cpu0(&u); + else + smp_call_function(iucv_setmask_cpu0, &u, 0, 1); - return b2f0_result; + return u.result; } /** @@ -2280,6 +2328,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) /* end of if statement */ switch (int_buf->iptype) { case 0x01: /* connection pending */ + if (messagesDisabled) { + iucv_setmask(~0); + messagesDisabled = 0; + } spin_lock_irqsave(&iucv_lock, flags); list_for_each(lh, &iucv_handler_table) { h = list_entry(lh, handler, list); @@ -2328,11 +2380,17 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) break; case 0x02: /*connection complete */ + if (messagesDisabled) { + iucv_setmask(~0); + messagesDisabled = 0; + } if (h) { if (interrupt->ConnectionComplete) + { interrupt->ConnectionComplete( (iucv_ConnectionComplete *)int_buf, h->pgm_data); + } else iucv_debug(1, "ConnectionComplete not called"); @@ -2341,6 +2399,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) break; case 0x03: /* connection severed */ + if (messagesDisabled) { + iucv_setmask(~0); + messagesDisabled = 0; + } if (h) { if (interrupt->ConnectionSevered) interrupt->ConnectionSevered( @@ -2354,6 +2416,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) break; case 0x04: /* connection quiesced */ + if (messagesDisabled) { + iucv_setmask(~0); + messagesDisabled = 0; + } if (h) { if (interrupt->ConnectionQuiesced) interrupt->ConnectionQuiesced( @@ -2366,6 +2432,10 @@ iucv_do_int(iucv_GeneralInterrupt * int_buf) break; case 0x05: /* connection resumed */ + if (messagesDisabled) { + iucv_setmask(~0); + messagesDisabled = 0; + } if (h) { if (interrupt->ConnectionResumed) interrupt->ConnectionResumed( @@ -2467,7 +2537,9 @@ EXPORT_SYMBOL (iucv_quiesce); EXPORT_SYMBOL (iucv_receive); #if 0 EXPORT_SYMBOL (iucv_receive_array); +#endif EXPORT_SYMBOL (iucv_reject); +#if 0 EXPORT_SYMBOL (iucv_reply); EXPORT_SYMBOL (iucv_reply_array); EXPORT_SYMBOL (iucv_reply_prmmsg); diff --git a/drivers/s390/net/iucv.h b/drivers/s390/net/iucv.h index 49f84e20308f..b3f5e2d5edd4 100644 --- a/drivers/s390/net/iucv.h +++ b/drivers/s390/net/iucv.h @@ -62,6 +62,8 @@ #define Priority_MessagePendingInterruptsFlag 0x40 #define Nonpriority_MessageCompletionInterruptsFlag 0x20 #define Priority_MessageCompletionInterruptsFlag 0x10 +#define IUCVControlInterruptsFlag 0x08 +#define AllInterrupts 0xf8 /* * Mapping of external interrupt buffers should be used with the corresponding * interrupt types. @@ -738,6 +740,7 @@ int iucv_send2way_prmmsg_array (u16 pathid, * 0x40 - Priority_MessagePendingInterruptsFlag * 0x20 - Nonpriority_MessageCompletionInterruptsFlag * 0x10 - Priority_MessageCompletionInterruptsFlag + * 0x08 - IUCVControlInterruptsFlag * Output: NA * Return: Return code from CP IUCV call. */ diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index fb6da78eeb57..bd9f8cc03d94 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -11,7 +11,7 @@ * Frank Pavlic (pavlic@de.ibm.com) and * Martin Schwidefsky <schwidefsky@de.ibm.com> * - * $Revision: 1.61 $ $Date: 2003/12/02 15:18:50 $ + * $Revision: 1.66 $ $Date: 2004/02/19 13:46:01 $ * * 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 @@ -58,7 +58,7 @@ /** * initialization string for output */ -#define VERSION_LCS_C "$Revision: 1.61 $" +#define VERSION_LCS_C "$Revision: 1.66 $" static char version[] __initdata = "LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")"; @@ -100,7 +100,7 @@ lcs_register_debug_facility(void) debug_register_view(lcs_dbf_setup, &debug_hex_ascii_view); debug_set_level(lcs_dbf_setup, 5); debug_register_view(lcs_dbf_trace, &debug_hex_ascii_view); - debug_set_level(lcs_dbf_trace, 3); + debug_set_level(lcs_dbf_trace, 5); return 0; } @@ -141,8 +141,11 @@ lcs_free_channel(struct lcs_channel *channel) int cnt; LCS_DBF_TEXT(3, setup, "ichfree"); - for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) - kfree(channel->iob[cnt].data); + for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) { + if (channel->iob[cnt].data != NULL) + kfree(channel->iob[cnt].data); + channel->iob[cnt].data = NULL; + } } /** @@ -360,7 +363,8 @@ lcs_cleanup_card(struct lcs_card *card) kfree(ipm_list); } #endif - free_netdev(card->dev); + if (card->dev != NULL) + free_netdev(card->dev); /* Cleanup channels. */ lcs_cleanup_channel(&card->write); lcs_cleanup_channel(&card->read); @@ -672,6 +676,7 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, struct lcs_cmd *cmd; struct timer_list timer; int rc; + char buf[16]; cmd = (struct lcs_cmd *) buffer->data; cmd->sequence_no = ++card->sequence_no; @@ -695,6 +700,9 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, add_timer(&timer); wait_event(reply.wait_q, reply.received); del_timer(&timer); + LCS_DBF_TEXT(5, trace, "sendcmd"); + sprintf(buf, "rc:%d", reply.rc); + LCS_DBF_TEXT(5, trace, buf); return reply.rc ? -EIO : 0; } @@ -794,7 +802,7 @@ lcs_send_startlan(struct lcs_card *card, __u8 initiator) struct lcs_buffer *buffer; struct lcs_cmd *cmd; - LCS_DBF_TEXT(2, trace, "cmdstpln"); + LCS_DBF_TEXT(2, trace, "cmdstaln"); buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; cmd->cmd_code = LCS_CMD_STARTLAN; @@ -1042,7 +1050,8 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) LCS_DBF_TEXT(5, trace, dbf_text); /* How far in the ccw chain have we processed? */ - if (channel->state != CH_STATE_INIT) { + if ((channel->state != CH_STATE_INIT) && + (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) { index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa) - channel->ccws; if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) || @@ -1066,9 +1075,14 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* CCW execution stopped on a suspend bit. */ channel->state = CH_STATE_SUSPENDED; - if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) + if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) { + if (irb->scsw.cc != 0) { + ccw_device_halt(channel->ccwdev, (addr_t) channel); + return; + } /* The channel has been stopped by halt_IO. */ channel->state = CH_STATE_HALTED; + } /* Do the rest in the tasklet. */ tasklet_schedule(&channel->irq_tasklet); @@ -1267,7 +1281,7 @@ lcs_startlan(struct lcs_card *card) else rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); } else { - for (i = 0; i <= card->max_port_no; i++) { + for (i = 0; i <= 16; i++) { card->portno = i; if (card->lan_type != LCS_FRAME_TYPE_AUTO) rc = lcs_send_startlan(card, @@ -1291,7 +1305,7 @@ lcs_startlan(struct lcs_card *card) static int lcs_detect(struct lcs_card *card) { - int rc; + int rc = 0; LCS_DBF_TEXT(3, setup," lcsdetct"); /* start/reset card */ @@ -1790,7 +1804,6 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) if (rc) { LCS_DBF_TEXT(3, setup, "errinit"); PRINT_ERR("LCS card Initialization failed\n"); - lcs_free_card(card); return rc; } @@ -1798,7 +1811,6 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) if (rc) { lcs_stopcard(card); lcs_cleanup_card(card); - lcs_free_card(card); return -ENODEV; } switch (card->lan_type) { @@ -1859,7 +1871,6 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) return 0; out: lcs_cleanup_card(card); - lcs_free_card(card); return -ENODEV; } @@ -1932,9 +1943,9 @@ __init lcs_init_module(void) { int rc; - LCS_DBF_TEXT(0, setup, "lcsinit"); PRINT_INFO("Loading %s\n",version); rc = lcs_register_debug_facility(); + LCS_DBF_TEXT(0, setup, "lcsinit"); if (rc) { PRINT_ERR("Initialization failed\n"); return rc; diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index ba62eca087ac..2ea792784713 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1,5 +1,5 @@ /* - * $Id: netiucv.c,v 1.30 2003/12/02 12:29:32 braunu Exp $ + * $Id: netiucv.c,v 1.38 2004/02/19 13:12:57 mschwide Exp $ * * IUCV network driver * @@ -30,10 +30,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: IUCV network driver $Revision: 1.30 $ + * RELEASE-TAG: IUCV network driver $Revision: 1.38 $ * */ +#undef DEBUG + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -62,8 +64,6 @@ #include "iucv.h" #include "fsm.h" -#undef DEBUG - MODULE_AUTHOR ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)"); MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver"); @@ -80,6 +80,8 @@ struct connection_profile { unsigned long txlen; unsigned long tx_time; struct timespec send_stamp; + unsigned long tx_pending; + unsigned long tx_max_pending; }; /** @@ -92,6 +94,7 @@ struct iucv_connection { struct sk_buff *rx_buff; struct sk_buff *tx_buff; struct sk_buff_head collect_queue; + struct sk_buff_head commit_queue; spinlock_t collect_lock; int collect_len; int max_buffsize; @@ -131,7 +134,8 @@ struct netiucv_priv { unsigned long tbusy; fsm_instance *fsm; struct iucv_connection *conn; - struct device dev; + struct device *dev; + fsm_timer timer; }; /** @@ -213,6 +217,7 @@ enum dev_states { DEV_STATE_STARTWAIT, DEV_STATE_STOPWAIT, DEV_STATE_RUNNING, + DEV_STATE_STARTRETRY, /** * MUST be always the last element!! */ @@ -234,6 +239,7 @@ enum dev_events { DEV_EVENT_STOP, DEV_EVENT_CONUP, DEV_EVENT_CONDOWN, + DEV_EVENT_TIMER, /** * MUST be always the last element!! */ @@ -530,7 +536,11 @@ netiucv_unpack_skb(struct iucv_connection *conn, struct sk_buff *pskb) skb->dev = pskb->dev; skb->protocol = pskb->protocol; pskb->ip_summed = CHECKSUM_UNNECESSARY; - netif_rx(skb); + /* + * Since receiving is always initiated from a tasklet (in iucv.c), + * we must use netif_rx_ni() instead of netif_rx() + */ + netif_rx_ni(skb); dev->last_rx = jiffies; privptr->stats.rx_packets++; privptr->stats.rx_bytes += skb->len; @@ -582,10 +592,11 @@ conn_action_txdone(fsm_instance *fi, int event, void *arg) iucv_MessageComplete *eib = (iucv_MessageComplete *)ev->data; struct netiucv_priv *privptr = NULL; /* Shut up, gcc! skb is always below 2G. */ - struct sk_buff *skb = (struct sk_buff *)(unsigned long)eib->ipmsgtag; + __u32 single_flag = eib->ipmsgtag; __u32 txbytes = 0; __u32 txpackets = 0; __u32 stat_maxcq = 0; + struct sk_buff *skb; unsigned long saveflags; ll_header header; @@ -594,13 +605,17 @@ conn_action_txdone(fsm_instance *fi, int event, void *arg) fsm_deltimer(&conn->timer); if (conn && conn->netdev && conn->netdev->priv) privptr = (struct netiucv_priv *)conn->netdev->priv; - if (skb) { + conn->prof.tx_pending--; + if (single_flag) { + if ((skb = skb_dequeue(&conn->commit_queue))) { + atomic_dec(&skb->users); + dev_kfree_skb_any(skb); + } if (privptr) { privptr->stats.tx_packets++; privptr->stats.tx_bytes += (skb->len - NETIUCV_HDRLEN - NETIUCV_HDRLEN); } - dev_kfree_skb_any(skb); } conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head; conn->tx_buff->len = 0; @@ -634,11 +649,17 @@ conn_action_txdone(fsm_instance *fi, int event, void *arg) conn->tx_buff->data, conn->tx_buff->len); conn->prof.doios_multi++; conn->prof.txlen += conn->tx_buff->len; + conn->prof.tx_pending++; + if (conn->prof.tx_pending > conn->prof.tx_max_pending) + conn->prof.tx_max_pending = conn->prof.tx_pending; if (rc != 0) { fsm_deltimer(&conn->timer); + conn->prof.tx_pending--; fsm_newstate(fi, CONN_STATE_IDLE); if (privptr) privptr->stats.tx_errors += txpackets; + printk(KERN_DEBUG "iucv_send returned %08x\n", + rc); } else { if (privptr) { privptr->stats.tx_packets += txpackets; @@ -722,6 +743,12 @@ conn_action_connsever(fsm_instance *fi, int event, void *arg) pr_debug("%s() called\n", __FUNCTION__); switch (state) { + case CONN_STATE_SETUPWAIT: + printk(KERN_INFO "%s: Remote dropped connection\n", + netdev->name); + fsm_newstate(fi, CONN_STATE_STOPPED); + fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev); + break; case CONN_STATE_IDLE: case CONN_STATE_TX: printk(KERN_INFO "%s: Remote dropped connection\n", @@ -763,10 +790,14 @@ conn_action_start(fsm_instance *fi, int event, void *arg) pr_debug("%s('%s'): connecting ...\n", conn->netdev->name, conn->userid); + /* We must set the state before calling iucv_connect because the callback + * handler could be called at any point after the connection request is + * sent */ + + fsm_newstate(fi, CONN_STATE_SETUPWAIT); rc = iucv_connect(&(conn->pathid), NETIUCV_QUEUELEN_DEFAULT, iucvMagic, conn->userid, iucv_host, 0, NULL, NULL, conn->handle, conn); - fsm_newstate(fi, CONN_STATE_SETUPWAIT); switch (rc) { case 0: return; @@ -840,6 +871,7 @@ conn_action_stop(fsm_instance *fi, int event, void *arg) if (conn->handle) iucv_unregister_program(conn->handle); conn->handle = 0; + netiucv_purge_skb_queue(&conn->commit_queue); fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev); } @@ -860,6 +892,7 @@ static const fsm_node conn_fsm[] = { { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start }, { CONN_STATE_STARTWAIT, CONN_EVENT_START, conn_action_start }, + { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop }, { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop }, { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop }, { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop }, @@ -883,6 +916,7 @@ static const fsm_node conn_fsm[] = { { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx }, { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone }, + { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone }, }; static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node); @@ -908,6 +942,7 @@ dev_action_start(fsm_instance *fi, int event, void *arg) pr_debug("%s() called\n", __FUNCTION__); + fsm_deltimer(&privptr->timer); ev.conn = privptr->conn; fsm_newstate(fi, DEV_STATE_STARTWAIT); fsm_event(privptr->conn->fsm, CONN_EVENT_START, &ev); @@ -931,6 +966,7 @@ dev_action_stop(fsm_instance *fi, int event, void *arg) ev.conn = privptr->conn; + fsm_deltimer(&privptr->timer); fsm_newstate(fi, DEV_STATE_STOPWAIT); fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev); } @@ -947,10 +983,13 @@ static void dev_action_connup(fsm_instance *fi, int event, void *arg) { struct net_device *dev = (struct net_device *)arg; + struct netiucv_priv *privptr = dev->priv; pr_debug("%s() called\n", __FUNCTION__); switch (fsm_getstate(fi)) { + case DEV_STATE_STARTRETRY: + fsm_deltimer(&privptr->timer); case DEV_STATE_STARTWAIT: fsm_newstate(fi, DEV_STATE_RUNNING); printk(KERN_INFO @@ -989,6 +1028,9 @@ dev_action_conndown(fsm_instance *fi, int event, void *arg) fsm_event(privptr->conn->fsm, CONN_EVENT_START, &ev); break; case DEV_STATE_STARTWAIT: + fsm_addtimer(&privptr->timer, NETIUCV_TIMEOUT_5SEC, + DEV_EVENT_TIMER, dev); + fsm_newstate(fi, DEV_STATE_STARTRETRY); break; case DEV_STATE_STOPWAIT: fsm_newstate(fi, DEV_STATE_STOPPED); @@ -997,18 +1039,22 @@ dev_action_conndown(fsm_instance *fi, int event, void *arg) } static const fsm_node dev_fsm[] = { - { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start }, + { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start }, + + { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start }, + { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown }, - { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start }, - { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown }, + { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop }, + { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup }, + { DEV_STATE_STARTWAIT, DEV_EVENT_CONDOWN, dev_action_conndown }, - { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop }, - { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup }, - { DEV_STATE_STARTWAIT, DEV_EVENT_CONDOWN, dev_action_conndown }, + { DEV_STATE_STARTRETRY, DEV_EVENT_TIMER, dev_action_start }, + { DEV_STATE_STARTRETRY, DEV_EVENT_CONUP, dev_action_connup }, + { DEV_STATE_STARTRETRY, DEV_EVENT_STOP, dev_action_stop }, - { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop }, - { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown }, - { DEV_STATE_RUNNING, DEV_EVENT_CONUP, fsm_action_nop }, + { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop }, + { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown }, + { DEV_STATE_RUNNING, DEV_EVENT_CONUP, fsm_action_nop }, }; static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node); @@ -1081,14 +1127,22 @@ netiucv_transmit_skb(struct iucv_connection *conn, struct sk_buff *skb) { CONN_EVENT_TIMER, conn); conn->prof.send_stamp = xtime; - rc = iucv_send(conn->pathid, NULL, 0, 0, + rc = iucv_send(conn->pathid, NULL, 0, 0, 1 /* single_flag */, + 0, nskb->data, nskb->len); /* Shut up, gcc! nskb is always below 2G. */ - (__u32)(((unsigned long)nskb)&0xffffffff), 0, - nskb->data, nskb->len); conn->prof.doios_single++; conn->prof.txlen += skb->len; + conn->prof.tx_pending++; + if (conn->prof.tx_pending > conn->prof.tx_max_pending) + conn->prof.tx_max_pending = conn->prof.tx_pending; if (rc != 0) { + struct netiucv_priv *privptr; fsm_deltimer(&conn->timer); + fsm_newstate(conn->fsm, CONN_STATE_IDLE); + conn->prof.tx_pending--; + privptr = (struct netiucv_priv *)conn->netdev->priv; + if (privptr) + privptr->stats.tx_errors++; if (copied) dev_kfree_skb(nskb); else { @@ -1099,9 +1153,13 @@ netiucv_transmit_skb(struct iucv_connection *conn, struct sk_buff *skb) { skb_pull(skb, NETIUCV_HDRLEN); skb_trim(skb, skb->len - NETIUCV_HDRLEN); } + printk(KERN_DEBUG "iucv_send returned %08x\n", + rc); } else { if (copied) dev_kfree_skb(skb); + atomic_inc(&nskb->users); + skb_queue_tail(&conn->commit_queue, nskb); } } @@ -1256,8 +1314,7 @@ static ssize_t buffer_write (struct device *dev, const char *buf, size_t count) { struct netiucv_priv *priv = dev->driver_data; - struct net_device *ndev = - container_of((void *)priv, struct net_device, priv); + struct net_device *ndev = priv->conn->netdev; char *e; int bs1; char tmp[CTRL_BUFSIZE]; @@ -1266,7 +1323,7 @@ buffer_write (struct device *dev, const char *buf, size_t count) return -EINVAL; if (copy_from_user(tmp, buf, count)) - return -EFAULT; + return -EFAULT; tmp[count+1] = '\0'; bs1 = simple_strtoul(tmp, &e, 0); @@ -1425,6 +1482,44 @@ txtime_write (struct device *dev, const char *buf, size_t count) static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write); +static ssize_t +txpend_show (struct device *dev, char *buf) +{ + struct netiucv_priv *priv = dev->driver_data; + + return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending); +} + +static ssize_t +txpend_write (struct device *dev, const char *buf, size_t count) +{ + struct netiucv_priv *priv = dev->driver_data; + + priv->conn->prof.tx_pending = 0; + return count; +} + +static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write); + +static ssize_t +txmpnd_show (struct device *dev, char *buf) +{ + struct netiucv_priv *priv = dev->driver_data; + + return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending); +} + +static ssize_t +txmpnd_write (struct device *dev, const char *buf, size_t count) +{ + struct netiucv_priv *priv = dev->driver_data; + + priv->conn->prof.tx_max_pending = 0; + return count; +} + +static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write); + static struct attribute *netiucv_attrs[] = { &dev_attr_buffer.attr, &dev_attr_user.attr, @@ -1444,6 +1539,8 @@ static struct attribute *netiucv_stat_attrs[] = { &dev_attr_tx_multi_write_ops.attr, &dev_attr_netto_bytes.attr, &dev_attr_max_tx_io_time.attr, + &dev_attr_tx_pending.attr, + &dev_attr_tx_max_pending.attr, NULL, }; @@ -1457,6 +1554,8 @@ netiucv_add_files(struct device *dev) { int ret; + pr_debug("%s() called\n", __FUNCTION__); + ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group); if (ret) return ret; @@ -1469,50 +1568,72 @@ netiucv_add_files(struct device *dev) static inline void netiucv_remove_files(struct device *dev) { + pr_debug("%s() called\n", __FUNCTION__); sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group); sysfs_remove_group(&dev->kobj, &netiucv_attr_group); } -/* - * XXX: Don't use sysfs unless you know WTF you are doing. - * This particular turd registers sysfs objects embedded into netiucv_priv - * which is kfreed without any regard to possible sysfs references. - * As the result, the wanker who'd decided that sysfs exports were too hip and - * cute to resist had generated a set of user-exploitable holes in this driver. - */ - static int netiucv_register_device(struct net_device *ndev, int ifno) { struct netiucv_priv *priv = ndev->priv; - struct device *dev = &priv->dev; + struct device *dev = kmalloc(sizeof(struct device), GFP_KERNEL); int ret; - char *str = "netiucv"; - snprintf(dev->bus_id, BUS_ID_SIZE, "%s%x", str, ifno); - dev->bus = &iucv_bus; - dev->parent = iucv_root; + + pr_debug("%s() called\n", __FUNCTION__); + + if (dev) { + memset(dev, 0, sizeof(struct device)); + snprintf(dev->bus_id, BUS_ID_SIZE, "netiucv%x", ifno); + dev->bus = &iucv_bus; + dev->parent = iucv_root; + /* + * The release function could be called after the + * module has been unloaded. It's _only_ task is to + * free the struct. Therefore, we specify kfree() + * directly here. (Probably a little bit obfuscating + * but legitime ...). + */ + dev->release = (void (*)(struct device *))kfree; + } else + return -ENOMEM; ret = device_register(dev); if (ret) return ret; - ret = netiucv_add_files(dev); - + if (ret) + goto out_unreg; + ret = sysfs_create_link(&dev->kobj, &ndev->class_dev.kobj, ndev->name); if (ret) - device_unregister(dev); - else - dev->driver_data = priv; + goto out_rm_files; + ret = sysfs_create_link(&ndev->class_dev.kobj, &dev->kobj, dev->bus_id); + if (ret) + goto out_rm_link; + dev->driver_data = priv; + priv->dev = dev; + return 0; + +out_rm_link: + sysfs_remove_link(&dev->kobj, ndev->name); +out_rm_files: + netiucv_remove_files(dev); +out_unreg: + device_unregister(dev); return ret; } static void -netiucv_unregister_device(struct net_device *ndev) +netiucv_unregister_device(struct device *dev) { - struct netiucv_priv *priv = (struct netiucv_priv*)ndev->priv; - struct device *dev = &priv->dev; - + struct netiucv_priv *priv = dev->driver_data; + struct net_device *ndev = priv->conn->netdev; + + pr_debug("%s() called\n", __FUNCTION__); + sysfs_remove_link(&ndev->class_dev.kobj, dev->bus_id); + sysfs_remove_link(&dev->kobj, ndev->name); netiucv_remove_files(dev); device_unregister(dev); } @@ -1532,6 +1653,7 @@ netiucv_new_connection(struct net_device *dev, char *username) if (conn) { memset(conn, 0, sizeof(struct iucv_connection)); skb_queue_head_init(&conn->collect_queue); + skb_queue_head_init(&conn->commit_queue); conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT; conn->netdev = dev; @@ -1581,6 +1703,8 @@ netiucv_remove_connection(struct iucv_connection *conn) { struct iucv_connection **clist = &connections; + pr_debug("%s() called\n", __FUNCTION__); + if (conn == NULL) return; while (*clist) { @@ -1600,14 +1724,48 @@ netiucv_remove_connection(struct iucv_connection *conn) } } -static void setup_netiucv(struct net_device *dev) +/** + * Release everything of a net device. + */ +static void +netiucv_free_netdevice(struct net_device *dev) +{ + struct netiucv_priv *privptr; + + pr_debug("%s() called\n", __FUNCTION__); + + if (!dev) + return; + + privptr = (struct netiucv_priv *)dev->priv; + if (privptr) { + if (privptr->fsm) + fsm_deltimer(&privptr->timer); + if (privptr->conn) + netiucv_remove_connection(privptr->conn); + if (privptr->fsm) + kfree_fsm(privptr->fsm); + privptr->conn = 0; privptr->fsm = 0; + /* privptr gets freed by free_netdev() */ + } + free_netdev(dev); +} + +/** + * Initialize a net device. (Called from kernel in alloc_netdev()) + */ +static void +netiucv_setup_netdevice(struct net_device *dev) { + memset(dev->priv, 0, sizeof(struct netiucv_priv)); + dev->mtu = NETIUCV_MTU_DEFAULT; dev->hard_start_xmit = netiucv_tx; dev->open = netiucv_open; dev->stop = netiucv_close; dev->get_stats = netiucv_stats; dev->change_mtu = netiucv_change_mtu; + dev->destructor = netiucv_free_netdevice; dev->hard_header_len = NETIUCV_HDRLEN; dev->addr_len = 0; dev->type = ARPHRD_SLIP; @@ -1623,61 +1781,32 @@ static struct net_device * netiucv_init_netdevice(int ifno, char *username) { struct netiucv_priv *privptr; - int priv_size; + struct net_device *dev; - struct net_device *dev = alloc_netdev(0, "", setup_netiucv); + dev = alloc_netdev(sizeof(struct netiucv_priv), "", + netiucv_setup_netdevice); if (!dev) return NULL; sprintf(dev->name, "iucv%d", ifno); - priv_size = sizeof(struct netiucv_priv); - dev->priv = kmalloc(priv_size, GFP_KERNEL); - if (dev->priv == NULL) { - free_netdev(dev); - return NULL; - } - memset(dev->priv, 0, priv_size); privptr = (struct netiucv_priv *)dev->priv; privptr->fsm = init_fsm("netiucvdev", dev_state_names, dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS, dev_fsm, DEV_FSM_LEN, GFP_KERNEL); if (privptr->fsm == NULL) { - kfree(privptr); free_netdev(dev); return NULL; } privptr->conn = netiucv_new_connection(dev, username); if (!privptr->conn) { kfree_fsm(privptr->fsm); - kfree(privptr); free_netdev(dev); return NULL; } - + fsm_settimer(privptr->fsm, &privptr->timer); fsm_newstate(privptr->fsm, DEV_STATE_STOPPED); - return dev; -} - -/** - * Allocate and initialize everything of a net device. - */ -static void -netiucv_free_netdevice(struct net_device *dev) -{ - struct netiucv_priv *privptr; - - if (!dev) - return; - privptr = (struct netiucv_priv *)dev->priv; - if (privptr) { - if (privptr->conn) - netiucv_remove_connection(privptr->conn); - if (privptr->fsm) - kfree_fsm(privptr->fsm); - kfree(privptr); - } - free_netdev(dev); + return dev; } static ssize_t @@ -1695,7 +1824,7 @@ conn_write(struct device_driver *drv, const char *buf, size_t count) } for (i=0, p=(char *)buf; i<8 && *p; i++, p++) { - if (isalnum(*p)) + if (isalnum(*p) || (*p == '$')) username[i]= *p; else if (*p == '\n') { /* trailing lf, grr */ @@ -1740,7 +1869,7 @@ static struct device_driver netiucv_driver = { static void netiucv_banner(void) { - char vbuf[] = "$Revision: 1.30 $"; + char vbuf[] = "$Revision: 1.38 $"; char *version = vbuf; if ((version = strchr(version, ':'))) { @@ -1756,10 +1885,12 @@ static void __exit netiucv_exit(void) { while (connections) { - struct net_device *dev = connections->netdev; - unregister_netdev(dev); + struct net_device *ndev = connections->netdev; + struct netiucv_priv *priv = (struct netiucv_priv*)ndev->priv; + struct device *dev = priv->dev; + + unregister_netdev(ndev); netiucv_unregister_device(dev); - netiucv_free_netdevice(dev); } driver_remove_file(&netiucv_driver, &driver_attr_connection); diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c new file mode 100644 index 000000000000..23b151c94855 --- /dev/null +++ b/drivers/s390/net/smsgiucv.c @@ -0,0 +1,180 @@ +/* + * IUCV special message driver + * + * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <asm/cpcmd.h> +#include <asm/ebcdic.h> + +#include "iucv.h" + +struct smsg_callback { + struct list_head list; + char *prefix; + int len; + void (*callback)(char *str); +}; + +MODULE_AUTHOR + ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky@de.ibm.com)"); +MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver"); + +static iucv_handle_t smsg_handle; +static unsigned short smsg_pathid; +static spinlock_t smsg_list_lock = SPIN_LOCK_UNLOCKED; +static struct list_head smsg_list = LIST_HEAD_INIT(smsg_list); + +static void +smsg_connection_complete(iucv_ConnectionComplete *eib, void *pgm_data) +{ +} + + +static void +smsg_message_pending(iucv_MessagePending *eib, void *pgm_data) +{ + struct smsg_callback *cb; + unsigned char *msg; + unsigned short len; + int rc; + + len = eib->ln1msg2.ipbfln1f; + msg = kmalloc(len + 1, GFP_ATOMIC|GFP_DMA); + if (!msg) { + iucv_reject(eib->ippathid, eib->ipmsgid, eib->iptrgcls); + return; + } + rc = iucv_receive(eib->ippathid, eib->ipmsgid, eib->iptrgcls, + msg, len, 0, 0, 0); + if (rc == 0) { + msg[len] = 0; + EBCASC(msg, len); + spin_lock(&smsg_list_lock); + list_for_each_entry(cb, &smsg_list, list) + if (strncmp(msg + 8, cb->prefix, cb->len) == 0) { + cb->callback(msg + 8); + break; + } + spin_unlock(&smsg_list_lock); + } + kfree(msg); +} + +static iucv_interrupt_ops_t smsg_ops = { + .ConnectionComplete = smsg_connection_complete, + .MessagePending = smsg_message_pending, +}; + +static struct device_driver smsg_driver = { + .name = "SMSGIUCV", + .bus = &iucv_bus, +}; + +int +smsg_register_callback(char *prefix, void (*callback)(char *str)) +{ + struct smsg_callback *cb; + + cb = kmalloc(sizeof(struct smsg_callback), GFP_KERNEL); + if (!cb) + return -ENOMEM; + cb->prefix = prefix; + cb->len = strlen(prefix); + cb->callback = callback; + spin_lock(&smsg_list_lock); + list_add_tail(&cb->list, &smsg_list); + spin_unlock(&smsg_list_lock); + return 0; +} + +void +smsg_unregister_callback(char *prefix, void (*callback)(char *str)) +{ + struct smsg_callback *cb, *tmp; + + spin_lock(&smsg_list_lock); + cb = 0; + list_for_each_entry(tmp, &smsg_list, list) + if (tmp->callback == callback && + strcmp(tmp->prefix, prefix) == 0) { + cb = tmp; + list_del(&cb->list); + break; + } + spin_unlock(&smsg_list_lock); + kfree(cb); +} + +static void __exit +smsg_exit(void) +{ + if (smsg_handle > 0) { + cpcmd("SET SMSG OFF", 0, 0); + iucv_sever(smsg_pathid, 0); + iucv_unregister_program(smsg_handle); + driver_unregister(&smsg_driver); + } + return; +} + +static int __init +smsg_init(void) +{ + static unsigned char pgmmask[24] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + int rc; + + rc = driver_register(&smsg_driver); + if (rc != 0) { + printk(KERN_ERR "SMSGIUCV: failed to register driver.\n"); + return rc; + } + smsg_handle = iucv_register_program("SMSGIUCV ", "*MSG ", + pgmmask, &smsg_ops, 0); + if (!smsg_handle) { + printk(KERN_ERR "SMSGIUCV: failed to register to iucv"); + driver_unregister(&smsg_driver); + return -EIO; /* better errno ? */ + } + rc = iucv_connect (&smsg_pathid, 1, 0, "*MSG ", 0, 0, 0, 0, + smsg_handle, 0); + if (rc) { + printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG"); + iucv_unregister_program(smsg_handle); + driver_unregister(&smsg_driver); + smsg_handle = 0; + return -EIO; + } + cpcmd("SET SMSG IUCV", 0, 0); + return 0; +} + +module_init(smsg_init); +module_exit(smsg_exit); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(smsg_register_callback); +EXPORT_SYMBOL(smsg_unregister_callback); diff --git a/drivers/s390/net/smsgiucv.h b/drivers/s390/net/smsgiucv.h new file mode 100644 index 000000000000..04cd87152964 --- /dev/null +++ b/drivers/s390/net/smsgiucv.h @@ -0,0 +1,10 @@ +/* + * IUCV special message driver + * + * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + */ + +int smsg_register_callback(char *, void (*)(char *)); +void smsg_unregister_callback(char *, void (*)(char *)); + diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index 92de215e0b80..2138af884f82 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -11,6 +11,7 @@ #include <linux/config.h> #include <linux/init.h> #include <linux/sched.h> +#include <linux/errno.h> #include <asm/lowcore.h> @@ -19,12 +20,14 @@ #define DBG printk // #define DBG(args,...) do {} while (0); +static struct semaphore m_sem; static struct semaphore s_sem; -extern void css_process_crw(int); -extern void chsc_process_crw(void); -extern void chp_process_crw(int, int); +extern int css_process_crw(int); +extern int chsc_process_crw(void); +extern int chp_process_crw(int, int); extern void css_reiterate_subchannels(void); +extern void css_trigger_slow_path(void); static void s390_handle_damage(char *msg) @@ -36,6 +39,21 @@ s390_handle_damage(char *msg) disabled_wait((unsigned long) __builtin_return_address(0)); } +static int +s390_mchk_slow_path(void *param) +{ + struct semaphore *sem; + + sem = (struct semaphore *)param; + /* Set a nice name. */ + daemonize("kslowcrw"); +repeat: + down_interruptible(sem); + css_trigger_slow_path(); + goto repeat; + return 0; +} + /* * Retrieve CRWs and call function to handle event. * @@ -45,15 +63,15 @@ static int s390_collect_crw_info(void *param) { struct crw crw; - int ccode; + int ccode, ret, slow; struct semaphore *sem; sem = (struct semaphore *)param; /* Set a nice name. */ daemonize("kmcheck"); - repeat: down_interruptible(sem); + slow = 0; while (1) { ccode = stcrw(&crw); if (ccode != 0) @@ -66,12 +84,15 @@ repeat: if (crw.oflw) { pr_debug("%s: crw overflow detected!\n", __FUNCTION__); css_reiterate_subchannels(); + slow = 1; continue; } switch (crw.rsc) { case CRW_RSC_SCH: pr_debug("source is subchannel %04X\n", crw.rsid); - css_process_crw (crw.rsid); + ret = css_process_crw (crw.rsid); + if (ret == -EAGAIN) + slow = 1; break; case CRW_RSC_MONITOR: pr_debug("source is monitoring facility\n"); @@ -80,28 +101,36 @@ repeat: pr_debug("source is channel path %02X\n", crw.rsid); switch (crw.erc) { case CRW_ERC_IPARM: /* Path has come. */ - chp_process_crw(crw.rsid, 1); + ret = chp_process_crw(crw.rsid, 1); break; case CRW_ERC_PERRI: /* Path has gone. */ - chp_process_crw(crw.rsid, 0); + case CRW_ERC_PERRN: + ret = chp_process_crw(crw.rsid, 0); break; default: pr_debug("Don't know how to handle erc=%x\n", crw.erc); + ret = 0; } + if (ret == -EAGAIN) + slow = 1; break; case CRW_RSC_CONFIG: pr_debug("source is configuration-alert facility\n"); break; case CRW_RSC_CSS: pr_debug("source is channel subsystem\n"); - chsc_process_crw(); + ret = chsc_process_crw(); + if (ret == -EAGAIN) + slow = 1; break; default: pr_debug("unknown source\n"); break; } } + if (slow) + up(&s_sem); goto repeat; return 0; } @@ -140,7 +169,7 @@ s390_do_machine_check(void) "check\n"); if (mci->cp) /* channel report word pending */ - up(&s_sem); + up(&m_sem); #ifdef CONFIG_MACHCHK_WARNING /* @@ -172,6 +201,7 @@ s390_do_machine_check(void) static int machine_check_init(void) { + init_MUTEX_LOCKED(&m_sem); init_MUTEX_LOCKED( &s_sem ); ctl_clear_bit(14, 25); /* disable damage MCH */ ctl_set_bit(14, 26); /* enable degradation MCH */ @@ -195,7 +225,8 @@ arch_initcall(machine_check_init); static int __init machine_check_crw_init (void) { - kernel_thread(s390_collect_crw_info, &s_sem, CLONE_FS|CLONE_FILES); + kernel_thread(s390_collect_crw_info, &m_sem, CLONE_FS|CLONE_FILES); + kernel_thread(s390_mchk_slow_path, &s_sem, CLONE_FS|CLONE_FILES); ctl_set_bit(14, 28); /* enable channel report MCH */ return 0; } diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index d34923a5b44a..ea7019547f92 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -4,11 +4,12 @@ * * FCP adapter driver for IBM eServer zSeries * - * Copyright 2002 IBM Corporation + * (C) Copyright IBM Corp. 2002, 2004 + * * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> - * Aron Zeh <arzeh@de.ibm.com> - * Wolfgang Taphorn <taphorn@de.ibm.com> + * Aron Zeh + * Wolfgang Taphorn * Stefan Bader <stefan.bader@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> * @@ -28,7 +29,7 @@ */ /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_AUX_REVISION "$Revision: 1.79 $" +#define ZFCP_AUX_REVISION "$Revision: 1.98 $" /********************** INCLUDES *********************************************/ @@ -51,6 +52,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/workqueue.h> +#include <linux/syscalls.h> #include "zfcp_ext.h" @@ -61,6 +63,9 @@ #include <asm/cpcmd.h> /* Debugging only */ #include <asm/processor.h> /* Debugging only */ +#include <linux/miscdevice.h> +#include <linux/major.h> + /* accumulated log level (module parameter) */ static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS; static char *device; @@ -73,7 +78,7 @@ static void __exit zfcp_module_exit(void); int zfcp_reboot_handler(struct notifier_block *, unsigned long, void *); /* FCP related */ -static void zfcp_nameserver_request_handler(struct zfcp_fsf_req *); +static void zfcp_ns_gid_pn_handler(unsigned long); /* miscellaneous */ #ifdef ZFCP_STAT_REQSIZES @@ -83,6 +88,34 @@ static int zfcp_statistics_clear(struct list_head *); static int zfcp_statistics_new(struct list_head *, u32); #endif +static inline int zfcp_sg_list_alloc(struct zfcp_sg_list *, size_t); +static inline int zfcp_sg_list_free(struct zfcp_sg_list *); +static inline int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *, void *, + size_t); +static inline int zfcp_sg_list_copy_to_user(void *, struct zfcp_sg_list *, + size_t); + +static int zfcp_cfdc_dev_ioctl(struct inode *, struct file *, + unsigned int, unsigned long); + +#define ZFCP_CFDC_IOC_MAGIC 0xDD +#define ZFCP_CFDC_IOC \ + _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_sense_data) + +#ifdef CONFIG_S390_SUPPORT +static struct ioctl_trans zfcp_ioctl_trans = {ZFCP_CFDC_IOC, (void*) sys_ioctl}; +#endif + +static struct file_operations zfcp_cfdc_fops = { + .ioctl = zfcp_cfdc_dev_ioctl +}; + +static struct miscdevice zfcp_cfdc_misc = { + .minor = ZFCP_CFDC_DEV_MINOR, + .name = ZFCP_CFDC_DEV_NAME, + .fops = &zfcp_cfdc_fops +}; + /*********************** KERNEL/MODULE PARAMETERS ***************************/ /* declare driver module init/cleanup functions */ @@ -128,7 +161,7 @@ _zfcp_hex_dump(char *addr, int count) if ((i % 32) == 31) printk("\n"); } - if ((i % 32) != 31) + if (((i-1) % 32) != 31) printk("\n"); } @@ -137,7 +170,6 @@ _zfcp_hex_dump(char *addr, int count) /****************************************************************/ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_OTHER #ifdef ZFCP_STAT_REQSIZES @@ -242,7 +274,7 @@ zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req, { #ifdef ZFCP_DEBUG_COMMANDS struct zfcp_adapter *adapter = fsf_req->adapter; - Scsi_Cmnd *scsi_cmnd; + struct scsi_cmnd *scsi_cmnd; int level = 3; int i; unsigned long flags; @@ -258,6 +290,8 @@ zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req, sizeof (u32)); debug_event(adapter->cmd_dbf, level, &scsi_cmnd, sizeof (unsigned long)); + debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd, + min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len)); for (i = 0; i < add_length; i += ZFCP_CMD_DBF_LENGTH) debug_event(adapter->cmd_dbf, level, @@ -268,8 +302,10 @@ zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req, #endif } +/* XXX additionally log unit if available */ +/* ---> introduce new parameter for unit, see 2.4 code */ void -zfcp_cmd_dbf_event_scsi(const char *text, Scsi_Cmnd * scsi_cmnd) +zfcp_cmd_dbf_event_scsi(const char *text, struct scsi_cmnd *scsi_cmnd) { #ifdef ZFCP_DEBUG_COMMANDS struct zfcp_adapter *adapter; @@ -287,6 +323,8 @@ zfcp_cmd_dbf_event_scsi(const char *text, Scsi_Cmnd * scsi_cmnd) debug_event(adapter->cmd_dbf, level, &scsi_cmnd->result, sizeof (u32)); debug_event(adapter->cmd_dbf, level, &scsi_cmnd, sizeof (unsigned long)); + debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd, + min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len)); if (likely(fsf_req)) { debug_event(adapter->cmd_dbf, level, &fsf_req, sizeof (unsigned long)); @@ -359,13 +397,12 @@ static void __init zfcp_init_device_configure(void) { int found = 0; - unsigned long flags; struct zfcp_adapter *adapter; struct zfcp_port *port; struct zfcp_unit *unit; down(&zfcp_data.config_sema); - read_lock_irqsave(&zfcp_data.config_lock, flags); + read_lock_irq(&zfcp_data.config_lock); list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) if (strcmp(zfcp_data.init_busid, zfcp_get_busid_by_adapter(adapter)) == 0) { @@ -373,7 +410,7 @@ zfcp_init_device_configure(void) found = 1; break; } - read_unlock_irqrestore(&zfcp_data.config_lock, flags); + read_unlock_irq(&zfcp_data.config_lock); if (!found) goto out_adapter; port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0); @@ -419,6 +456,28 @@ zfcp_module_init(void) zfcp_statistics_init_all(); #endif +#ifdef CONFIG_S390_SUPPORT + retval = register_ioctl32_conversion(zfcp_ioctl_trans.cmd, + zfcp_ioctl_trans.handler); + if (retval != 0) { + ZFCP_LOG_INFO("Cannot register a 32-bit support of " + "the IOC handler\n"); + goto out_ioctl32; + } +#endif + retval = misc_register(&zfcp_cfdc_misc); + if (retval != 0) { + ZFCP_LOG_INFO( + "Device file for the control file data channel " + "cannot be registered\n"); + goto out_misc_register; + } else { + ZFCP_LOG_INFO( + "Device file for the control file data channel " + "has become MAJOR/MINOR numbers %d/%d\n", + ZFCP_CFDC_DEV_MAJOR, zfcp_cfdc_misc.minor); + } + /* Initialise proc semaphores */ sema_init(&zfcp_data.config_sema, 1); @@ -445,6 +504,12 @@ zfcp_module_init(void) out_ccw_register: unregister_reboot_notifier(&zfcp_data.reboot_notifier); + misc_deregister(&zfcp_cfdc_misc); + out_misc_register: +#ifdef CONFIG_S390_SUPPORT + unregister_ioctl32_conversion(zfcp_ioctl_trans.cmd); + out_ioctl32: +#endif #ifdef ZFCP_STAT_REQSIZES zfcp_statistics_clear_all(); #endif @@ -458,6 +523,10 @@ zfcp_module_exit(void) { unregister_reboot_notifier(&zfcp_data.reboot_notifier); zfcp_ccw_unregister(); + misc_deregister(&zfcp_cfdc_misc); +#ifdef CONFIG_S390_SUPPORT + unregister_ioctl32_conversion(zfcp_ioctl_trans.cmd); +#endif #ifdef ZFCP_STAT_REQSIZES zfcp_statistics_clear_all(); #endif @@ -480,15 +549,372 @@ zfcp_reboot_handler(struct notifier_block *notifier, unsigned long code, return NOTIFY_DONE; } + +/* + * function: zfcp_cfdc_dev_ioctl + * + * purpose: Handle control file upload/download transaction via IOCTL + * interface + * + * returns: 0 - Operation completed successfuly + * -ENOTTY - Unknown IOCTL command + * -EINVAL - Invalid sense data record + * -ENXIO - The FCP adapter is not available + * -EOPNOTSUPP - The FCP adapter does not have CFDC support + * -ENOMEM - Insufficient memory + * -EFAULT - User space memory I/O operation fault + * -EPERM - Cannot create or queue FSF request or create SBALs + */ +static int +zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, + unsigned int command, unsigned long buffer) +{ + struct zfcp_cfdc_sense_data sense_data, *sense_data_user; + struct zfcp_adapter *adapter = NULL; + struct zfcp_fsf_req *fsf_req = NULL; + struct zfcp_sg_list *sg_list = NULL; + u32 fsf_command, option; + char *bus_id = NULL; + int retval = 0; + + ZFCP_LOG_NORMAL( + "Control file data channel transaction opened\n"); + + sg_list = kmalloc(sizeof(struct zfcp_sg_list), GFP_KERNEL); + if (sg_list == NULL) { + ZFCP_LOG_NORMAL( + "Not enough memory for the scatter-gather list\n"); + retval = -ENOMEM; + goto out; + } + sg_list->count = 0; + + if (command != ZFCP_CFDC_IOC) { + ZFCP_LOG_NORMAL( + "IOC request code 0x%x is not valid\n", + command); + retval = -ENOTTY; + goto out; + } + + if ((sense_data_user = (struct zfcp_cfdc_sense_data*)buffer) == NULL) { + ZFCP_LOG_NORMAL( + "Sense data record is required\n"); + retval = -EINVAL; + goto out; + } + + retval = copy_from_user(&sense_data, sense_data_user, + sizeof(struct zfcp_cfdc_sense_data)); + if (retval) { + ZFCP_LOG_NORMAL("Cannot copy sense data record from user space " + "memory\n"); + retval = -EFAULT; + goto out; + } + + if (sense_data.signature != ZFCP_CFDC_SIGNATURE) { + ZFCP_LOG_NORMAL( + "No valid sense data request signature 0x%08x found\n", + ZFCP_CFDC_SIGNATURE); + retval = -EINVAL; + goto out; + } + + switch (sense_data.command) { + + case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL: + fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; + option = FSF_CFDC_OPTION_NORMAL_MODE; + break; + + case ZFCP_CFDC_CMND_DOWNLOAD_FORCE: + fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; + option = FSF_CFDC_OPTION_FORCE; + break; + + case ZFCP_CFDC_CMND_FULL_ACCESS: + fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; + option = FSF_CFDC_OPTION_FULL_ACCESS; + break; + + case ZFCP_CFDC_CMND_RESTRICTED_ACCESS: + fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; + option = FSF_CFDC_OPTION_RESTRICTED_ACCESS; + break; + + case ZFCP_CFDC_CMND_UPLOAD: + fsf_command = FSF_QTCB_UPLOAD_CONTROL_FILE; + option = 0; + break; + + default: + ZFCP_LOG_NORMAL( + "Command code 0x%08x is not valid\n", + sense_data.command); + retval = -EINVAL; + goto out; + } + + bus_id = kmalloc(BUS_ID_SIZE, GFP_KERNEL); + if (bus_id == NULL) { + ZFCP_LOG_NORMAL("Out of memory!\n"); + retval = -ENOMEM; + goto out; + } + snprintf(bus_id, BUS_ID_SIZE, "%d.%d.%04x", + (sense_data.devno >> 24), + (sense_data.devno >> 16) & 0xFF, + (sense_data.devno & 0xFFFF)); + + retval = -ENXIO; + read_lock_irq(&zfcp_data.config_lock); + list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) { + if (strncmp(bus_id, zfcp_get_busid_by_adapter(adapter), + BUS_ID_SIZE) == 0) { + zfcp_adapter_get(adapter); + retval = 0; + break; + } + } + read_unlock_irq(&zfcp_data.config_lock); + + kfree(bus_id); + + if (retval != 0) { + ZFCP_LOG_NORMAL("Specified adapter does not exist\n"); + goto out; + } + + if (sense_data.command & ZFCP_CFDC_WITH_CONTROL_FILE) { + retval = zfcp_sg_list_alloc(sg_list, + ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); + if (retval) { + ZFCP_LOG_NORMAL("Not enough memory for the " + "scatter-gather list\n"); + retval = -ENOMEM; + goto out; + } + } + + if ((sense_data.command & ZFCP_CFDC_DOWNLOAD) && + (sense_data.command & ZFCP_CFDC_WITH_CONTROL_FILE)) { + retval = zfcp_sg_list_copy_from_user( + sg_list, &sense_data_user->control_file, + ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); + if (retval) { + ZFCP_LOG_NORMAL("Cannot copy control file from user " + "space memory\n"); + retval = -EFAULT; + goto out; + } + } + + retval = zfcp_fsf_control_file( + adapter, &fsf_req, fsf_command, option, sg_list); + if (retval == -EOPNOTSUPP) { + ZFCP_LOG_NORMAL( + "Specified adapter does not support control file\n"); + goto out; + } else if (retval != 0) { + ZFCP_LOG_NORMAL( + "Cannot create or queue FSF request or create SBALs\n"); + retval = -EPERM; + goto out; + } + + wait_event(fsf_req->completion_wq, + fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + + sense_data.fsf_status = fsf_req->qtcb->header.fsf_status; + memcpy(&sense_data.fsf_status_qual, + &fsf_req->qtcb->header.fsf_status_qual, + sizeof(union fsf_status_qual)); + memcpy(&sense_data.payloads, &fsf_req->qtcb->bottom.support.els, 256); + + retval = copy_to_user(sense_data_user, &sense_data, + sizeof(struct zfcp_cfdc_sense_data)); + if (retval) { + ZFCP_LOG_NORMAL( + "Cannot copy sense data record to user space memory\n"); + retval = -EFAULT; + goto out; + } + + if (sense_data.command & ZFCP_CFDC_UPLOAD) { + retval = zfcp_sg_list_copy_to_user( + &sense_data_user->control_file, sg_list, + ZFCP_CFDC_MAX_CONTROL_FILE_SIZE); + if (retval) { + ZFCP_LOG_NORMAL("Cannot copy control file to user " + "space memory\n"); + retval = -EFAULT; + goto out; + } + } + + out: + if (fsf_req != NULL) + zfcp_fsf_req_cleanup(fsf_req); + + if ((adapter != NULL) && (retval != -ENXIO)) + zfcp_adapter_put(adapter); + + if (sg_list != NULL) { + zfcp_sg_list_free(sg_list); + kfree(sg_list); + } + + ZFCP_LOG_NORMAL( + "Control file data channel transaction closed\n"); + + return retval; +} + + +/* + * function: zfcp_sg_list_alloc + * + * purpose: Create a scatter-gather list of the specified size + * + * returns: 0 - Scatter gather list is created + * -ENOMEM - Insufficient memory (*list_ptr is then set to NULL) + */ +static inline int +zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size) +{ + struct scatterlist *sg; + int i; + int retval = 0; + + sg_list->count = size >> PAGE_SHIFT; + if (size & ~PAGE_MASK) + sg_list->count++; + sg_list->sg = kmalloc(sg_list->count * sizeof(struct scatterlist), + GFP_KERNEL); + if (sg_list->sg == NULL) { + retval = -ENOMEM; + goto out; + } + + for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) { + sg->length = min(size, PAGE_SIZE); + sg->offset = 0; + sg->page = alloc_pages(GFP_KERNEL, 0); + if (sg->page == NULL) { + sg_list->count = i; + zfcp_sg_list_free(sg_list); + retval = -ENOMEM; + goto out; + } + size -= sg->length; + } + + out: + return retval; +} + + +/* + * function: zfcp_sg_list_free + * + * purpose: Destroy a scatter-gather list and release memory + * + * returns: Always 0 + */ +static inline int +zfcp_sg_list_free(struct zfcp_sg_list *sg_list) +{ + struct scatterlist *sg; + int i; + int retval = 0; + + BUG_ON((sg_list->sg == NULL) || (sg_list == NULL)); + + for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) + __free_pages(sg->page, 0); + + return retval; +} + + +/* + * function: zfcp_sg_list_copy_from_user + * + * purpose: Copy data from user space memory to the scatter-gather list + * + * returns: 0 - The data has been copied from user + * -EFAULT - Memory I/O operation fault + */ +static inline int +zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list, void *user_buffer, + size_t size) +{ + struct scatterlist *sg; + unsigned int length; + void *zfcp_buffer; + int retval = 0; + + for (sg = sg_list->sg; size > 0; sg++) { + length = min((unsigned int)size, sg->length); + zfcp_buffer = (void*) + ((page_to_pfn(sg->page) << PAGE_SHIFT) + sg->offset); + if (copy_from_user(zfcp_buffer, user_buffer, length)) { + ZFCP_LOG_INFO("Memory error (copy_from_user)\n"); + retval = -EFAULT; + goto out; + } + user_buffer += length; + size -= length; + } + + out: + return retval; +} + + +/* + * function: zfcp_sg_list_copy_to_user + * + * purpose: Copy data from the scatter-gather list to user space memory + * + * returns: 0 - The data has been copied to user + * -EFAULT - Memory I/O operation fault + */ +static inline int +zfcp_sg_list_copy_to_user(void *user_buffer, struct zfcp_sg_list *sg_list, + size_t size) +{ + struct scatterlist *sg; + unsigned int length; + void *zfcp_buffer; + int retval = 0; + + for (sg = sg_list->sg; size > 0; sg++) { + length = min((unsigned int)size, sg->length); + zfcp_buffer = (void*) + ((page_to_pfn(sg->page) << PAGE_SHIFT) + sg->offset); + if (copy_to_user(user_buffer, zfcp_buffer, length)) { + ZFCP_LOG_INFO("Memory error (copy_to_user)\n"); + retval = -EFAULT; + goto out; + } + user_buffer += length; + size -= length; + } + + out: + return retval; +} + + #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX /****************************************************************/ /****** Functions for configuration/set-up of structures ********/ /****************************************************************/ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG /** * zfcp_get_unit_by_lun - find unit in unit list of port by fcp lun @@ -684,83 +1110,92 @@ zfcp_mempool_free(void *element, void *size) static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) { - adapter->pool.erp_fsf = mempool_create( - 1, - zfcp_mempool_alloc, - zfcp_mempool_free, - (void *) ZFCP_QTCB_AND_REQ_SIZE); - if (!adapter->pool.erp_fsf) { - ZFCP_LOG_INFO - ("error: FCP command buffer pool allocation failed\n"); + adapter->pool.fsf_req_erp = + mempool_create(ZFCP_POOL_FSF_REQ_ERP_NR, + zfcp_mempool_alloc, zfcp_mempool_free, (void *) + sizeof(struct zfcp_fsf_req_pool_element)); + + if (NULL == adapter->pool.fsf_req_erp) { + ZFCP_LOG_INFO("error: pool allocation failed (fsf_req_erp)\n"); return -ENOMEM; } - adapter->pool.nameserver = mempool_create( - 1, - zfcp_mempool_alloc, - zfcp_mempool_free, - (void *) (2 * sizeof (struct fc_ct_iu))); - if (!adapter->pool.nameserver) { - ZFCP_LOG_INFO - ("error: Nameserver buffer pool allocation failed\n"); + adapter->pool.fsf_req_scsi = + mempool_create(ZFCP_POOL_FSF_REQ_SCSI_NR, + zfcp_mempool_alloc, zfcp_mempool_free, (void *) + sizeof(struct zfcp_fsf_req_pool_element)); + + if (NULL == adapter->pool.fsf_req_scsi) { + ZFCP_LOG_INFO("error: pool allocation failed (fsf_req_scsi)\n"); return -ENOMEM; } - adapter->pool.status_read_fsf = mempool_create( - ZFCP_STATUS_READS_RECOM, - zfcp_mempool_alloc, - zfcp_mempool_free, - (void *) sizeof (struct zfcp_fsf_req)); - if (!adapter->pool.status_read_fsf) { - ZFCP_LOG_INFO - ("error: Status read request pool allocation failed\n"); + adapter->pool.fsf_req_abort = + mempool_create(ZFCP_POOL_FSF_REQ_ABORT_NR, + zfcp_mempool_alloc, zfcp_mempool_free, (void *) + sizeof(struct zfcp_fsf_req_pool_element)); + + if (NULL == adapter->pool.fsf_req_abort) { + ZFCP_LOG_INFO("error: pool allocation failed " + "(fsf_req_abort)\n"); return -ENOMEM; } - adapter->pool.status_read_buf = mempool_create( - ZFCP_STATUS_READS_RECOM, - zfcp_mempool_alloc, - zfcp_mempool_free, - (void *) sizeof (struct fsf_status_read_buffer)); - if (!adapter->pool.status_read_buf) { - ZFCP_LOG_INFO - ("error: Status read buffer pool allocation failed\n"); + adapter->pool.fsf_req_status_read = + mempool_create(ZFCP_POOL_STATUS_READ_NR, + zfcp_mempool_alloc, zfcp_mempool_free, + (void *) sizeof(struct zfcp_fsf_req)); + + if (NULL == adapter->pool.fsf_req_status_read) { + ZFCP_LOG_INFO("error: pool allocation failed " + "(fsf_req_status_read\n"); return -ENOMEM; } - adapter->pool.fcp_command_fsf = mempool_create( - 1, - zfcp_mempool_alloc, - zfcp_mempool_free, - (void *) - ZFCP_QTCB_AND_REQ_SIZE); - if (!adapter->pool.fcp_command_fsf) { - ZFCP_LOG_INFO - ("error: FCP command buffer pool allocation failed\n"); + adapter->pool.data_status_read = + mempool_create(ZFCP_POOL_STATUS_READ_NR, + zfcp_mempool_alloc, zfcp_mempool_free, + (void *) sizeof(struct fsf_status_read_buffer)); + + if (NULL == adapter->pool.data_status_read) { + ZFCP_LOG_INFO("error: pool allocation failed " + "(data_status_read)\n"); + return -ENOMEM; + } + + adapter->pool.data_gid_pn = + mempool_create(ZFCP_POOL_DATA_GID_PN_NR, + zfcp_mempool_alloc, zfcp_mempool_free, (void *) + sizeof(struct zfcp_gid_pn_data)); + + if (NULL == adapter->pool.data_gid_pn) { + ZFCP_LOG_INFO("error: pool allocation failed (data_gid_pn)\n"); return -ENOMEM; } - init_timer(&adapter->pool.fcp_command_fsf_timer); - adapter->pool.fcp_command_fsf_timer.function = - zfcp_erp_scsi_low_mem_buffer_timeout_handler; - adapter->pool.fcp_command_fsf_timer.data = (unsigned long) adapter; return 0; } -/* locks: must only be called with zfcp_data.config_sema taken */ +/** + * zfcp_free_low_mem_buffers - free memory pools of an adapter + * @adapter: pointer to zfcp_adapter for which memory pools should be freed + * locking: zfcp_data.config_sema must be held + */ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { - if (adapter->pool.status_read_fsf) - mempool_destroy(adapter->pool.status_read_fsf); - if (adapter->pool.status_read_buf) - mempool_destroy(adapter->pool.status_read_buf); - if (adapter->pool.nameserver) - mempool_destroy(adapter->pool.nameserver); - if (adapter->pool.erp_fsf) - mempool_destroy(adapter->pool.erp_fsf); - if (adapter->pool.fcp_command_fsf) - mempool_destroy(adapter->pool.fcp_command_fsf); + if (adapter->pool.fsf_req_erp) + mempool_destroy(adapter->pool.fsf_req_erp); + if (adapter->pool.fsf_req_scsi) + mempool_destroy(adapter->pool.fsf_req_scsi); + if (adapter->pool.fsf_req_abort) + mempool_destroy(adapter->pool.fsf_req_abort); + if (adapter->pool.fsf_req_status_read) + mempool_destroy(adapter->pool.fsf_req_status_read); + if (adapter->pool.data_status_read) + mempool_destroy(adapter->pool.data_status_read); + if (adapter->pool.data_gid_pn) + mempool_destroy(adapter->pool.data_gid_pn); } /* @@ -859,7 +1294,7 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) #ifdef ZFCP_DEBUG_REQUESTS /* debug feature area which records fsf request sequence numbers */ - sprintf(dbf_name, ZFCP_REQ_DBF_NAME "0x%s", + sprintf(dbf_name, ZFCP_REQ_DBF_NAME "%s", zfcp_get_busid_by_adapter(adapter)); adapter->req_dbf = debug_register(dbf_name, ZFCP_REQ_DBF_INDEX, @@ -954,15 +1389,6 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view); debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL); - retval = zfcp_erp_thread_setup(adapter); - if (retval) { - ZFCP_LOG_INFO("error: out of resources. " - "error recovery thread for the adapter %s " - "could not be started\n", - zfcp_get_busid_by_adapter(adapter)); - goto thread_failed; - } - /* put allocated adapter at list tail */ write_lock_irq(&zfcp_data.config_lock); atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); @@ -973,15 +1399,6 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) goto out; - thread_failed: - if (qdio_free(adapter->ccw_device) != 0) - ZFCP_LOG_NORMAL - ("bug: could not free memory used by data transfer " - "mechanism for adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); - - debug_unregister(adapter->erp_dbf); - failed_erp_dbf: #ifdef ZFCP_DEBUG_INCOMING_ELS debug_unregister(adapter->in_els_dbf); @@ -1007,7 +1424,11 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) dev_set_drvdata(&ccw_device->dev, NULL); failed_low_mem_buffers: zfcp_free_low_mem_buffers(adapter); - qdio_free(ccw_device); + if (qdio_free(ccw_device) != 0) + ZFCP_LOG_NORMAL + ("bug: could not free memory used by data transfer " + "mechanism for adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); qdio_allocate_failed: zfcp_qdio_free_queues(adapter); queues_alloc_failed: @@ -1060,9 +1481,7 @@ zfcp_adapter_dequeue(struct zfcp_adapter *adapter) "%i adapters still in list\n", (unsigned long) adapter, zfcp_data.adapters); - retval = zfcp_erp_thread_kill(adapter); - - retval |= qdio_free(adapter->ccw_device); + retval = qdio_free(adapter->ccw_device); if (retval) ZFCP_LOG_NORMAL ("bug: could not free memory used by data transfer " @@ -1261,14 +1680,12 @@ zfcp_nameserver_enqueue(struct zfcp_adapter *adapter) } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX /****************************************************************/ /******* Fibre Channel Standard related Functions **************/ /****************************************************************/ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_FC -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_FC void zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, @@ -1361,7 +1778,7 @@ zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, "0x%Lx.\n", port->wwpn); debug_text_event(adapter->erp_dbf, 1, "unsol_els_rscnk:"); - zfcp_erp_port_reopen(port, 0); + zfcp_test_link(port); } } read_unlock_irqrestore(&zfcp_data.config_lock, flags); @@ -1462,222 +1879,180 @@ zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req) zfcp_fsf_incoming_els_rscn(adapter, status_buffer); else zfcp_fsf_incoming_els_unknown(adapter, status_buffer); -} -/* - * function: zfcp_release_nameserver_buffers - * - * purpose: - * - * returns: - */ -static void -zfcp_release_nameserver_buffers(struct zfcp_fsf_req *fsf_req) -{ - struct zfcp_adapter *adapter = fsf_req->adapter; - void *buffer = fsf_req->data.send_generic.outbuf; - - /* FIXME: not sure about appeal of this new flag (martin) */ - if (fsf_req->status & ZFCP_STATUS_FSFREQ_POOLBUF) - mempool_free(buffer, adapter->pool.nameserver); - else - kfree(buffer); } -/* - * function: zfcp_get_nameserver_buffers - * - * purpose: - * - * returns: - * - * locks: fsf_request_list_lock is held when doing buffer pool - * operations + +/** + * zfcp_gid_pn_buffers_alloc - allocate buffers for GID_PN nameserver request + * @gid_pn: pointer to return pointer to struct zfcp_gid_pn_data + * @pool: pointer to mempool_t if non-null memory pool is used for allocation */ static int -zfcp_get_nameserver_buffers(struct zfcp_fsf_req *fsf_req) +zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool) { - struct zfcp_send_generic *data = &fsf_req->data.send_generic; - struct zfcp_adapter *adapter = fsf_req->adapter; - int retval = 0; + struct zfcp_gid_pn_data *data; - data->outbuf = kmalloc(2 * sizeof (struct fc_ct_iu), GFP_ATOMIC); - if (data->outbuf) { - memset(data->outbuf, 0, 2 * sizeof (struct fc_ct_iu)); + if (pool != NULL) { + data = mempool_alloc(pool, GFP_ATOMIC); + if (likely(data != NULL)) { + data->ct.pool = pool; + } } else { - ZFCP_LOG_DEBUG("Out of memory. Could not allocate at " - "least one of the buffers " - "required for a name-server request on the" - "adapter %s directly.. trying emergency pool\n", - zfcp_get_busid_by_adapter(adapter)); - data->outbuf = - mempool_alloc(adapter->pool.nameserver, GFP_ATOMIC); - if (!data->outbuf) { - ZFCP_LOG_DEBUG - ("Out of memory. Could not get emergency " - "buffer required for a name-server request " - "on the adapter %s. All buffers are in " - "use.\n", - zfcp_get_busid_by_adapter(adapter)); - retval = -ENOMEM; - goto out; + data = kmalloc(sizeof(struct zfcp_gid_pn_data), GFP_ATOMIC); } - memset(data->outbuf, 0, 2 * sizeof (struct fc_ct_iu)); - fsf_req->status |= ZFCP_STATUS_FSFREQ_POOLBUF; + + if (NULL == data){ + ZFCP_LOG_DEBUG("Out of memory.\n"); + return -ENOMEM; } - data->outbuf_length = sizeof (struct fc_ct_iu); - data->inbuf_length = sizeof (struct fc_ct_iu); - data->inbuf = - (char *) ((unsigned long) data->outbuf + sizeof (struct fc_ct_iu)); - out: - return retval; + + memset(data, 0, sizeof(*data)); + data->ct.req = &data->req; + data->ct.resp = &data->resp; + data->ct.req_count = data->ct.resp_count = 1; + zfcp_address_to_sg(&data->ct_iu_req, &data->req); + zfcp_address_to_sg(&data->ct_iu_resp, &data->resp); + data->req.length = sizeof(struct ct_iu_gid_pn_req); + data->resp.length = sizeof(struct ct_iu_gid_pn_resp); + + *gid_pn = data; + return 0; } -/* - * function: zfcp_nameserver_request - * - * purpose: - * - * returns: +/** + * zfcp_gid_pn_buffers_free - free buffers for GID_PN nameserver request + * @gid_pn: pointer to struct zfcp_gid_pn_data which has to be freed */ -int -zfcp_nameserver_request(struct zfcp_erp_action *erp_action) +static void +zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn) { - int retval = 0; - struct fc_ct_iu *fc_ct_iu; - unsigned long lock_flags; - - /* setup new FSF request */ - retval = zfcp_fsf_req_create(erp_action->adapter, - FSF_QTCB_SEND_GENERIC, - &lock_flags, - ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, - &(erp_action->fsf_req)); - if (retval < 0) { - ZFCP_LOG_INFO("error: Out of resources. Could not create a " - "nameserver registration request for " - "adapter %s.\n", - zfcp_get_busid_by_adapter(erp_action->adapter)); - goto failed_req; - } - retval = zfcp_get_nameserver_buffers(erp_action->fsf_req); - if (retval < 0) { - ZFCP_LOG_INFO("error: Out of memory. Could not allocate one of " - "the buffers required for a nameserver request " - "on adapter %s.\n", - zfcp_get_busid_by_adapter(erp_action->adapter)); - goto failed_buffers; + if ((gid_pn->ct.pool != 0)) { + mempool_free(gid_pn, gid_pn->ct.pool); + } else { + kfree(gid_pn); } - /* setup name-server request in first page */ - fc_ct_iu = - (struct fc_ct_iu *) erp_action->fsf_req->data.send_generic.outbuf; - fc_ct_iu->revision = ZFCP_CT_REVISION; - fc_ct_iu->gs_type = ZFCP_CT_DIRECTORY_SERVICE; - fc_ct_iu->gs_subtype = ZFCP_CT_NAME_SERVER; - fc_ct_iu->options = ZFCP_CT_SYNCHRONOUS; - fc_ct_iu->cmd_rsp_code = ZFCP_CT_GID_PN; - fc_ct_iu->max_res_size = ZFCP_CT_MAX_SIZE; - fc_ct_iu->data.wwpn = erp_action->port->wwpn; - - erp_action->fsf_req->data.send_generic.handler = - zfcp_nameserver_request_handler; - erp_action->fsf_req->data.send_generic.handler_data = - (unsigned long) erp_action->port; - erp_action->fsf_req->data.send_generic.port = - erp_action->adapter->nameserver_port; - erp_action->fsf_req->erp_action = erp_action; - - /* send this one */ - retval = zfcp_fsf_send_generic(erp_action->fsf_req, - ZFCP_NAMESERVER_TIMEOUT, - &lock_flags, - &erp_action->timer); - if (retval) { - ZFCP_LOG_INFO("error: Could not send a" - "nameserver request command to adapter %s\n", - zfcp_get_busid_by_adapter(erp_action->adapter)); - goto failed_send; - } + return; +} +/** + * zfcp_ns_gid_pn_request - initiate GID_PN nameserver request + * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed + */ +int +zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action) +{ + int ret; + struct ct_iu_gid_pn_req *ct_iu_req; + struct zfcp_gid_pn_data *gid_pn; + struct zfcp_adapter *adapter = erp_action->adapter; + + ret = zfcp_gid_pn_buffers_alloc(&gid_pn, adapter->pool.data_gid_pn); + if (ret < 0) { + ZFCP_LOG_INFO("error: Out of memory. Could not allocate " + "buffers for nameserver request GID_PN. " + "(adapter: %s)\n", + zfcp_get_busid_by_adapter(adapter)); goto out; + } - failed_send: - zfcp_release_nameserver_buffers(erp_action->fsf_req); + /* setup nameserver request */ + ct_iu_req = zfcp_sg_to_address(gid_pn->ct.req); + ct_iu_req->header.revision = ZFCP_CT_REVISION; + ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE; + ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER; + ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS; + ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GID_PN; + ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE; + ct_iu_req->wwpn = erp_action->port->wwpn; + + /* setup parameters for send generic command */ + gid_pn->ct.port = adapter->nameserver_port; + gid_pn->ct.handler = zfcp_ns_gid_pn_handler; + gid_pn->ct.handler_data = (unsigned long) gid_pn; + gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT; + gid_pn->ct.timer = &erp_action->timer; + gid_pn->port = erp_action->port; + + ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp, + erp_action); + if (ret) { + ZFCP_LOG_INFO("error: Could not send nameserver request GID_PN." + "(adapter %s)\n", + zfcp_get_busid_by_adapter(adapter)); - failed_buffers: - zfcp_fsf_req_free(erp_action->fsf_req); - erp_action->fsf_req = NULL; + zfcp_gid_pn_buffers_free(gid_pn); + } - failed_req: out: - write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, - lock_flags); - return retval; + return ret; } -/* - * function: zfcp_nameserver_request_handler - * - * purpose: - * - * returns: +/** + * zfcp_ns_gid_pn_handler - handler for GID_PN nameserver request + * @data: unsigned long, contains pointer to struct zfcp_gid_pn_data */ -static void -zfcp_nameserver_request_handler(struct zfcp_fsf_req *fsf_req) +static void zfcp_ns_gid_pn_handler(unsigned long data) { - struct fc_ct_iu *fc_ct_iu_resp = - (struct fc_ct_iu *) (fsf_req->data.send_generic.inbuf); - struct fc_ct_iu *fc_ct_iu_req = - (struct fc_ct_iu *) (fsf_req->data.send_generic.outbuf); - struct zfcp_port *port = - (struct zfcp_port *) fsf_req->data.send_generic.handler_data; - - if (fc_ct_iu_resp->revision != ZFCP_CT_REVISION) + struct zfcp_port *port; + struct zfcp_send_ct *ct; + struct ct_iu_gid_pn_req *ct_iu_req; + struct ct_iu_gid_pn_resp *ct_iu_resp; + struct zfcp_gid_pn_data *gid_pn; + + + gid_pn = (struct zfcp_gid_pn_data *) data; + port = gid_pn->port; + ct = &gid_pn->ct; + ct_iu_req = zfcp_sg_to_address(ct->req); + ct_iu_resp = zfcp_sg_to_address(ct->resp); + + if (ct_iu_resp->header.revision != ZFCP_CT_REVISION) goto failed; - if (fc_ct_iu_resp->gs_type != ZFCP_CT_DIRECTORY_SERVICE) + if (ct_iu_resp->header.gs_type != ZFCP_CT_DIRECTORY_SERVICE) goto failed; - if (fc_ct_iu_resp->gs_subtype != ZFCP_CT_NAME_SERVER) + if (ct_iu_resp->header.gs_subtype != ZFCP_CT_NAME_SERVER) goto failed; - if (fc_ct_iu_resp->options != ZFCP_CT_SYNCHRONOUS) + if (ct_iu_resp->header.options != ZFCP_CT_SYNCHRONOUS) goto failed; - if (fc_ct_iu_resp->cmd_rsp_code != ZFCP_CT_ACCEPT) { + if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) { /* FIXME: do we need some specific erp entry points */ atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status); goto failed; } /* paranoia */ - if (fc_ct_iu_req->data.wwpn != port->wwpn) { - ZFCP_LOG_NORMAL("bug: Port WWPN returned by nameserver lookup " - "does not correspond to " - "the expected value on the adapter %s. " - "(debug info 0x%Lx, 0x%Lx)\n", - zfcp_get_busid_by_port(port), - port->wwpn, fc_ct_iu_req->data.wwpn); + if (ct_iu_req->wwpn != port->wwpn) { + ZFCP_LOG_NORMAL( + "bug: Port WWPN returned by nameserver lookup " + "does not correspond to the expected value " + "(adapter: %s, debug info: 0x%016Lx, 0x%016Lx)\n", + zfcp_get_busid_by_port(port), port->wwpn, + ct_iu_req->wwpn); goto failed; } /* looks like a valid d_id */ - port->d_id = ZFCP_DID_MASK & fc_ct_iu_resp->data.d_id; + port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK; atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status); ZFCP_LOG_DEBUG("busid %s: WWPN=0x%Lx ---> D_ID=0x%6.6x\n", zfcp_get_busid_by_port(port), port->wwpn, (unsigned int) port->d_id); goto out; - failed: +failed: ZFCP_LOG_NORMAL("warning: WWPN 0x%Lx not found by nameserver lookup " - "using the adapter %s\n", + "(adapter: %s)\n", port->wwpn, zfcp_get_busid_by_port(port)); ZFCP_LOG_DEBUG("CT IUs do not match:\n"); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, - (char *) fc_ct_iu_req, sizeof (struct fc_ct_iu)); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, - (char *) fc_ct_iu_resp, sizeof (struct fc_ct_iu)); + ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_req, + sizeof(struct ct_iu_gid_pn_req)); + ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_resp, + sizeof(struct ct_iu_gid_pn_resp)); out: - zfcp_release_nameserver_buffers(fsf_req); + zfcp_gid_pn_buffers_free(gid_pn); + return; } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 576628fd5ebe..3a0702537305 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -5,7 +5,8 @@ * * CCW driver related routines * - * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation + * (C) Copyright IBM Corp. 2003, 2004 + * * Authors: * Martin Peschke <mpeschke@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> @@ -25,7 +26,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_CCW_C_REVISION "$Revision: 1.36 $" +#define ZFCP_CCW_C_REVISION "$Revision: 1.48 $" #include <linux/init.h> #include <linux/module.h> @@ -34,18 +35,22 @@ #include "zfcp_def.h" #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG static int zfcp_ccw_probe(struct ccw_device *); static void zfcp_ccw_remove(struct ccw_device *); static int zfcp_ccw_set_online(struct ccw_device *); static int zfcp_ccw_set_offline(struct ccw_device *); +static int zfcp_ccw_notify(struct ccw_device *, int); static struct ccw_device_id zfcp_ccw_device_id[] = { {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE, ZFCP_CONTROL_UNIT_MODEL, ZFCP_DEVICE_TYPE, ZFCP_DEVICE_MODEL)}, + {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE, + ZFCP_CONTROL_UNIT_MODEL, + ZFCP_DEVICE_TYPE, + ZFCP_DEVICE_MODEL_PRIV)}, {}, }; @@ -56,6 +61,7 @@ static struct ccw_driver zfcp_ccw_driver = { .remove = zfcp_ccw_remove, .set_online = zfcp_ccw_set_online, .set_offline = zfcp_ccw_set_offline, + .notify = zfcp_ccw_notify, }; MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id); @@ -80,6 +86,9 @@ zfcp_ccw_probe(struct ccw_device *ccw_device) adapter = zfcp_adapter_enqueue(ccw_device); if (!adapter) retval = -EINVAL; + else + ZFCP_LOG_DEBUG("Probed adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); up(&zfcp_data.config_sema); return retval; } @@ -104,6 +113,8 @@ zfcp_ccw_remove(struct ccw_device *ccw_device) down(&zfcp_data.config_sema); adapter = dev_get_drvdata(&ccw_device->dev); + ZFCP_LOG_DEBUG("Removing adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); write_lock_irq(&zfcp_data.config_lock); list_for_each_entry_safe(port, p, &adapter->port_list_head, list) { list_for_each_entry_safe(unit, u, &port->unit_list_head, list) { @@ -152,13 +163,26 @@ zfcp_ccw_set_online(struct ccw_device *ccw_device) down(&zfcp_data.config_sema); adapter = dev_get_drvdata(&ccw_device->dev); + retval = zfcp_erp_thread_setup(adapter); + if (retval) { + ZFCP_LOG_INFO("error: out of resources. " + "error recovery thread for adapter %s " + "could not be started\n", + zfcp_get_busid_by_adapter(adapter)); + goto out; + } + retval = zfcp_adapter_scsi_register(adapter); if (retval) - goto out; + goto out_scsi_register; zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED); zfcp_erp_wait(adapter); + goto out; + + out_scsi_register: + zfcp_erp_thread_kill(adapter); out: up(&zfcp_data.config_sema); return retval; @@ -183,11 +207,50 @@ zfcp_ccw_set_offline(struct ccw_device *ccw_device) zfcp_erp_adapter_shutdown(adapter, 0); zfcp_erp_wait(adapter); zfcp_adapter_scsi_unregister(adapter); + zfcp_erp_thread_kill(adapter); up(&zfcp_data.config_sema); return 0; } /** + * zfcp_ccw_notify + * @ccw_device: pointer to belonging ccw device + * @event: indicates if adapter was detached or attached + * + * This function gets called by the common i/o layer if an adapter has gone + * or reappeared. + */ +static int +zfcp_ccw_notify(struct ccw_device *ccw_device, int event) +{ + struct zfcp_adapter *adapter; + + down(&zfcp_data.config_sema); + adapter = dev_get_drvdata(&ccw_device->dev); + switch (event) { + case CIO_GONE: + ZFCP_LOG_NORMAL("Adapter %s: device gone.\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case CIO_NO_PATH: + ZFCP_LOG_NORMAL("Adapter %s: no path.\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case CIO_OPER: + ZFCP_LOG_NORMAL("Adapter %s: operational again.\n", + zfcp_get_busid_by_adapter(adapter)); + zfcp_erp_modify_adapter_status(adapter, + ZFCP_STATUS_COMMON_RUNNING, + ZFCP_SET); + zfcp_erp_adapter_reopen(adapter, + ZFCP_STATUS_COMMON_ERP_FAILED); + break; + } + up(&zfcp_data.config_sema); + return 1; +} + +/** * zfcp_ccw_register - ccw register function * * Registers the driver at the common i/o layer. This function will be called @@ -222,4 +285,3 @@ zfcp_ccw_unregister(void) } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 159c01ed6694..f2f9ce13276e 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -4,11 +4,12 @@ * * FCP adapter driver for IBM eServer zSeries * - * Copyright 2002 IBM Corporation + * (C) Copyright IBM Corp. 2002, 2004 + * * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> - * Aron Zeh <arzeh@de.ibm.com> - * Wolfgang Taphorn <taphorn@de.ibm.com> + * Aron Zeh + * Wolfgang Taphorn * Stefan Bader <stefan.bader@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> * @@ -32,19 +33,29 @@ #define ZFCP_DEF_H /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_DEF_REVISION "$Revision: 1.48 $" +#define ZFCP_DEF_REVISION "$Revision: 1.62 $" /*************************** INCLUDES *****************************************/ #include <linux/blkdev.h> +#include <scsi/scsi.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> #include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" #include "../../fc4/fc.h" #include "zfcp_fsf.h" /* FSF SW Interface */ #include <asm/ccwdev.h> #include <asm/qdio.h> #include <asm/debug.h> +#include <asm/ebcdic.h> #include <linux/reboot.h> +#include <linux/mempool.h> +#include <linux/ioctl.h> +#ifdef CONFIG_S390_SUPPORT +#include <linux/ioctl32.h> +#endif /************************ DEBUG FLAGS *****************************************/ @@ -56,6 +67,24 @@ #define ZFCP_STAT_REQSIZES #define ZFCP_STAT_QUEUES +/********************* GENERAL DEFINES *********************************/ + +/* zfcp version number, it consists of major, minor, and patch-level number */ +#define ZFCP_VERSION "4.0.0" + +static inline void * +zfcp_sg_to_address(struct scatterlist *list) +{ + return (void *) (page_address(list->page) + list->offset); +} + +static inline void +zfcp_address_to_sg(void *address, struct scatterlist *list) +{ + list->page = virt_to_page(address); + list->offset = ((unsigned long) address) & (PAGE_SIZE - 1); +} + /********************* SCSI SPECIFIC DEFINES *********************************/ /* 32 bit for SCSI ID and LUN as long as the SCSI stack uses this type */ @@ -64,7 +93,6 @@ typedef u32 scsi_lun_t; #define ZFCP_ERP_SCSI_LOW_MEM_TIMEOUT (100*HZ) #define ZFCP_SCSI_ER_TIMEOUT (100*HZ) -#define ZFCP_SCSI_HOST_FLUSH_TIMEOUT (1*HZ) /********************* CIO/QDIO SPECIFIC DEFINES *****************************/ @@ -73,9 +101,12 @@ typedef u32 scsi_lun_t; #define ZFCP_CONTROL_UNIT_MODEL 0x03 #define ZFCP_DEVICE_TYPE 0x1732 #define ZFCP_DEVICE_MODEL 0x03 +#define ZFCP_DEVICE_MODEL_PRIV 0x04 /* allow as many chained SBALs as are supported by hardware */ #define ZFCP_MAX_SBALS_PER_REQ FSF_MAX_SBALS_PER_REQ +#define ZFCP_MAX_SBALS_PER_CT_REQ FSF_MAX_SBALS_PER_REQ +#define ZFCP_MAX_SBALS_PER_ELS_REQ FSF_MAX_SBALS_PER_ELS_REQ /* DMQ bug workaround: don't use last SBALE */ #define ZFCP_MAX_SBALES_PER_SBAL (QDIO_MAX_ELEMENTS_PER_BUFFER - 1) @@ -115,9 +146,6 @@ typedef u32 scsi_lun_t; #define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 6 #define ZFCP_EXCHANGE_CONFIG_DATA_SLEEP 50 -#define ZFCP_QTCB_SIZE (sizeof(struct fsf_qtcb) + FSF_QTCB_LOG_SIZE) -#define ZFCP_QTCB_AND_REQ_SIZE (sizeof(struct zfcp_fsf_req) + ZFCP_QTCB_SIZE) - /*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/ typedef unsigned long long wwn_t; @@ -129,7 +157,8 @@ typedef unsigned int fcp_dl_t; #define ZFCP_FC_SERVICE_CLASS_DEFAULT FSF_CLASS_3 /* timeout for name-server lookup (in seconds) */ -#define ZFCP_NAMESERVER_TIMEOUT 10 +#define ZFCP_NS_GID_PN_TIMEOUT 10 +#define ZFCP_NS_GA_NXT_TIMEOUT 120 /* largest SCSI command we can process */ /* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */ @@ -241,33 +270,177 @@ struct fcp_logo { wwn_t nport_wwpn; } __attribute__((packed)); -struct fc_ct_iu { - u8 revision; /* 0x01 */ - u8 in_id[3]; /* 0x00 */ - u8 gs_type; /* 0xFC Directory Service */ - u8 gs_subtype; /* 0x02 Name Server */ - u8 options; /* 0x10 synchronous/single exchange */ - u8 reserved0; - u16 cmd_rsp_code; /* 0x0121 GID_PN */ - u16 max_res_size; /* <= (4096 - 16) / 4 */ - u8 reserved1; +/* + * FC-FS stuff + */ +#define R_A_TOV 10 /* seconds */ +#define ZFCP_ELS_TIMEOUT (2 * R_A_TOV) + +#define ZFCP_LS_RJT 0x01 +#define ZFCP_LS_ACC 0x02 +#define ZFCP_LS_RTV 0x0E +#define ZFCP_LS_RLS 0x0F +#define ZFCP_LS_PDISC 0x50 +#define ZFCP_LS_ADISC 0x52 +#define ZFCP_LS_RSCN 0x61 +#define ZFCP_LS_RNID 0x78 +#define ZFCP_LS_RLIR 0x7A +#define ZFCP_LS_RTV_E_D_TOV_FLAG 0x04000000 + +/* LS_ACC Reason Codes */ +#define ZFCP_LS_RJT_INVALID_COMMAND_CODE 0x01 +#define ZFCP_LS_RJT_LOGICAL_ERROR 0x03 +#define ZFCP_LS_RJT_LOGICAL_BUSY 0x05 +#define ZFCP_LS_RJT_PROTOCOL_ERROR 0x07 +#define ZFCP_LS_RJT_UNABLE_TO_PERFORM 0x09 +#define ZFCP_LS_RJT_COMMAND_NOT_SUPPORTED 0x0B +#define ZFCP_LS_RJT_VENDOR_UNIQUE_ERROR 0xFF + +struct zfcp_ls_rjt { + u8 code; + u8 field[3]; + u8 reserved; u8 reason_code; - u8 reason_code_expl; + u8 reason_expl; u8 vendor_unique; - union { +} __attribute__ ((packed)); + +struct zfcp_ls_rtv { + u8 code; + u8 field[3]; +} __attribute__ ((packed)); + +struct zfcp_ls_rtv_acc { + u8 code; + u8 field[3]; + u32 r_a_tov; + u32 e_d_tov; + u32 qualifier; +} __attribute__ ((packed)); + +struct zfcp_ls_rls { + u8 code; + u8 field[3]; + fc_id_t port_id; +} __attribute__ ((packed)); + +struct zfcp_ls_rls_acc { + u8 code; + u8 field[3]; + u32 link_failure_count; + u32 loss_of_sync_count; + u32 loss_of_signal_count; + u32 prim_seq_prot_error; + u32 invalid_transmition_word; + u32 invalid_crc_count; +} __attribute__ ((packed)); + +struct zfcp_ls_pdisc { + u8 code; + u8 field[3]; + u8 common_svc_parm[16]; wwn_t wwpn; - fc_id_t d_id; - } data; + wwn_t wwnn; + struct { + u8 class1[16]; + u8 class2[16]; + u8 class3[16]; + } svc_parm; + u8 reserved[16]; + u8 vendor_version[16]; } __attribute__ ((packed)); +struct zfcp_ls_pdisc_acc { + u8 code; + u8 field[3]; + u8 common_svc_parm[16]; + wwn_t wwpn; + wwn_t wwnn; + struct { + u8 class1[16]; + u8 class2[16]; + u8 class3[16]; + } svc_parm; + u8 reserved[16]; + u8 vendor_version[16]; +} __attribute__ ((packed)); + +struct zfcp_ls_adisc { + u8 code; + u8 field[3]; + fc_id_t hard_nport_id; + wwn_t wwpn; + wwn_t wwnn; + fc_id_t nport_id; +} __attribute__ ((packed)); + +struct zfcp_ls_adisc_acc { + u8 code; + u8 field[3]; + fc_id_t hard_nport_id; + wwn_t wwpn; + wwn_t wwnn; + fc_id_t nport_id; +} __attribute__ ((packed)); + +struct zfcp_ls_rnid { + u8 code; + u8 field[3]; + u8 node_id_format; + u8 reserved[3]; +} __attribute__((packed)); + +/* common identification data */ +struct zfcp_ls_rnid_common_id { + u64 n_port_name; + u64 node_name; +} __attribute__((packed)); + +/* general topology specific identification data */ +struct zfcp_ls_rnid_general_topology_id { + u8 vendor_unique[16]; + u32 associated_type; + u32 physical_port_number; + u32 nr_attached_nodes; + u8 node_management; + u8 ip_version; + u16 port_number; + u8 ip_address[16]; + u8 reserved[2]; + u16 vendor_specific; +} __attribute__((packed)); + +struct zfcp_ls_rnid_acc { + u8 code; + u8 field[3]; + u8 node_id_format; + u8 common_id_length; + u8 reserved; + u8 specific_id_length; + struct zfcp_ls_rnid_common_id + common_id; + struct zfcp_ls_rnid_general_topology_id + specific_id; +} __attribute__((packed)); + +/* + * FC-GS-2 stuff + */ #define ZFCP_CT_REVISION 0x01 #define ZFCP_CT_DIRECTORY_SERVICE 0xFC #define ZFCP_CT_NAME_SERVER 0x02 #define ZFCP_CT_SYNCHRONOUS 0x00 #define ZFCP_CT_GID_PN 0x0121 +#define ZFCP_CT_GA_NXT 0x0100 #define ZFCP_CT_MAX_SIZE 0x1020 #define ZFCP_CT_ACCEPT 0x8002 +/* + * FC-GS-4 stuff + */ +#define ZFCP_CT_TIMEOUT (3 * R_A_TOV) + + /***************** S390 DEBUG FEATURE SPECIFIC DEFINES ***********************/ /* debug feature entries per adapter */ @@ -333,16 +506,6 @@ struct fc_ct_iu { #define ZFCP_LOG_LEVEL_DEBUG 2 #define ZFCP_LOG_LEVEL_TRACE 3 -/* default log levels for different log areas */ -#define ZFCP_LOG_LEVEL_DEFAULT_OTHER ZFCP_LOG_LEVEL_INFO -#define ZFCP_LOG_LEVEL_DEFAULT_SCSI ZFCP_LOG_LEVEL_INFO -#define ZFCP_LOG_LEVEL_DEFAULT_FSF ZFCP_LOG_LEVEL_INFO -#define ZFCP_LOG_LEVEL_DEFAULT_CONFIG ZFCP_LOG_LEVEL_INFO -#define ZFCP_LOG_LEVEL_DEFAULT_CIO ZFCP_LOG_LEVEL_INFO -#define ZFCP_LOG_LEVEL_DEFAULT_QDIO ZFCP_LOG_LEVEL_INFO -#define ZFCP_LOG_LEVEL_DEFAULT_ERP ZFCP_LOG_LEVEL_INFO -#define ZFCP_LOG_LEVEL_DEFAULT_FC ZFCP_LOG_LEVEL_INFO - /* * this allows removal of logging code by the preprocessor * (the most detailed log level still to be compiled in is specified, @@ -350,93 +513,75 @@ struct fc_ct_iu { */ #define ZFCP_LOG_LEVEL_LIMIT ZFCP_LOG_LEVEL_TRACE -/* positional "loglevel" nibble assignment */ -#define ZFCP_LOG_VALUE(zfcp_lognibble) \ +/* get "loglevel" nibble assignment */ +#define ZFCP_GET_LOG_VALUE(zfcp_lognibble) \ ((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF) -#define ZFCP_LOG_VALUE_OTHER ZFCP_LOG_VALUE(ZFCP_LOG_AREA_OTHER) -#define ZFCP_LOG_VALUE_SCSI ZFCP_LOG_VALUE(ZFCP_LOG_AREA_SCSI) -#define ZFCP_LOG_VALUE_FSF ZFCP_LOG_VALUE(ZFCP_LOG_AREA_FSF) -#define ZFCP_LOG_VALUE_CONFIG ZFCP_LOG_VALUE(ZFCP_LOG_AREA_CONFIG) -#define ZFCP_LOG_VALUE_CIO ZFCP_LOG_VALUE(ZFCP_LOG_AREA_CIO) -#define ZFCP_LOG_VALUE_QDIO ZFCP_LOG_VALUE(ZFCP_LOG_AREA_QDIO) -#define ZFCP_LOG_VALUE_ERP ZFCP_LOG_VALUE(ZFCP_LOG_AREA_ERP) -#define ZFCP_LOG_VALUE_FC ZFCP_LOG_VALUE(ZFCP_LOG_AREA_FC) +/* set "loglevel" nibble */ +#define ZFCP_SET_LOG_NIBBLE(value, zfcp_lognibble) \ + (value << (zfcp_lognibble << 2)) /* all log-level defaults are combined to generate initial log-level */ #define ZFCP_LOG_LEVEL_DEFAULTS \ - ((ZFCP_LOG_LEVEL_DEFAULT_OTHER << (ZFCP_LOG_AREA_OTHER<<2)) | \ - (ZFCP_LOG_LEVEL_DEFAULT_SCSI << (ZFCP_LOG_AREA_SCSI<<2)) | \ - (ZFCP_LOG_LEVEL_DEFAULT_FSF << (ZFCP_LOG_AREA_FSF<<2)) | \ - (ZFCP_LOG_LEVEL_DEFAULT_CONFIG << (ZFCP_LOG_AREA_CONFIG<<2)) | \ - (ZFCP_LOG_LEVEL_DEFAULT_CIO << (ZFCP_LOG_AREA_CIO<<2)) | \ - (ZFCP_LOG_LEVEL_DEFAULT_QDIO << (ZFCP_LOG_AREA_QDIO<<2)) | \ - (ZFCP_LOG_LEVEL_DEFAULT_ERP << (ZFCP_LOG_AREA_ERP<<2)) | \ - (ZFCP_LOG_LEVEL_DEFAULT_FC << (ZFCP_LOG_AREA_FC<<2))) - -/* the prefix placed at the beginning of each driver message */ -#define ZFCP_LOG_PREFIX ZFCP_NAME": " - -/* log area specific prefixes */ -#define ZFCP_LOG_AREA_PREFIX_OTHER "" -#define ZFCP_LOG_AREA_PREFIX_SCSI "SCSI: " -#define ZFCP_LOG_AREA_PREFIX_FSF "FSF: " -#define ZFCP_LOG_AREA_PREFIX_CONFIG "config: " -#define ZFCP_LOG_AREA_PREFIX_CIO "common I/O: " -#define ZFCP_LOG_AREA_PREFIX_QDIO "QDIO: " -#define ZFCP_LOG_AREA_PREFIX_ERP "ERP: " -#define ZFCP_LOG_AREA_PREFIX_FC "FC: " + (ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_INFO, ZFCP_LOG_AREA_OTHER) | \ + ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_INFO, ZFCP_LOG_AREA_SCSI) | \ + ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_INFO, ZFCP_LOG_AREA_FSF) | \ + ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_INFO, ZFCP_LOG_AREA_CONFIG) | \ + ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_INFO, ZFCP_LOG_AREA_CIO) | \ + ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_INFO, ZFCP_LOG_AREA_QDIO) | \ + ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_INFO, ZFCP_LOG_AREA_ERP) | \ + ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_INFO, ZFCP_LOG_AREA_FC)) /* check whether we have the right level for logging */ -#define ZFCP_LOG_CHECK(ll) (ZFCP_LOG_VALUE(ZFCP_LOG_AREA)) >= ll +#define ZFCP_LOG_CHECK(level) \ + ((ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA)) >= level) -/* As we have two printks it is possible for them to be seperated by another - * message. This holds true even for printks from within this module. - * In any case there should only be a small readability hit, however. - */ -#define _ZFCP_LOG(m...) \ - { \ - printk( "%s%s: ", \ - ZFCP_LOG_PREFIX ZFCP_LOG_AREA_PREFIX, \ - __FUNCTION__); \ - printk(m); \ - } +/* logging routine for zfcp */ +#define _ZFCP_LOG(fmt, args...) \ + printk(KERN_ERR ZFCP_NAME": %s(%d): " fmt, __FUNCTION__, \ + __LINE__ , ##args); -#define ZFCP_LOG(ll, m...) \ - if (ZFCP_LOG_CHECK(ll)) \ - _ZFCP_LOG(m) +#define ZFCP_LOG(level, fmt, args...) \ + if (ZFCP_LOG_CHECK(level)) \ + _ZFCP_LOG(fmt , ##args) #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL -#define ZFCP_LOG_NORMAL(m...) -#else /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_NORMAL */ -#define ZFCP_LOG_NORMAL(m...) ZFCP_LOG(ZFCP_LOG_LEVEL_NORMAL, m) +# define ZFCP_LOG_NORMAL(fmt, args...) +#else +# define ZFCP_LOG_NORMAL(fmt, args...) \ + if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_NORMAL)) \ + printk(KERN_ERR ZFCP_NAME": " fmt , ##args); #endif #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO -#define ZFCP_LOG_INFO(m...) -#else /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_INFO */ -#define ZFCP_LOG_INFO(m...) ZFCP_LOG(ZFCP_LOG_LEVEL_INFO, m) +# define ZFCP_LOG_INFO(fmt, args...) +#else +# define ZFCP_LOG_INFO(fmt, args...) \ + if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_INFO)) \ + printk(KERN_ERR ZFCP_NAME": " fmt , ##args); #endif #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG -#define ZFCP_LOG_DEBUG(m...) -#else /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_DEBUG */ -#define ZFCP_LOG_DEBUG(m...) ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, m) +# define ZFCP_LOG_DEBUG(fmt, args...) +#else +# define ZFCP_LOG_DEBUG(fmt, args...) \ + ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, fmt , ##args) #endif #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE -#define ZFCP_LOG_TRACE(m...) -#else /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_TRACE */ -#define ZFCP_LOG_TRACE(m...) ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, m) +# define ZFCP_LOG_TRACE(fmt, args...) +#else +# define ZFCP_LOG_TRACE(fmt, args...) \ + ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args) #endif -#ifdef ZFCP_PRINT_FLAGS -extern u32 flags_dump; -#define ZFCP_LOG_FLAGS(ll, m...) \ - if (ll<=flags_dump) \ - _ZFCP_LOG(m) +#ifndef ZFCP_PRINT_FLAGS +# define ZFCP_LOG_FLAGS(level, fmt, args...) #else -#define ZFCP_LOG_FLAGS(ll, m...) +extern u32 flags_dump; +# define ZFCP_LOG_FLAGS(level, fmt, args...) \ + if (level <= flags_dump) \ + _ZFCP_LOG(fmt , ##args) #endif /*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/ @@ -506,19 +651,19 @@ extern u32 flags_dump; #define ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP 0x00000400 #define ZFCP_STATUS_FSFREQ_RETRY 0x00000800 #define ZFCP_STATUS_FSFREQ_DISMISSED 0x00001000 -#define ZFCP_STATUS_FSFREQ_POOLBUF 0x00002000 /*********************** ERROR RECOVERY PROCEDURE DEFINES ********************/ #define ZFCP_MAX_ERPS 3 -#define ZFCP_ERP_FSFREQ_TIMEOUT (100 * HZ) +#define ZFCP_ERP_FSFREQ_TIMEOUT (30 * HZ) #define ZFCP_ERP_MEMWAIT_TIMEOUT HZ #define ZFCP_STATUS_ERP_TIMEDOUT 0x10000000 #define ZFCP_STATUS_ERP_CLOSE_ONLY 0x01000000 #define ZFCP_STATUS_ERP_DISMISSING 0x00100000 #define ZFCP_STATUS_ERP_DISMISSED 0x00200000 +#define ZFCP_STATUS_ERP_LOWMEM 0x00400000 #define ZFCP_ERP_STEP_UNINITIALIZED 0x00000000 #define ZFCP_ERP_STEP_FSF_XCONFIG 0x00000001 @@ -546,19 +691,55 @@ extern u32 flags_dump; #define ZFCP_ERP_DISMISSED 0x4 #define ZFCP_ERP_NOMEM 0x5 -/************************* STRUCTURE DEFINITIONS *****************************/ +/******************** CFDC SPECIFIC STUFF *****************************/ + +/* Firewall data channel sense data record */ +struct zfcp_cfdc_sense_data { + u32 signature; /* Request signature */ + u32 devno; /* FCP adapter device number */ + u32 command; /* Command code */ + u32 fsf_status; /* FSF request status and status qualifier */ + u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; + u8 payloads[256]; /* Access conflicts list */ + u8 control_file[0]; /* Access control table */ +}; + +#define ZFCP_CFDC_SIGNATURE 0xCFDCACDF + +#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001 +#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101 +#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201 +#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401 +#define ZFCP_CFDC_CMND_UPLOAD 0x00010002 + +#define ZFCP_CFDC_DOWNLOAD 0x00000001 +#define ZFCP_CFDC_UPLOAD 0x00000002 +#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000 + +#define ZFCP_CFDC_DEV_NAME "zfcp_cfdc" +#define ZFCP_CFDC_DEV_MAJOR MISC_MAJOR +#define ZFCP_CFDC_DEV_MINOR MISC_DYNAMIC_MINOR + +#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE 127 * 1024 + +static const char zfcp_act_subtable_type[5][8] = { + {"unknown"}, {"OS"}, {"WWPN"}, {"DID"}, {"LUN"} +}; + + +/************************* STRUCTURE DEFINITIONS *****************************/ struct zfcp_fsf_req; -typedef void zfcp_send_generic_handler_t(struct zfcp_fsf_req*); +/* holds various memory pools of an adapter */ struct zfcp_adapter_mempool { - mempool_t *status_read_fsf; - mempool_t *status_read_buf; - mempool_t *nameserver; - mempool_t *erp_fsf; - mempool_t *fcp_command_fsf; - struct timer_list fcp_command_fsf_timer; + mempool_t *fsf_req_erp; + mempool_t *fsf_req_scsi; + mempool_t *fsf_req_abort; + mempool_t *fsf_req_status_read; + mempool_t *data_status_read; + mempool_t *data_gid_pn; }; struct zfcp_exchange_config_data{ @@ -587,7 +768,7 @@ struct zfcp_close_physical_port { struct zfcp_send_fcp_command_task { struct zfcp_fsf_req *fsf_req; struct zfcp_unit *unit; - Scsi_Cmnd *scsi_cmnd; + struct scsi_cmnd *scsi_cmnd; unsigned long start_jiffies; }; @@ -600,20 +781,119 @@ struct zfcp_abort_fcp_command { struct zfcp_unit *unit; }; -struct zfcp_send_generic { +/* + * header for CT_IU + */ +struct ct_hdr { + u8 revision; // 0x01 + u8 in_id[3]; // 0x00 + u8 gs_type; // 0xFC Directory Service + u8 gs_subtype; // 0x02 Name Server + u8 options; // 0x00 single bidirectional exchange + u8 reserved0; + u16 cmd_rsp_code; // 0x0121 GID_PN, or 0x0100 GA_NXT + u16 max_res_size; // <= (4096 - 16) / 4 + u8 reserved1; + u8 reason_code; + u8 reason_code_expl; + u8 vendor_unique; +} __attribute__ ((packed)); + +/* nameserver request CT_IU -- for requests where + * a port name is required */ +struct ct_iu_gid_pn_req { + struct ct_hdr header; + wwn_t wwpn; +} __attribute__ ((packed)); + +/* nameserver request CT_IU -- for requests where + * a port identifier is required */ +struct ct_iu_ga_nxt_req { + struct ct_hdr header; + fc_id_t d_id; +} __attribute__ ((packed)); + +/* FS_ACC IU and data unit for GID_PN nameserver request */ +struct ct_iu_gid_pn_resp { + struct ct_hdr header; + fc_id_t d_id; +} __attribute__ ((packed)); + +/* FS_ACC IU and data unit for GA_NXT nameserver request */ +struct ct_iu_ga_nxt_resp { + struct ct_hdr header; + u8 port_type; + u8 port_id[3]; + u64 port_wwn; + u8 port_symbolic_name_length; + u8 port_symbolic_name[255]; + u64 node_wwn; + u8 node_symbolic_name_length; + u8 node_symbolic_name[255]; + u64 initial_process_associator; + u8 node_ip[16]; + u32 cos; + u8 fc4_types[32]; + u8 port_ip[16]; + u64 fabric_wwn; + u8 reserved; + u8 hard_address[3]; +} __attribute__ ((packed)); + +typedef void (*zfcp_send_ct_handler_t)(unsigned long); + +/* used to pass parameters to zfcp_send_ct() */ +struct zfcp_send_ct { + struct zfcp_port *port; + struct scatterlist *req; + struct scatterlist *resp; + unsigned int req_count; + unsigned int resp_count; + zfcp_send_ct_handler_t handler; + unsigned long handler_data; + mempool_t *pool; /* mempool for ct not for fsf_req */ + int timeout; + struct timer_list *timer; + struct completion *completion; + int status; +}; + +/* used for name server requests in error recovery */ +struct zfcp_gid_pn_data { + struct zfcp_send_ct ct; + struct scatterlist req; + struct scatterlist resp; + struct ct_iu_gid_pn_req ct_iu_req; + struct ct_iu_gid_pn_resp ct_iu_resp; struct zfcp_port *port; - char *outbuf; - char *inbuf; - int outbuf_length; - int inbuf_length; - zfcp_send_generic_handler_t *handler; +}; + +typedef int (*zfcp_send_els_handler_t)(unsigned long); + +/* used to pass parameters to zfcp_send_els() */ +/* ToDo merge send_ct() and send_els() and corresponding structs */ +struct zfcp_send_els { + struct zfcp_port *port; + struct scatterlist *req; + struct scatterlist *resp; + unsigned int req_count; + unsigned int resp_count; + zfcp_send_els_handler_t handler; unsigned long handler_data; + struct completion *completion; + int ls_code; + int status; }; struct zfcp_status_read { struct fsf_status_read_buffer *buffer; }; +struct zfcp_fsf_done { + struct completion *complete; + int status; +}; + /* request specific data */ union zfcp_req_data { struct zfcp_exchange_config_data exchange_config_data; @@ -626,7 +906,8 @@ union zfcp_req_data { struct zfcp_send_fcp_command_task_management send_fcp_command_task_management; struct zfcp_abort_fcp_command abort_fcp_command; - struct zfcp_send_generic send_generic; + struct zfcp_send_ct *send_ct; + struct zfcp_send_els *send_els; struct zfcp_status_read status_read; }; @@ -671,6 +952,9 @@ struct zfcp_adapter { u32 fc_link_speed; /* FC interface speed */ u32 hydra_version; /* Hydra version */ u32 fsf_lic_version; + u32 supported_features;/* of FCP channel */ + u32 hardware_version; /* of FCP channel */ + u8 serial_number[32]; /* of hardware */ struct Scsi_Host *scsi_host; /* Pointer to mid-layer */ unsigned char name[9]; @@ -704,6 +988,10 @@ struct zfcp_adapter { wait_queue_head_t erp_done_wqh; struct zfcp_erp_action erp_action; /* pending error recovery */ atomic_t erp_counter; + u32 erp_total_count; /* total nr of enqueued erp + actions */ + u32 erp_low_mem_count; /* nr of erp actions waiting + for memory */ struct zfcp_port *nameserver_port; /* adapter's nameserver */ debug_info_t *erp_dbf; /* S/390 debug features */ debug_info_t *abort_dbf; @@ -751,7 +1039,7 @@ struct zfcp_unit { scsi_lun_t scsi_lun; /* own SCSI LUN */ fcp_lun_t fcp_lun; /* own FCP_LUN */ u32 handle; /* handle assigned by FSF */ - Scsi_Device *device; /* scsi device struct pointer */ + struct scsi_device *device; /* scsi device struct pointer */ struct zfcp_erp_action erp_action; /* pending error recovery */ atomic_t erp_counter; struct device sysfs_device; /* sysfs device */ @@ -765,8 +1053,14 @@ struct zfcp_fsf_req { u32 specific_magic; /* structure specific magic */ struct list_head list; /* list of FSF requests */ struct zfcp_adapter *adapter; /* adapter request belongs to */ - u8 sbal_count; /* # of SBALs in FSF request */ - u8 sbal_index; /* position of 1st SBAL */ + u8 sbal_number; /* nr of SBALs free for use */ + u8 sbal_first; /* first SBAL for this request */ + u8 sbal_last; /* last possible SBAL for + this reuest */ + u8 sbal_curr; /* current SBAL during creation + of request */ + u8 sbale_curr; /* current SBALE during creation + of request */ wait_queue_head_t completion_wq; /* can be used by a routine to wait for completion */ volatile u32 status; /* status of this request */ @@ -776,13 +1070,15 @@ struct zfcp_fsf_req { union zfcp_req_data data; /* Info fields of request */ struct zfcp_erp_action *erp_action; /* used if this request is issued on behalf of erp */ + mempool_t *pool; /* used if request was alloacted + from emergency pool */ }; typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*); /* driver data */ struct zfcp_data { - Scsi_Host_Template scsi_host_template; + struct scsi_host_template scsi_host_template; atomic_t status; /* Module status flags */ struct list_head adapter_list_head; /* head of adapter list */ struct list_head adapter_remove_lh; /* head of adapters to be @@ -792,7 +1088,7 @@ struct zfcp_data { struct list_head status_read_send_head; struct semaphore status_read_sema; wait_queue_head_t status_read_thread_wqh; - u16 adapters; /* # of adapters in list */ + u32 adapters; /* # of adapters in list */ rwlock_t config_lock; /* serialises changes to adapter/port/unit lists */ @@ -829,6 +1125,24 @@ struct zfcp_statistics { }; #endif +struct zfcp_sg_list { + struct scatterlist *sg; + unsigned int count; +}; + +/* number of elements for various memory pools */ +#define ZFCP_POOL_FSF_REQ_ERP_NR 1 +#define ZFCP_POOL_FSF_REQ_SCSI_NR 1 +#define ZFCP_POOL_FSF_REQ_ABORT_NR 1 +#define ZFCP_POOL_STATUS_READ_NR ZFCP_STATUS_READS_RECOM +#define ZFCP_POOL_DATA_GID_PN_NR 1 + +/* struct used by memory pools for fsf_requests */ +struct zfcp_fsf_req_pool_element { + struct zfcp_fsf_req fsf_req; + struct fsf_qtcb qtcb; +}; + /********************** ZFCP SPECIFIC DEFINES ********************************/ #define ZFCP_FSFREQ_CLEANUP_TIMEOUT HZ/10 @@ -836,6 +1150,7 @@ struct zfcp_statistics { #define ZFCP_KNOWN 0x00000001 #define ZFCP_REQ_AUTO_CLEANUP 0x00000002 #define ZFCP_WAIT_FOR_SBAL 0x00000004 +#define ZFCP_REQ_NO_QTCB 0x00000008 #define ZFCP_SET 0x00000100 #define ZFCP_CLEAR 0x00000200 diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index ec3fe77c3af4..20618f67f7cf 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -4,11 +4,12 @@ * * FCP adapter driver for IBM eServer zSeries * - * Copyright 2002 IBM Corporation + * (C) Copyright IBM Corp. 2002, 2004 + * * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> - * Aron Zeh <arzeh@de.ibm.com> - * Wolfgang Taphorn <taphorn@de.ibm.com> + * Aron Zeh + * Wolfgang Taphorn * Stefan Bader <stefan.bader@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> * @@ -28,12 +29,15 @@ */ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_ERP + /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_ERP_REVISION "$Revision: 1.39 $" +#define ZFCP_ERP_REVISION "$Revision: 1.44 $" #include "zfcp_ext.h" +static int zfcp_els(struct zfcp_port *, u8); +static int zfcp_els_handler(unsigned long); + static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *, int); static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *, int); static int zfcp_erp_port_reopen_internal(struct zfcp_port *, int); @@ -326,6 +330,375 @@ zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear_mask) return retval; } + +/* + * function: zfcp_els + * + * purpose: Originator of the ELS commands + * + * returns: 0 - Operation completed successfuly + * -EINVAL - Unknown IOCTL command or invalid sense data record + * -ENOMEM - Insufficient memory + * -EPERM - Cannot create or queue FSF request + */ +int +zfcp_els(struct zfcp_port *port, u8 ls_code) +{ + struct zfcp_send_els *send_els; + struct zfcp_ls_rls *rls; + struct zfcp_ls_pdisc *pdisc; + struct zfcp_ls_adisc *adisc; + struct page *page = NULL; + void *req; + int retval = 0; + + send_els = kmalloc(sizeof(struct zfcp_send_els), GFP_ATOMIC); + if (send_els == NULL) + goto nomem; + + send_els->req = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC); + if (send_els->req == NULL) + goto nomem; + send_els->req_count = 1; + + send_els->resp = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC); + if (send_els->resp == NULL) + goto nomem; + send_els->resp_count = 1; + + page = alloc_pages(GFP_ATOMIC, 0); + if (page == NULL) + goto nomem; + send_els->req->page = page; + send_els->resp->page = page; + send_els->req->offset = 0; + send_els->resp->offset = PAGE_SIZE >> 1; + + send_els->port = port; + send_els->ls_code = ls_code; + send_els->handler = zfcp_els_handler; + send_els->handler_data = (unsigned long)send_els; + send_els->completion = NULL; + + req = zfcp_sg_to_address(send_els->req); + + *(u32*)req = 0; + *(u8*)req = ls_code; + + switch (ls_code) { + + case ZFCP_LS_RTV: + send_els->req->length = sizeof(struct zfcp_ls_rtv); + send_els->resp->length = sizeof(struct zfcp_ls_rtv_acc); + ZFCP_LOG_NORMAL( + "RTV request from sid 0x%06x to did 0x%06x\n", + port->adapter->s_id, port->d_id); + break; + + case ZFCP_LS_RLS: + send_els->req->length = sizeof(struct zfcp_ls_rls); + send_els->resp->length = sizeof(struct zfcp_ls_rls_acc); + rls = (struct zfcp_ls_rls*)req; + rls->port_id = port->adapter->s_id; + ZFCP_LOG_NORMAL( + "RLS request from sid 0x%06x to did 0x%06x " + "with payload(port_id=0x%06x)\n", + port->adapter->s_id, port->d_id, rls->port_id); + break; + + case ZFCP_LS_PDISC: + send_els->req->length = sizeof(struct zfcp_ls_pdisc); + send_els->resp->length = sizeof(struct zfcp_ls_pdisc_acc); + pdisc = (struct zfcp_ls_pdisc*)req; + pdisc->wwpn = port->adapter->wwpn; + pdisc->wwnn = port->adapter->wwnn; + ZFCP_LOG_NORMAL( + "PDISC request from sid 0x%06x to did 0x%06x " + "with payload(wwpn=0x%016Lx wwnn=0x%016Lx)\n", + port->adapter->s_id, port->d_id, + pdisc->wwpn, pdisc->wwnn); + break; + + case ZFCP_LS_ADISC: + send_els->req->length = sizeof(struct zfcp_ls_adisc); + send_els->resp->length = sizeof(struct zfcp_ls_adisc_acc); + adisc = (struct zfcp_ls_adisc*)req; + adisc->hard_nport_id = port->adapter->s_id; + adisc->wwpn = port->adapter->wwpn; + adisc->wwnn = port->adapter->wwnn; + adisc->nport_id = port->adapter->s_id; + ZFCP_LOG_NORMAL( + "ADISC request from sid 0x%06x to did 0x%06x " + "with payload(wwpn=0x%016Lx wwnn=0x%016Lx " + "hard_nport_id=0x%06x nport_id=0x%06x)\n", + port->adapter->s_id, port->d_id, + adisc->wwpn, adisc->wwnn, + adisc->hard_nport_id, adisc->nport_id); + break; + + default: + ZFCP_LOG_NORMAL( + "ELS command code 0x%02x is not supported\n", ls_code); + retval = -EINVAL; + goto invalid_ls_code; + } + + retval = zfcp_fsf_send_els(send_els); + if (retval != 0) { + ZFCP_LOG_NORMAL( + "ELS request could not be processed " + "(sid=0x%06x did=0x%06x)\n", + port->adapter->s_id, port->d_id); + retval = -EPERM; + } + + goto out; + +nomem: + ZFCP_LOG_INFO("Out of memory!\n"); + retval = -ENOMEM; + +invalid_ls_code: + if (page != NULL) + __free_pages(page, 0); + if (send_els != NULL) { + if (send_els->req != NULL) + kfree(send_els->req); + if (send_els->resp != NULL) + kfree(send_els->resp); + kfree(send_els); + } + +out: + return retval; +} + + +/* + * function: zfcp_els_handler + * + * purpose: Handler for all kind of ELSs + * + * returns: 0 - Operation completed successfuly + * -ENXIO - ELS has been rejected + * -EPERM - Port forced reopen failed + */ +int +zfcp_els_handler(unsigned long data) +{ + struct zfcp_send_els *send_els = (struct zfcp_send_els*)data; + struct zfcp_port *port = send_els->port; + struct zfcp_ls_rjt *rjt; + struct zfcp_ls_rtv_acc *rtv; + struct zfcp_ls_rls_acc *rls; + struct zfcp_ls_pdisc_acc *pdisc; + struct zfcp_ls_adisc_acc *adisc; + void *req, *resp; + u8 req_code, resp_code; + int retval = 0; + + if (send_els->status != 0) + goto skip_fsfstatus; + + req = (void*)((page_to_pfn(send_els->req->page) << PAGE_SHIFT) + send_els->req->offset); + resp = (void*)((page_to_pfn(send_els->resp->page) << PAGE_SHIFT) + send_els->resp->offset); + req_code = *(u8*)req; + resp_code = *(u8*)resp; + + switch (resp_code) { + + case ZFCP_LS_RJT: + rjt = (struct zfcp_ls_rjt*)resp; + + switch (rjt->reason_code) { + + case ZFCP_LS_RJT_INVALID_COMMAND_CODE: + ZFCP_LOG_NORMAL( + "Invalid command code " + "(wwpn=0x%016Lx command=0x%02x)\n", + (unsigned long long)port->wwpn, + req_code); + break; + + case ZFCP_LS_RJT_LOGICAL_ERROR: + ZFCP_LOG_NORMAL( + "Logical error " + "(wwpn=0x%016Lx reason_explanation=0x%02x)\n", + (unsigned long long)port->wwpn, + rjt->reason_expl); + break; + + case ZFCP_LS_RJT_LOGICAL_BUSY: + ZFCP_LOG_NORMAL( + "Logical busy " + "(wwpn=0x%016Lx reason_explanation=0x%02x)\n", + (unsigned long long)port->wwpn, + rjt->reason_expl); + break; + + case ZFCP_LS_RJT_PROTOCOL_ERROR: + ZFCP_LOG_NORMAL( + "Protocol error " + "(wwpn=0x%016Lx reason_explanation=0x%02x)\n", + (unsigned long long)port->wwpn, + rjt->reason_expl); + break; + + case ZFCP_LS_RJT_UNABLE_TO_PERFORM: + ZFCP_LOG_NORMAL( + "Unable to perform command requested " + "(wwpn=0x%016Lx reason_explanation=0x%02x)\n", + (unsigned long long)port->wwpn, + rjt->reason_expl); + break; + + case ZFCP_LS_RJT_COMMAND_NOT_SUPPORTED: + ZFCP_LOG_NORMAL( + "Command not supported " + "(wwpn=0x%016Lx command=0x%02x)\n", + (unsigned long long)port->wwpn, + req_code); + break; + + case ZFCP_LS_RJT_VENDOR_UNIQUE_ERROR: + ZFCP_LOG_NORMAL( + "Vendor unique error " + "(wwpn=0x%016Lx vendor_unique=0x%02x)\n", + (unsigned long long)port->wwpn, + rjt->vendor_unique); + break; + + default: + ZFCP_LOG_NORMAL( + "ELS has been rejected by remote port " + "with WWPN 0x%Lx on the adapter %s " + "with the reason code 0x%02x\n", + port->wwpn, zfcp_get_busid_by_port(port), + rjt->reason_code); + } + retval = -ENXIO; + break; + + case ZFCP_LS_ACC: + switch (req_code) { + + case ZFCP_LS_RTV: + rtv = (struct zfcp_ls_rtv_acc*)resp; + ZFCP_LOG_NORMAL( + "RTV response from did 0x%06x to sid 0x%06x " + "with payload(R_A_TOV=%ds E_D_TOV=%d%cs)\n", + port->d_id, port->adapter->s_id, + rtv->r_a_tov, rtv->e_d_tov, + rtv->qualifier & ZFCP_LS_RTV_E_D_TOV_FLAG ? + 'n' : 'm'); + break; + + case ZFCP_LS_RLS: + rls = (struct zfcp_ls_rls_acc*)resp; + ZFCP_LOG_NORMAL( + "RLS response from did 0x%06x to sid 0x%06x " + "with payload(link_failure_count=%u " + "loss_of_sync_count=%u " + "loss_of_signal_count=%u " + "primitive_sequence_protocol_error=%u " + "invalid_transmition_word=%u " + "invalid_crc_count=%u)\n", + port->d_id, port->adapter->s_id, + rls->link_failure_count, + rls->loss_of_sync_count, + rls->loss_of_signal_count, + rls->prim_seq_prot_error, + rls->invalid_transmition_word, + rls->invalid_crc_count); + break; + + case ZFCP_LS_PDISC: + pdisc = (struct zfcp_ls_pdisc_acc*)resp; + ZFCP_LOG_NORMAL( + "PDISC response from did 0x%06x to sid 0x%06x " + "with payload(wwpn=0x%016Lx wwnn=0x%016Lx " + "vendor='%-16s')\n", + port->d_id, port->adapter->s_id, + (unsigned long long)pdisc->wwpn, + (unsigned long long)pdisc->wwnn, + pdisc->vendor_version); + break; + + case ZFCP_LS_ADISC: + adisc = (struct zfcp_ls_adisc_acc*)resp; + ZFCP_LOG_NORMAL( + "ADISC response from did 0x%06x to sid 0x%06x " + "with payload(wwpn=0x%016Lx wwnn=0x%016Lx " + "hard_nport_id=0x%06x nport_id=0x%06x)\n", + port->d_id, port->adapter->s_id, + (unsigned long long)adisc->wwpn, + (unsigned long long)adisc->wwnn, + adisc->hard_nport_id, adisc->nport_id); + /* FIXME: missing wwnn value in port struct */ + if (port->wwnn == 0) + port->wwnn = adisc->wwnn; + break; + } + break; + + default: + ZFCP_LOG_NORMAL( + "Unknown payload code 0x%02x received on a request " + "0x%02x from sid 0x%06x to did 0x%06x, " + "port needs to be reopened\n", + req_code, resp_code, port->adapter->s_id, port->d_id); + retval = zfcp_erp_port_forced_reopen(port, 0); + if (retval != 0) { + ZFCP_LOG_NORMAL( + "Cannot reopen a remote port " + "with WWPN 0x%Lx on the adapter %s\n", + port->wwpn, zfcp_get_busid_by_port(port)); + retval = -EPERM; + } + } + +skip_fsfstatus: + __free_pages(send_els->req->page, 0); + kfree(send_els->req); + kfree(send_els->resp); + + return retval; +} + + +/* + * function: zfcp_test_link + * + * purpose: Test a status of a link to a remote port using the ELS command ADISC + * + * returns: 0 - Link is OK + * -EPERM - Port forced reopen failed + */ +int +zfcp_test_link(struct zfcp_port *port) +{ + int retval; + + retval = zfcp_els(port, ZFCP_LS_ADISC); + if (retval != 0) { + ZFCP_LOG_NORMAL( + "Port with WWPN 0x%Lx on the adapter %s " + "needs to be reopened\n", + port->wwpn, zfcp_get_busid_by_port(port)); + retval = zfcp_erp_port_forced_reopen(port, 0); + if (retval != 0) { + ZFCP_LOG_NORMAL( + "Cannot reopen a remote port " + "with WWPN 0x%Lx on the adapter %s\n", + port->wwpn, zfcp_get_busid_by_port(port)); + retval = -EPERM; + } + } + + return retval; +} + + /* * function: * @@ -741,37 +1114,27 @@ zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action) "a_ca_disreq"); fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; } + if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) { + ZFCP_LOG_NORMAL + ("error: Error Recovery Procedure " + "step timed out. The action flag " + "is 0x%x. The FSF request " + "is at 0x%lx\n", + erp_action->action, + (unsigned long) + erp_action->fsf_req); + } /* * If fsf_req is neither dismissed nor completed - * then keep it running asynchronously and don't mess with - * the association of erp_action and fsf_req. + * then keep it running asynchronously and don't mess + * with the association of erp_action and fsf_req. */ if (fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED | ZFCP_STATUS_FSFREQ_DISMISSED)) { - /* forget about association between fsf_req and erp_action */ + /* forget about association between fsf_req + and erp_action */ fsf_req->erp_action = NULL; erp_action->fsf_req = NULL; - /* some special things for time out conditions */ - if (erp_action-> status & ZFCP_STATUS_ERP_TIMEDOUT) { - ZFCP_LOG_NORMAL - ("error: Error Recovery Procedure step timed out. " - "The action flag is 0x%x. The FSF request " - "is at 0x%lx\n", erp_action->action, - (unsigned long) erp_action->fsf_req); - /* fight for low memory buffer, if required */ - if (fsf_req-> - status & ZFCP_STATUS_FSFREQ_POOL) { - debug_text_event(adapter->erp_dbf, 3, - "a_ca_lowmem"); - ZFCP_LOG_NORMAL - ("error: The error recovery action using the " - "low memory pool timed out. Restarting IO on " - "the adapter %s to free it.\n", - zfcp_get_busid_by_adapter - (adapter)); - zfcp_erp_adapter_reopen_internal(adapter, 0); - } - } } } else { debug_text_event(adapter->erp_dbf, 3, "a_ca_gonereq"); @@ -1122,12 +1485,36 @@ zfcp_erp_strategy(struct zfcp_erp_action *erp_action) goto unlock; case ZFCP_ERP_NOMEM: /* no memory to continue immediately, let it sleep */ + if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) { + ++adapter->erp_low_mem_count; + erp_action->status |= ZFCP_STATUS_ERP_LOWMEM; + } + /* This condition is true if there is no memory available + for any erp_action on this adapter. This implies that there + are no elements in the memory pool(s) left for erp_actions. + This might happen if an erp_action that used a memory pool + element was timed out. + */ + if (adapter->erp_total_count == adapter->erp_low_mem_count) { + debug_text_event(adapter->erp_dbf, 3, "a_st_lowmem"); + ZFCP_LOG_NORMAL + ("error: Out of memory. No mempool elements " + "available. Restarting IO on the adapter %s " + "to free mempool.\n", + zfcp_get_busid_by_adapter(adapter)); + zfcp_erp_adapter_reopen_internal(adapter, 0); + } else { debug_text_event(adapter->erp_dbf, 2, "a_st_memw"); retval = zfcp_erp_strategy_memwait(erp_action); - /* fall through, waiting for memory means action continues */ + } + goto unlock; case ZFCP_ERP_CONTINUES: /* leave since this action runs asynchronously */ debug_text_event(adapter->erp_dbf, 6, "a_st_cont"); + if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) { + --adapter->erp_low_mem_count; + erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM; + } goto unlock; } /* ok, finished action (whatever its result is) */ @@ -1531,16 +1918,24 @@ zfcp_erp_strategy_check_unit(struct zfcp_unit *unit, int result) debug_event(unit->port->adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t)); - if (result == ZFCP_ERP_SUCCEEDED) { + switch (result) { + case ZFCP_ERP_SUCCEEDED : atomic_set(&unit->erp_counter, 0); zfcp_erp_unit_unblock(unit); - } else { - /* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */ + break; + case ZFCP_ERP_FAILED : atomic_inc(&unit->erp_counter); - if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS) { + if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS) zfcp_erp_unit_failed(unit); - result = ZFCP_ERP_EXIT; - } + break; + case ZFCP_ERP_EXIT : + /* nothing */ + break; + } + + if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) { + zfcp_erp_unit_block(unit, 0); /* for ZFCP_ERP_SUCCEEDED */ + result = ZFCP_ERP_EXIT; } return result; @@ -1559,16 +1954,24 @@ zfcp_erp_strategy_check_port(struct zfcp_port *port, int result) debug_text_event(port->adapter->erp_dbf, 5, "p_stct"); debug_event(port->adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t)); - if (result == ZFCP_ERP_SUCCEEDED) { + switch (result) { + case ZFCP_ERP_SUCCEEDED : atomic_set(&port->erp_counter, 0); zfcp_erp_port_unblock(port); - } else { - /* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */ + break; + case ZFCP_ERP_FAILED : atomic_inc(&port->erp_counter); - if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) { + if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) + break; + case ZFCP_ERP_EXIT : + /* nothing */ + break; + } + + if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) { + zfcp_erp_port_block(port, 0); /* for ZFCP_ERP_SUCCEEDED */ + result = ZFCP_ERP_EXIT; zfcp_erp_port_failed(port); - result = ZFCP_ERP_EXIT; - } } return result; @@ -1586,16 +1989,24 @@ zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter, int result) { debug_text_event(adapter->erp_dbf, 5, "a_stct"); - if (result == ZFCP_ERP_SUCCEEDED) { + switch (result) { + case ZFCP_ERP_SUCCEEDED : atomic_set(&adapter->erp_counter, 0); zfcp_erp_adapter_unblock(adapter); - } else { - /* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */ + break; + case ZFCP_ERP_FAILED : atomic_inc(&adapter->erp_counter); - if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) { + if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) zfcp_erp_adapter_failed(adapter); - result = ZFCP_ERP_EXIT; - } + break; + case ZFCP_ERP_EXIT : + /* nothing */ + break; + } + + if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) { + zfcp_erp_adapter_block(adapter, 0); /* for ZFCP_ERP_SUCCEEDED */ + result = ZFCP_ERP_EXIT; } return result; @@ -1999,10 +2410,10 @@ zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *erp_action, int close) int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action) { - int retval = 0; - struct zfcp_adapter *adapter = erp_action->adapter; + int retval; int i; - volatile struct qdio_buffer_element *buffere; + volatile struct qdio_buffer_element *sbale; + struct zfcp_adapter *adapter = erp_action->adapter; int retval_cleanup = 0; if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) { @@ -2034,10 +2445,10 @@ zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action) * put buffers into response queue, */ for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { - buffere = &(adapter->response_queue.buffer[i]->element[0]); - buffere->length = 0; - buffere->flags = SBAL_FLAGS_LAST_ENTRY; - buffere->addr = 0; + sbale = &(adapter->response_queue.buffer[i]->element[0]); + sbale->length = 0; + sbale->flags = SBAL_FLAGS_LAST_ENTRY; + sbale->addr = 0; } ZFCP_LOG_TRACE("Calling do QDIO busid=%s, flags=0x%x, queue_no=%i, " @@ -2591,7 +3002,7 @@ zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *erp_action) if (atomic_test_mask((ZFCP_STATUS_COMMON_OPEN | ZFCP_STATUS_PORT_DID_DID), &port->status)) { - ZFCP_LOG_DEBUG("port wwpn=0x%Lx is open ", port->wwpn); + ZFCP_LOG_DEBUG("port wwpn=0x%Lx is open\n", port->wwpn); retval = ZFCP_ERP_SUCCEEDED; } else { ZFCP_LOG_DEBUG("failed to open port wwpn=0x%Lx\n", @@ -2845,7 +3256,7 @@ zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *erp_action) struct zfcp_port *port = erp_action->port; zfcp_erp_timeout_init(erp_action); - retval = zfcp_nameserver_request(erp_action); + retval = zfcp_ns_gid_pn_request(erp_action); if (retval == -ENOMEM) { debug_text_event(adapter->erp_dbf, 5, "p_pstn_nomem"); debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t)); @@ -3087,6 +3498,10 @@ zfcp_erp_action_enqueue(int action, * efficient. */ + if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, + &adapter->status)) + goto out; + debug_event(adapter->erp_dbf, 4, &action, sizeof (int)); /* check whether we really need this */ switch (action) { @@ -3222,6 +3637,8 @@ zfcp_erp_action_enqueue(int action, erp_action->action = action; erp_action->status = status; + ++adapter->erp_total_count; + /* finally put it into 'ready' queue and kick erp thread */ list_add(&erp_action->list, &adapter->erp_ready_head); up(&adapter->erp_ready_sem); @@ -3243,6 +3660,12 @@ zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) int retval = 0; struct zfcp_adapter *adapter = erp_action->adapter; + --adapter->erp_total_count; + if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) { + --adapter->erp_low_mem_count; + erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM; + } + debug_text_event(adapter->erp_dbf, 4, "a_actdeq"); debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof (int)); list_del(&erp_action->list); @@ -3404,4 +3827,3 @@ zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action) } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 685edbef5f76..dc39b5ed8a89 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -4,11 +4,12 @@ * * FCP adapter driver for IBM eServer zSeries * - * Copyright 2002 IBM Corporation + * (C) Copyright IBM Corp. 2002, 2004 + * * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> - * Aron Zeh <arzeh@de.ibm.com> - * Wolfgang Taphorn <taphorn@de.ibm.com> + * Aron Zeh + * Wolfgang Taphorn * Stefan Bader <stefan.bader@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> * @@ -30,7 +31,7 @@ #ifndef ZFCP_EXT_H #define ZFCP_EXT_H /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_EXT_REVISION "$Revision: 1.38 $" +#define ZFCP_EXT_REVISION "$Revision: 1.45 $" #include "zfcp_def.h" @@ -46,7 +47,6 @@ extern void zfcp_sysfs_port_remove_files(struct device *, u32); extern int zfcp_sysfs_unit_create_files(struct device *); extern void zfcp_sysfs_unit_remove_files(struct device *); extern void zfcp_sysfs_port_release(struct device *); -extern int zfcp_sysfs_port_shutdown(struct zfcp_port *); extern void zfcp_sysfs_unit_release(struct device *); /**************************** CONFIGURATION *********************************/ @@ -65,7 +65,6 @@ extern void zfcp_unit_dequeue(struct zfcp_unit *); extern int zfcp_ccw_register(void); extern void zfcp_ccw_unregister(void); -extern int zfcp_initialize_with_0copy(struct zfcp_adapter *); extern void zfcp_qdio_zero_sbals(struct qdio_buffer **, int, int); extern int zfcp_qdio_allocate(struct zfcp_adapter *); extern int zfcp_qdio_allocate_queues(struct zfcp_adapter *); @@ -74,6 +73,16 @@ extern int zfcp_qdio_determine_pci(struct zfcp_qdio_queue *, struct zfcp_fsf_req *); extern int zfcp_qdio_reqid_check(struct zfcp_adapter *, void *); +extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req + (struct zfcp_fsf_req *, int, int); +extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr + (struct zfcp_fsf_req *); +extern int zfcp_qdio_sbals_from_sg + (struct zfcp_fsf_req *, unsigned long, struct scatterlist *, int, int); +extern int zfcp_qdio_sbals_from_scsicmnd + (struct zfcp_fsf_req *, unsigned long, struct scsi_cmnd *); + + /******************************** FSF ****************************************/ extern int zfcp_fsf_open_port(struct zfcp_erp_action *); extern int zfcp_fsf_close_port(struct zfcp_erp_action *); @@ -83,17 +92,20 @@ extern int zfcp_fsf_open_unit(struct zfcp_erp_action *); extern int zfcp_fsf_close_unit(struct zfcp_erp_action *); extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *); +extern int zfcp_fsf_control_file(struct zfcp_adapter *, struct zfcp_fsf_req **, + u32, u32, struct zfcp_sg_list *); extern void zfcp_fsf_scsi_er_timeout_handler(unsigned long); extern int zfcp_fsf_req_dismiss_all(struct zfcp_adapter *); extern int zfcp_fsf_status_read(struct zfcp_adapter *, int); -extern int zfcp_fsf_req_create(struct zfcp_adapter *,u32, unsigned long *, - int, struct zfcp_fsf_req **); -extern void zfcp_fsf_req_free(struct zfcp_fsf_req *); -extern int zfcp_fsf_send_generic(struct zfcp_fsf_req *, unsigned char, - unsigned long *, struct timer_list *); +extern int zfcp_fsf_req_create(struct zfcp_adapter *, u32, int, mempool_t *, + unsigned long *, struct zfcp_fsf_req **); +extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *, + struct zfcp_erp_action *); +extern int zfcp_fsf_send_els(struct zfcp_send_els *); extern int zfcp_fsf_req_wait_and_cleanup(struct zfcp_fsf_req *, int, u32 *); extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *, - struct zfcp_unit *, Scsi_Cmnd *, + struct zfcp_unit *, + struct scsi_cmnd *, int); extern int zfcp_fsf_req_complete(struct zfcp_fsf_req *); extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *); @@ -105,15 +117,11 @@ extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command( /******************************** FCP ****************************************/ extern int zfcp_nameserver_enqueue(struct zfcp_adapter *); -extern int zfcp_nameserver_request(struct zfcp_erp_action *); -extern void zfcp_fsf_els_processing(struct zfcp_fsf_req *); +extern int zfcp_ns_gid_pn_request(struct zfcp_erp_action *); /******************************* SCSI ****************************************/ extern int zfcp_adapter_scsi_register(struct zfcp_adapter *); extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *); -extern void zfcp_scsi_block_requests(struct Scsi_Host *); -extern int zfcp_create_sbals_from_sg(struct zfcp_fsf_req *, - Scsi_Cmnd *, char, int, int); extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t); extern char *zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *); extern void set_host_byte(u32 *, char); @@ -122,6 +130,11 @@ extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *); extern void zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *); extern fcp_dl_t zfcp_get_fcp_dl(struct fcp_cmnd_iu *); +extern int zfcp_scsi_command_async(struct zfcp_adapter *,struct zfcp_unit *unit, + struct scsi_cmnd *scsi_cmnd); +extern int zfcp_scsi_command_sync(struct zfcp_unit *unit, + struct scsi_cmnd *scsi_cmnd); + /******************************** ERP ****************************************/ extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u32, int); extern int zfcp_erp_adapter_reopen(struct zfcp_adapter *, int); @@ -147,10 +160,12 @@ extern int zfcp_erp_thread_kill(struct zfcp_adapter *); extern int zfcp_erp_wait(struct zfcp_adapter *); extern void zfcp_erp_fsf_req_handler(struct zfcp_fsf_req *); +extern int zfcp_test_link(struct zfcp_port *); + /******************************** AUX ****************************************/ extern void zfcp_cmd_dbf_event_fsf(const char *, struct zfcp_fsf_req *, void *, int); -extern void zfcp_cmd_dbf_event_scsi(const char *, Scsi_Cmnd *); +extern void zfcp_cmd_dbf_event_scsi(const char *, struct scsi_cmnd *); extern void zfcp_in_els_dbf_event(struct zfcp_adapter *, const char *, struct fsf_status_read_buffer *, int); #ifdef ZFCP_STAT_REQSIZES diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 968e265b5721..12b7176b888f 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -4,11 +4,12 @@ * * FCP adapter driver for IBM eServer zSeries * - * Copyright 2002 IBM Corporation + * (C) Copyright IBM Corp. 2002, 2004 + * * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> - * Aron Zeh <arzeh@de.ibm.com> - * Wolfgang Taphorn <taphorn@de.ibm.com> + * Aron Zeh + * Wolfgang Taphorn * Stefan Bader <stefan.bader@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> * @@ -28,7 +29,7 @@ */ /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_FSF_C_REVISION "$Revision: 1.16 $" +#define ZFCP_FSF_C_REVISION "$Revision: 1.29 $" #include "zfcp_ext.h" @@ -43,18 +44,22 @@ static int zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *); static int zfcp_fsf_send_fcp_command_task_management_handler( struct zfcp_fsf_req *); static int zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *); -static int zfcp_fsf_send_generic_handler(struct zfcp_fsf_req *); static int zfcp_fsf_status_read_handler(struct zfcp_fsf_req *); +static int zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *); +static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *); +static int zfcp_fsf_control_file_handler(struct zfcp_fsf_req *); static inline int zfcp_fsf_req_create_sbal_check( unsigned long *, struct zfcp_qdio_queue *, int); -static struct zfcp_fsf_req *zfcp_fsf_req_get(int, mempool_t *); -static struct zfcp_fsf_req *zfcp_fsf_req_alloc(struct zfcp_adapter *, u32, int); +static inline int zfcp_use_one_sbal( + struct scatterlist *, int, struct scatterlist *, int); +static struct zfcp_fsf_req *zfcp_fsf_req_alloc(mempool_t *, int); static int zfcp_fsf_req_send(struct zfcp_fsf_req *, struct timer_list *); static int zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *); static int zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *); static int zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *); static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *); static void zfcp_fsf_req_dismiss(struct zfcp_fsf_req *); +static void zfcp_fsf_req_free(struct zfcp_fsf_req *); /* association between FSF command and FSF QTCB type */ static u32 fsf_qtcb_type[] = { @@ -67,7 +72,10 @@ static u32 fsf_qtcb_type[] = { [FSF_QTCB_CLOSE_PHYSICAL_PORT] = FSF_SUPPORT_COMMAND, [FSF_QTCB_SEND_ELS] = FSF_SUPPORT_COMMAND, [FSF_QTCB_SEND_GENERIC] = FSF_SUPPORT_COMMAND, - [FSF_QTCB_EXCHANGE_CONFIG_DATA] = FSF_CONFIG_COMMAND + [FSF_QTCB_EXCHANGE_CONFIG_DATA] = FSF_CONFIG_COMMAND, + [FSF_QTCB_EXCHANGE_PORT_DATA] = FSF_PORT_COMMAND, + [FSF_QTCB_DOWNLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND, + [FSF_QTCB_UPLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND }; /****************************************************************/ @@ -75,7 +83,6 @@ static u32 fsf_qtcb_type[] = { /****************************************************************/ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_FSF -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_FSF /* * function: zfcp_fsf_req_alloc @@ -91,93 +98,38 @@ static u32 fsf_qtcb_type[] = { * */ static struct zfcp_fsf_req * -zfcp_fsf_req_alloc(struct zfcp_adapter *adapter, u32 fsf_cmd, int kmalloc_flags) +zfcp_fsf_req_alloc(mempool_t *pool, int req_flags) { + size_t size; + void *ptr; struct zfcp_fsf_req *fsf_req = NULL; - switch (fsf_cmd) { + if (req_flags & ZFCP_REQ_NO_QTCB) + size = sizeof(struct zfcp_fsf_req); + else + size = sizeof(struct zfcp_fsf_req_pool_element); - case FSF_QTCB_FCP_CMND: - case FSF_QTCB_ABORT_FCP_CMND: - fsf_req = zfcp_fsf_req_get(kmalloc_flags, - adapter->pool.fcp_command_fsf); - if (unlikely(fsf_req && - (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL))) { - /* - * watch low mem buffer - * Note: If the command is reset or aborted, two - * timeouts (this and the SCSI ER one) will be started - * for the command. There is no problem however as - * the first expired timer will call adapter_reopen - * which will delete the other - */ - adapter->pool.fcp_command_fsf_timer.expires = - jiffies + ZFCP_ERP_SCSI_LOW_MEM_TIMEOUT; - add_timer(&adapter->pool.fcp_command_fsf_timer); - } -#ifdef ZFCP_DEBUG_REQUESTS - debug_text_event(adapter->req_dbf, 5, "fsfa_fcp"); - if (unlikely(fsf_req && - (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL))) - debug_text_event(adapter->req_dbf, 5, "fsfa_pl"); -#endif /* ZFCP_DEBUG_REQUESTS */ - break; + if (likely(pool != NULL)) + ptr = mempool_alloc(pool, GFP_ATOMIC); + else + ptr = kmalloc(size, GFP_ATOMIC); - case FSF_QTCB_OPEN_PORT_WITH_DID: - case FSF_QTCB_OPEN_LUN: - case FSF_QTCB_CLOSE_LUN: - case FSF_QTCB_CLOSE_PORT: - case FSF_QTCB_CLOSE_PHYSICAL_PORT: - case FSF_QTCB_SEND_ELS: - case FSF_QTCB_EXCHANGE_CONFIG_DATA: - case FSF_QTCB_SEND_GENERIC: - fsf_req = - zfcp_fsf_req_get(kmalloc_flags, adapter->pool.erp_fsf); -#ifdef ZFCP_DEBUG_REQUESTS - debug_text_event(adapter->req_dbf, 5, "fsfa_erp"); - if (fsf_req && (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL)) - debug_text_event(adapter->req_dbf, 5, "fsfa_pl"); -#endif /* ZFCP_DEBUG_REQUESTS */ - break; + if (unlikely(NULL == ptr)) + goto out; - case FSF_QTCB_UNSOLICITED_STATUS: - fsf_req = - mempool_alloc(adapter->pool.status_read_fsf, GFP_ATOMIC); - if (fsf_req) { - memset(fsf_req, 0, sizeof (struct zfcp_fsf_req)); - fsf_req->status |= ZFCP_STATUS_FSFREQ_POOL; - } else - ZFCP_LOG_NORMAL("bug: could not find free fsf_req\n"); -#ifdef ZFCP_DEBUG_REQUESTS - debug_text_event(adapter->req_dbf, 5, "fsfa_sr"); - debug_text_event(adapter->req_dbf, 5, "fsfa_pl"); -#endif /* ZFCP_DEBUG_REQUESTS */ - break; + memset(ptr, 0, size); - default: - ZFCP_LOG_NORMAL("bug: An attempt to send an unsupported " - "command has been detected. " - "(debug info 0x%x)\n", fsf_cmd); - } //switch(fsf_cmd) - - if (unlikely(!fsf_req)) { - ZFCP_LOG_DEBUG("error: Out of memory. Allocation of FSF " - "request structure failed\n"); + if (req_flags & ZFCP_REQ_NO_QTCB) { + fsf_req = (struct zfcp_fsf_req *) ptr; } else { - ZFCP_LOG_TRACE("FSF request allocated at 0x%lx, " - "adapter 0x%lx (%s)\n", - (unsigned long) fsf_req, - (unsigned long) adapter, - zfcp_get_busid_by_adapter(adapter)); + fsf_req = &((struct zfcp_fsf_req_pool_element *) ptr)->fsf_req; + fsf_req->qtcb = + &((struct zfcp_fsf_req_pool_element *) ptr)->qtcb; } -#ifdef ZFCP_DEBUG_REQUESTS - debug_event(adapter->req_dbf, 5, &fsf_req, sizeof (unsigned long)); - if (likely(fsf_req->qtcb)) - debug_event(adapter->req_dbf, 5, &fsf_req->qtcb, - sizeof (unsigned long)); -#endif /* ZFCP_DEBUG_REQUESTS */ + fsf_req->pool = pool; + out: return fsf_req; } @@ -191,40 +143,13 @@ zfcp_fsf_req_alloc(struct zfcp_adapter *adapter, u32 fsf_cmd, int kmalloc_flags) * * locks: none */ -void +static void zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req) { - struct zfcp_adapter *adapter = fsf_req->adapter; - - switch (fsf_req->fsf_command) { - - case FSF_QTCB_FCP_CMND: - case FSF_QTCB_ABORT_FCP_CMND: - if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_POOL)) { - del_timer(&adapter->pool.fcp_command_fsf_timer); - mempool_free(fsf_req, adapter->pool.fcp_command_fsf); - } else - kfree(fsf_req); - break; - - case FSF_QTCB_OPEN_PORT_WITH_DID: - case FSF_QTCB_OPEN_LUN: - case FSF_QTCB_CLOSE_LUN: - case FSF_QTCB_CLOSE_PORT: - case FSF_QTCB_CLOSE_PHYSICAL_PORT: - case FSF_QTCB_SEND_ELS: - case FSF_QTCB_EXCHANGE_CONFIG_DATA: - case FSF_QTCB_SEND_GENERIC: - if (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL) - mempool_free(fsf_req, adapter->pool.erp_fsf); + if (likely(fsf_req->pool != NULL)) + mempool_free(fsf_req, fsf_req->pool); else kfree(fsf_req); - break; - - case FSF_QTCB_UNSOLICITED_STATUS: - mempool_free(fsf_req, adapter->pool.status_read_fsf); - break; - } } /* @@ -386,23 +311,24 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) /* log additional information provided by FSF (if any) */ if (unlikely(fsf_req->qtcb->header.log_length)) { /* do not trust them ;-) */ - if (fsf_req->qtcb->header.log_start > ZFCP_QTCB_SIZE) { + if (fsf_req->qtcb->header.log_start > sizeof(struct fsf_qtcb)) { ZFCP_LOG_NORMAL ("bug: ULP (FSF logging) log data starts " "beyond end of packet header. Ignored. " "(start=%i, size=%li)\n", - fsf_req->qtcb->header.log_start, ZFCP_QTCB_SIZE); + fsf_req->qtcb->header.log_start, + sizeof(struct fsf_qtcb)); goto forget_log; } if ((fsf_req->qtcb->header.log_start + fsf_req->qtcb->header.log_length) - > ZFCP_QTCB_SIZE) { + > sizeof(struct fsf_qtcb)) { ZFCP_LOG_NORMAL("bug: ULP (FSF logging) log data ends " "beyond end of packet header. Ignored. " "(start=%i, length=%i, size=%li)\n", fsf_req->qtcb->header.log_start, fsf_req->qtcb->header.log_length, - ZFCP_QTCB_SIZE); + sizeof(struct fsf_qtcb)); goto forget_log; } ZFCP_LOG_TRACE("ULP log data: \n"); @@ -667,7 +593,7 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) (char *) (((unsigned long) fsf_req) & 0xFFFFFF00), sizeof (struct zfcp_fsf_req)); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, (char *) fsf_req->qtcb, - ZFCP_QTCB_SIZE); + sizeof(struct fsf_qtcb)); debug_text_event(adapter->erp_dbf, 0, "prot_inval:"); debug_exception(adapter->erp_dbf, 0, &fsf_req->qtcb->prefix.prot_status, @@ -852,7 +778,7 @@ zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req) (unsigned long) fsf_req, (unsigned long) (fsf_req->qtcb)); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, - (char *) fsf_req->qtcb, ZFCP_QTCB_SIZE); + (char *) fsf_req->qtcb, sizeof(struct fsf_qtcb)); } switch (fsf_req->fsf_command) { @@ -869,44 +795,52 @@ zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req) case FSF_QTCB_SEND_GENERIC: ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_GENERIC\n"); - zfcp_fsf_send_generic_handler(fsf_req); - zfcp_erp_fsf_req_handler(fsf_req); + zfcp_fsf_send_ct_handler(fsf_req); break; case FSF_QTCB_OPEN_PORT_WITH_DID: ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_PORT_WITH_DID\n"); zfcp_fsf_open_port_handler(fsf_req); - zfcp_erp_fsf_req_handler(fsf_req); break; case FSF_QTCB_OPEN_LUN: ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_LUN\n"); zfcp_fsf_open_unit_handler(fsf_req); - zfcp_erp_fsf_req_handler(fsf_req); break; case FSF_QTCB_CLOSE_LUN: ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_LUN\n"); zfcp_fsf_close_unit_handler(fsf_req); - zfcp_erp_fsf_req_handler(fsf_req); break; case FSF_QTCB_CLOSE_PORT: ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PORT\n"); zfcp_fsf_close_port_handler(fsf_req); - zfcp_erp_fsf_req_handler(fsf_req); break; case FSF_QTCB_CLOSE_PHYSICAL_PORT: ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PHYSICAL_PORT\n"); zfcp_fsf_close_physical_port_handler(fsf_req); - zfcp_erp_fsf_req_handler(fsf_req); break; case FSF_QTCB_EXCHANGE_CONFIG_DATA: ZFCP_LOG_FLAGS(2, "FSF_QTCB_EXCHANGE_CONFIG_DATA\n"); zfcp_fsf_exchange_config_data_handler(fsf_req); - zfcp_erp_fsf_req_handler(fsf_req); + break; + + case FSF_QTCB_SEND_ELS : + ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_ELS\n"); + zfcp_fsf_send_els_handler(fsf_req); + break; + + case FSF_QTCB_DOWNLOAD_CONTROL_FILE: + ZFCP_LOG_FLAGS(2, "FSF_QTCB_DOWNLOAD_CONTROL_FILE\n"); + zfcp_fsf_control_file_handler(fsf_req); + break; + + case FSF_QTCB_UPLOAD_CONTROL_FILE: + ZFCP_LOG_FLAGS(2, "FSF_QTCB_UPLOAD_CONTROL_FILE\n"); + zfcp_fsf_control_file_handler(fsf_req); break; default: @@ -927,6 +861,7 @@ zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req) fsf_req->qtcb->header.fsf_command); } + zfcp_erp_fsf_req_handler(fsf_req); return retval; } @@ -943,14 +878,14 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags) struct zfcp_fsf_req *fsf_req; struct fsf_status_read_buffer *status_buffer; unsigned long lock_flags; - volatile struct qdio_buffer_element *buffere; - struct zfcp_qdio_queue *req_queue = &adapter->request_queue; + volatile struct qdio_buffer_element *sbale; int retval = 0; /* setup new FSF request */ - retval = zfcp_fsf_req_create(adapter, - FSF_QTCB_UNSOLICITED_STATUS, - &lock_flags, req_flags, &fsf_req); + retval = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS, + req_flags | ZFCP_REQ_NO_QTCB, + adapter->pool.fsf_req_status_read, + &lock_flags, &fsf_req); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " "unsolicited status buffer for " @@ -959,8 +894,13 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags) goto failed_req_create; } + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS; + sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY; + fsf_req->sbale_curr = 2; + status_buffer = - mempool_alloc(adapter->pool.status_read_buf, GFP_ATOMIC); + mempool_alloc(adapter->pool.data_status_read, GFP_ATOMIC); if (!status_buffer) { ZFCP_LOG_NORMAL("bug: could not get some buffer\n"); goto failed_buf; @@ -969,9 +909,9 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags) fsf_req->data.status_read.buffer = status_buffer; /* insert pointer to respective buffer */ - buffere = req_queue->buffer[fsf_req->sbal_index]->element; - buffere[2].addr = (void *) status_buffer; - buffere[2].length = sizeof (struct fsf_status_read_buffer); + sbale = zfcp_qdio_sbale_curr(fsf_req); + sbale->addr = (void *) status_buffer; + sbale->length = sizeof(struct fsf_status_read_buffer); /* start QDIO request for this FSF request */ retval = zfcp_fsf_req_send(fsf_req, NULL); @@ -990,7 +930,7 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags) goto out; failed_req_send: - mempool_free(status_buffer, adapter->pool.status_read_buf); + mempool_free(status_buffer, adapter->pool.data_status_read); failed_buf: zfcp_fsf_req_free(fsf_req); @@ -1072,7 +1012,7 @@ zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req) fsf_req->data.status_read.buffer; if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { - mempool_free(status_buffer, adapter->pool.status_read_buf); + mempool_free(status_buffer, adapter->pool.data_status_read); zfcp_fsf_req_cleanup(fsf_req); goto out; } @@ -1104,6 +1044,7 @@ zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req) case FSF_STATUS_READ_LINK_DOWN: ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_LINK_DOWN\n"); + /* Unneccessary, ignoring.... */ break; @@ -1121,6 +1062,59 @@ zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | ZFCP_STATUS_COMMON_ERP_FAILED); + + break; + + case FSF_STATUS_READ_NOTIFICATION_LOST: + ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_NOTIFICATION_LOST\n"); + debug_text_event(adapter->erp_dbf, 2, "unsol_not_lost:"); + switch (status_buffer->status_subtype) { + case FSF_STATUS_READ_SUB_LOST_CFDC_UPDATED: + ZFCP_LOG_NORMAL( + "The unsolicited status information about " + "CFDC update on the adapter %s is lost " + "due to the lack of internal resources\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_STATUS_READ_SUB_LOST_CFDC_HARDENED: + ZFCP_LOG_NORMAL( + "The unsolicited status information about " + "CFDC harden on the adapter %s is lost " + "due to the lack of internal resources\n", + zfcp_get_busid_by_adapter(adapter)); + break; + } + break; + + case FSF_STATUS_READ_CFDC_UPDATED: + ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_UPDATED\n"); + debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_update:"); + ZFCP_LOG_NORMAL( + "CFDC has been updated on the adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + break; + + case FSF_STATUS_READ_CFDC_HARDENED: + ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_HARDENED\n"); + debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_harden:"); + switch (status_buffer->status_subtype) { + case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE: + ZFCP_LOG_NORMAL( + "CFDC of the adapter %s " + "has been saved on the SE\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2: + ZFCP_LOG_NORMAL( + "CFDC of the adapter %s " + "has been copied to the secondary SE\n", + zfcp_get_busid_by_adapter(adapter)); + break; + default: + ZFCP_LOG_NORMAL( + "CFDC of the adapter %s has been hardened\n", + zfcp_get_busid_by_adapter(adapter)); + } break; default: @@ -1138,7 +1132,7 @@ zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req) sizeof (struct fsf_status_read_buffer)); break; } - mempool_free(status_buffer, adapter->pool.status_read_buf); + mempool_free(status_buffer, adapter->pool.data_status_read); zfcp_fsf_req_cleanup(fsf_req); /* * recycle buffer and start new request repeat until outbound @@ -1192,13 +1186,15 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id, struct zfcp_adapter *adapter, struct zfcp_unit *unit, int req_flags) { - struct zfcp_fsf_req *new_fsf_req = NULL; - int retval = 0; + volatile struct qdio_buffer_element *sbale; unsigned long lock_flags; + struct zfcp_fsf_req *fsf_req = NULL; + int retval = 0; /* setup new FSF request */ retval = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND, - &lock_flags, req_flags, &new_fsf_req); + req_flags, adapter->pool.fsf_req_abort, + &lock_flags, &fsf_req); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " "abort command request on the device with " @@ -1211,19 +1207,23 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id, goto out; } - new_fsf_req->data.abort_fcp_command.unit = unit; + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + + fsf_req->data.abort_fcp_command.unit = unit; /* set handles of unit and its parent port in QTCB */ - new_fsf_req->qtcb->header.lun_handle = unit->handle; - new_fsf_req->qtcb->header.port_handle = unit->port->handle; + fsf_req->qtcb->header.lun_handle = unit->handle; + fsf_req->qtcb->header.port_handle = unit->port->handle; /* set handle of request which should be aborted */ - new_fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id; + fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id; /* start QDIO request for this FSF request */ zfcp_fsf_start_scsi_er_timer(adapter); - retval = zfcp_fsf_req_send(new_fsf_req, NULL); + retval = zfcp_fsf_req_send(fsf_req, NULL); if (retval) { del_timer(&adapter->scsi_er_timer); ZFCP_LOG_INFO("error: Could not send an abort command request " @@ -1231,8 +1231,8 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id, "port WWPN 0x%Lx and unit LUN 0x%Lx\n", zfcp_get_busid_by_adapter(adapter), unit->port->wwpn, unit->fcp_lun); - zfcp_fsf_req_free(new_fsf_req); - new_fsf_req = NULL; + zfcp_fsf_req_free(fsf_req); + fsf_req = NULL; goto out; } @@ -1244,7 +1244,7 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id, unit->fcp_lun, old_req_id); out: write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); - return new_fsf_req; + return fsf_req; } /* @@ -1429,73 +1429,183 @@ zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req) return retval; } -/* - * function: zfcp_fsf_send_generic - * - * purpose: sends a FC request according to FC-GS-3 - * - * returns: address of initiated FSF request - * NULL - request could not be initiated +/** + * zfcp_use_one_sbal - checks whether req buffer and resp bother each fit into + * one SBALE + * Two scatter-gather lists are passed, one for the reqeust and one for the + * response. + */ +static inline int +zfcp_use_one_sbal(struct scatterlist *req, int req_count, + struct scatterlist *resp, int resp_count) +{ + return ((req_count == 1) && + (resp_count == 1) && + (((unsigned long) zfcp_sg_to_address(&req[0]) & + PAGE_MASK) == + ((unsigned long) (zfcp_sg_to_address(&req[0]) + + req[0].length - 1) & PAGE_MASK)) && + (((unsigned long) zfcp_sg_to_address(&resp[0]) & + PAGE_MASK) == + ((unsigned long) (zfcp_sg_to_address(&resp[0]) + + resp[0].length - 1) & PAGE_MASK))); +} + +/** + * zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS) + * @ct: pointer to struct zfcp_send_ct which conatins all needed data for + * the request + * @pool: pointer to memory pool, if non-null this pool is used to allocate + * a struct zfcp_fsf_req + * @erp_action: pointer to erp_action, if non-null the Generic Service request + * is sent within error recovery */ int -zfcp_fsf_send_generic(struct zfcp_fsf_req *fsf_req, unsigned char timeout, - unsigned long *lock_flags, struct timer_list *timer) +zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool, + struct zfcp_erp_action *erp_action) { - int retval = 0; - struct qdio_buffer *buffer; - volatile struct qdio_buffer_element *buffer_element = NULL; - struct zfcp_port *port = fsf_req->data.send_generic.port; - struct zfcp_adapter *adapter = port->adapter; - - /* put buffers to the 2 SBALEs after the QTCB */ - buffer = (adapter->request_queue.buffer[fsf_req->sbal_index]); - buffer_element = &(buffer->element[2]); - buffer_element->addr = fsf_req->data.send_generic.outbuf; - buffer_element->length = fsf_req->data.send_generic.outbuf_length; - buffer_element++; - buffer_element->addr = fsf_req->data.send_generic.inbuf; - buffer_element->length = fsf_req->data.send_generic.inbuf_length; - buffer_element->flags |= SBAL_FLAGS_LAST_ENTRY; + volatile struct qdio_buffer_element *sbale; + struct zfcp_port *port; + struct zfcp_adapter *adapter; + struct zfcp_fsf_req *fsf_req; + unsigned long lock_flags; + int bytes; + int ret = 0; + + port = ct->port; + adapter = port->adapter; + + ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC, + ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, + pool, &lock_flags, &fsf_req); + if (ret < 0) { + ZFCP_LOG_INFO("error: out of memory. Could not create CT " + "request (FC-GS). (adapter: %s)\n", + zfcp_get_busid_by_adapter(adapter)); + goto failed_req; + } + + if (erp_action != NULL) { + erp_action->fsf_req = fsf_req; + fsf_req->erp_action = erp_action; + } + + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + if (zfcp_use_one_sbal(ct->req, ct->req_count, + ct->resp, ct->resp_count)){ + /* both request buffer and response buffer + fit into one sbale each */ + sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ; + sbale[2].addr = zfcp_sg_to_address(&ct->req[0]); + sbale[2].length = ct->req[0].length; + sbale[3].addr = zfcp_sg_to_address(&ct->resp[0]); + sbale[3].length = ct->resp[0].length; + sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY; + } else if (adapter->supported_features & + FSF_FEATURE_ELS_CT_CHAINED_SBALS) { + /* try to use chained SBALs */ + bytes = zfcp_qdio_sbals_from_sg(fsf_req, + SBAL_FLAGS0_TYPE_WRITE_READ, + ct->req, ct->req_count, + ZFCP_MAX_SBALS_PER_CT_REQ); + if (bytes <= 0) { + ZFCP_LOG_INFO("error: out of resources (outbuf). " + "Could not create CT request (FC-GS). " + "(adapter: %s)\n", + zfcp_get_busid_by_adapter(adapter)); + if (bytes == 0) + ret = -ENOMEM; + else + ret = bytes; + + goto failed_send; + } + fsf_req->qtcb->bottom.support.req_buf_length = bytes; + fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL; + bytes = zfcp_qdio_sbals_from_sg(fsf_req, + SBAL_FLAGS0_TYPE_WRITE_READ, + ct->resp, ct->resp_count, + ZFCP_MAX_SBALS_PER_CT_REQ); + if (bytes <= 0) { + ZFCP_LOG_INFO("error: out of resources (inbuf). " + "Could not create a CT request (FC-GS). " + "(adapter: %s)\n", + zfcp_get_busid_by_adapter(adapter)); + if (bytes == 0) + ret = -ENOMEM; + else + ret = bytes; + + goto failed_send; + } + fsf_req->qtcb->bottom.support.resp_buf_length = bytes; + } else { + /* reject send generic request */ + ZFCP_LOG_INFO( + "error: microcode does not support chained SBALs." + "CT request (FC-GS) too big. (adapter: %s)\n", + zfcp_get_busid_by_adapter(adapter)); + ret = -EOPNOTSUPP; + goto failed_send; + } /* settings in QTCB */ fsf_req->qtcb->header.port_handle = port->handle; fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class; - fsf_req->qtcb->bottom.support.timeout = timeout; + fsf_req->qtcb->bottom.support.timeout = ct->timeout; + fsf_req->data.send_ct = ct; /* start QDIO request for this FSF request */ - retval = zfcp_fsf_req_send(fsf_req, timer); - if (retval) { - ZFCP_LOG_DEBUG("error: Out of resources. could not send a " - "generic services " - "command via the adapter %s, port " - "WWPN 0x%Lx\n", + ret = zfcp_fsf_req_send(fsf_req, ct->timer); + if (ret) { + ZFCP_LOG_DEBUG("error: out of resources. Could not send CT " + "request (FC-GS). (adapter: %s, " + "port WWPN 0x%Lx)\n", zfcp_get_busid_by_adapter(adapter), port->wwpn); - /* - * fsf_req structure will be cleaned up by higher layer handler - */ - goto out; + goto failed_send; } - ZFCP_LOG_DEBUG("Send Generic request initiated " - "(adapter busido=%s, port d_id=0x%x)\n", - zfcp_get_busid_by_adapter(adapter), - (unsigned int) port->d_id); + ZFCP_LOG_DEBUG("CT request initiated. (adapter: %s, port WWPN 0x%Lx)\n", + zfcp_get_busid_by_adapter(adapter), port->wwpn); + goto out; + + failed_send: + zfcp_fsf_req_free(fsf_req); + if (erp_action != NULL) { + erp_action->fsf_req = NULL; + } + failed_req: out: - return retval; + write_unlock_irqrestore(&adapter->request_queue.queue_lock, + lock_flags); + return ret; } -/* - * function: zfcp_fsf_send_generic_handler - * - * purpose: is called for finished Send Generic request +/** + * zfcp_fsf_send_ct_handler - handler for Generic Service requests + * @fsf_req: pointer to struct zfcp_fsf_req * - * returns: + * Data specific for the Generic Service request is passed by + * fsf_req->data.send_ct + * Usually a specific handler for the request is called via + * fsf_req->data.send_ct->handler at end of this function. */ static int -zfcp_fsf_send_generic_handler(struct zfcp_fsf_req *fsf_req) +zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) { + struct zfcp_port *port; + struct zfcp_adapter *adapter; + struct zfcp_send_ct *send_ct; + struct fsf_qtcb_header *header; + struct fsf_qtcb_bottom_support *bottom; int retval = -EINVAL; - struct zfcp_port *port = fsf_req->data.send_generic.port; + u16 subtable, rule, counter; + + adapter = fsf_req->adapter; + send_ct = fsf_req->data.send_ct; + port = send_ct->port; + header = &fsf_req->qtcb->header; + bottom = &fsf_req->qtcb->bottom.support; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { /* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */ @@ -1503,141 +1613,468 @@ zfcp_fsf_send_generic_handler(struct zfcp_fsf_req *fsf_req) } /* evaluate FSF status in QTCB */ - switch (fsf_req->qtcb->header.fsf_status) { + switch (header->fsf_status) { - case FSF_PORT_HANDLE_NOT_VALID: - ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n"); - ZFCP_LOG_DEBUG("Temporary port identifier (handle) 0x%x " - "for the port with WWPN 0x%Lx connected to " - "the adapter %s is " - "not valid. This may happen occasionally.\n", - port->handle, - port->wwpn, zfcp_get_busid_by_port(port)); - ZFCP_LOG_INFO("status qualifier:\n"); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, - (char *) &fsf_req->qtcb->header.fsf_status_qual, - sizeof (union fsf_status_qual)); - debug_text_event(fsf_req->adapter->erp_dbf, 1, - "fsf_s_phandle_nv"); - zfcp_erp_adapter_reopen(port->adapter, 0); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + case FSF_GOOD : + ZFCP_LOG_FLAGS(2,"FSF_GOOD\n"); + retval = 0; break; - case FSF_SERVICE_CLASS_NOT_SUPPORTED: - ZFCP_LOG_FLAGS(0, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n"); - if (fsf_req->adapter->fc_service_class <= 3) { - ZFCP_LOG_NORMAL("error: The adapter %s does " + case FSF_SERVICE_CLASS_NOT_SUPPORTED : + ZFCP_LOG_FLAGS(2, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n"); + if (adapter->fc_service_class <= 3) { + ZFCP_LOG_INFO("error: The adapter %s does " "not support fibre-channel class %d.\n", zfcp_get_busid_by_port(port), - fsf_req->adapter->fc_service_class); + adapter->fc_service_class); } else { - ZFCP_LOG_NORMAL - ("bug: The fibre channel class at the adapter " - "%s is invalid. " "(debug info %d)\n", + ZFCP_LOG_INFO("bug: The fibre channel class at the " + "adapter %s is invalid. " + "(debug info %d)\n", zfcp_get_busid_by_port(port), - fsf_req->adapter->fc_service_class); + adapter->fc_service_class); } /* stop operation for this adapter */ - debug_text_exception(fsf_req->adapter->erp_dbf, 0, - "fsf_s_class_nsup"); + debug_text_exception(adapter->erp_dbf, 0, "fsf_s_class_nsup"); zfcp_erp_adapter_shutdown(port->adapter, 0); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_GENERIC_COMMAND_REJECTED: - ZFCP_LOG_FLAGS(1, "FSF_GENERIC_COMMAND_REJECTED\n"); + case FSF_ADAPTER_STATUS_AVAILABLE : + ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n"); + switch (header->fsf_status_qual.word[0]){ + case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE : + ZFCP_LOG_FLAGS(2,"FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n"); + /* reopening link to port */ + debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ltest"); + zfcp_test_link(port); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED : + ZFCP_LOG_FLAGS(2,"FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n"); + /* ERP strategy will escalate */ + debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ulp"); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + default: + ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x " + "arrived.\n", + header->fsf_status_qual.word[0]); + break; + } + break; + + case FSF_ACCESS_DENIED: + ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n"); + ZFCP_LOG_NORMAL("Access denied, cannot send generic command " + "to a port with WWPN 0x%Lx connected " + "to the adapter %s\n", port->wwpn, + zfcp_get_busid_by_port(port)); + counter = 0; + do { + subtable = header->fsf_status_qual.halfword[counter++]; + rule = header->fsf_status_qual.halfword[counter++]; + switch (subtable) { + case FSF_SQ_CFDC_SUBTABLE_OS: + case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: + case FSF_SQ_CFDC_SUBTABLE_PORT_DID: + case FSF_SQ_CFDC_SUBTABLE_LUN: + ZFCP_LOG_NORMAL("Access denied (%s rule %d)\n", + zfcp_act_subtable_type[subtable], rule); + break; + } + } while (counter < 4); + debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access"); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + + case FSF_GENERIC_COMMAND_REJECTED : + ZFCP_LOG_FLAGS(2, "FSF_GENERIC_COMMAND_REJECTED\n"); ZFCP_LOG_INFO("warning: The port with WWPN 0x%Lx connected to " - "the adapter %s is" + "the adapter %s has " "rejected a generic services command.\n", port->wwpn, zfcp_get_busid_by_port(port)); ZFCP_LOG_INFO("status qualifier:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, - (char *) &fsf_req->qtcb->header.fsf_status_qual, + (char *) &header->fsf_status_qual, sizeof (union fsf_status_qual)); - debug_text_event(fsf_req->adapter->erp_dbf, 1, - "fsf_s_gcom_rej"); + debug_text_event(adapter->erp_dbf, 1, "fsf_s_gcom_rej"); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_REQUEST_BUF_NOT_VALID: - ZFCP_LOG_FLAGS(1, "FSF_REQUEST_BUF_NOT_VALID\n"); + case FSF_PORT_HANDLE_NOT_VALID : + ZFCP_LOG_FLAGS(2, "FSF_PORT_HANDLE_NOT_VALID\n"); + ZFCP_LOG_DEBUG("Temporary port identifier (handle) 0x%x " + "for the port with WWPN 0x%Lx connected to " + "the adapter %s is " + "not valid. This may happen occasionally.\n", + port->handle, + port->wwpn, zfcp_get_busid_by_port(port)); + ZFCP_LOG_INFO("status qualifier:\n"); + ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, + (char *) &header->fsf_status_qual, + sizeof (union fsf_status_qual)); + debug_text_event(adapter->erp_dbf, 1, "fsf_s_phandle_nv"); + zfcp_erp_adapter_reopen(port->adapter, 0); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + + case FSF_REQUEST_BUF_NOT_VALID : + ZFCP_LOG_FLAGS(2, "FSF_REQUEST_BUF_NOT_VALID\n"); ZFCP_LOG_NORMAL("error: The port with WWPN 0x%Lx connected to " - "the adapter %s is" + "the adapter %s has " "rejected a generic services command " "due to invalid request buffer.\n", port->wwpn, zfcp_get_busid_by_port(port)); - debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_reqiv"); + debug_text_event(adapter->erp_dbf, 1, "fsf_s_reqiv"); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_RESPONSE_BUF_NOT_VALID: - ZFCP_LOG_FLAGS(1, "FSF_RESPONSE_BUF_NOT_VALID\n"); + case FSF_RESPONSE_BUF_NOT_VALID : + ZFCP_LOG_FLAGS(2, "FSF_RESPONSE_BUF_NOT_VALID\n"); ZFCP_LOG_NORMAL("error: The port with WWPN 0x%Lx connected to " - "the adapter %s is" + "the adapter %s has " "rejected a generic services command " "due to invalid response buffer.\n", port->wwpn, zfcp_get_busid_by_port(port)); - debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_resiv"); + debug_text_event(adapter->erp_dbf, 1, "fsf_s_resiv"); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_PORT_BOXED: + case FSF_PORT_BOXED : ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n"); ZFCP_LOG_DEBUG("The remote port " "with WWPN 0x%Lx on the adapter %s " "needs to be reopened\n", port->wwpn, zfcp_get_busid_by_port(port)); - debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_pboxed"); + debug_text_event(adapter->erp_dbf, 2, "fsf_s_pboxed"); zfcp_erp_port_reopen(port, 0); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | ZFCP_STATUS_FSFREQ_RETRY; break; + default : + ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " + "(debug info 0x%x)\n", header->fsf_status); + debug_text_event(adapter->erp_dbf, 0, "fsf_sq_inval:"); + debug_exception(adapter->erp_dbf, 0, + &header->fsf_status_qual.word[0], sizeof (u32)); + break; + } + +skip_fsfstatus: + if (send_ct->handler != NULL) { + send_ct->handler(send_ct->handler_data); + } + + return retval; +} + +/** + * zfcp_fsf_send_els - initiate an ELS command (FC-FS) + * @els: pointer to struct zfcp_send_els which contains all needed data for + * the command. + */ +int +zfcp_fsf_send_els(struct zfcp_send_els *els) +{ + volatile struct qdio_buffer_element *sbale; + struct zfcp_fsf_req *fsf_req; + struct zfcp_port *port; + struct zfcp_adapter *adapter; + unsigned long lock_flags; + int bytes; + int ret = 0; + + port = els->port; + adapter = port->adapter; + + ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS, + ZFCP_WAIT_FOR_SBAL|ZFCP_REQ_AUTO_CLEANUP, + NULL, &lock_flags, &fsf_req); + if (ret < 0) { + ZFCP_LOG_INFO("error: out of memory. Could not create ELS " + "request. (adapter: %s, port did: 0x%06x)\n", + zfcp_get_busid_by_adapter(adapter), port->d_id); + goto failed_req; + } + + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + if (zfcp_use_one_sbal(els->req, els->req_count, + els->resp, els->resp_count)){ + /* both request buffer and response buffer + fit into one sbale each */ + sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ; + sbale[2].addr = zfcp_sg_to_address(&els->req[0]); + sbale[2].length = els->req[0].length; + sbale[3].addr = zfcp_sg_to_address(&els->resp[0]); + sbale[3].length = els->resp[0].length; + sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY; + } else if (adapter->supported_features & + FSF_FEATURE_ELS_CT_CHAINED_SBALS) { + /* try to use chained SBALs */ + bytes = zfcp_qdio_sbals_from_sg(fsf_req, + SBAL_FLAGS0_TYPE_WRITE_READ, + els->req, els->req_count, + ZFCP_MAX_SBALS_PER_ELS_REQ); + if (bytes <= 0) { + ZFCP_LOG_INFO("error: out of resources (outbuf). " + "Could not create ELS request. " + "(adapter: %s, port did: 0x%06x)\n", + zfcp_get_busid_by_adapter(adapter), + port->d_id); + if (bytes == 0) { + ret = -ENOMEM; + } else { + ret = bytes; + } + goto failed_send; + } + fsf_req->qtcb->bottom.support.req_buf_length = bytes; + fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL; + bytes = zfcp_qdio_sbals_from_sg(fsf_req, + SBAL_FLAGS0_TYPE_WRITE_READ, + els->resp, els->resp_count, + ZFCP_MAX_SBALS_PER_ELS_REQ); + if (bytes <= 0) { + ZFCP_LOG_INFO("error: out of resources (inbuf). " + "Could not create ELS request. " + "(adapter: %s, port did: 0x%06x)\n", + zfcp_get_busid_by_adapter(adapter), + port->d_id); + if (bytes == 0) { + ret = -ENOMEM; + } else { + ret = bytes; + } + goto failed_send; + } + fsf_req->qtcb->bottom.support.resp_buf_length = bytes; + } else { + /* reject request */ + ZFCP_LOG_INFO("error: microcode does not support chained SBALs." + "ELS request too big. " + "(adapter: %s, port did: 0x%06x)\n", + zfcp_get_busid_by_adapter(adapter), port->d_id); + ret = -EOPNOTSUPP; + goto failed_send; + } + + /* settings in QTCB */ + fsf_req->qtcb->bottom.support.d_id = port->d_id; + fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class; + fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT; + fsf_req->data.send_els = els; + + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + + /* start QDIO request for this FSF request */ + ret = zfcp_fsf_req_send(fsf_req, NULL); + if (ret) { + ZFCP_LOG_DEBUG("error: out of resources. Could not send ELS " + "request. (adapter: %s, port WWPN 0x%Lx)\n", + zfcp_get_busid_by_adapter(adapter), port->wwpn); + goto failed_send; + } + + ZFCP_LOG_DEBUG("ELS request initiated (adapter: %s, port WWPN 0x%Lx)\n", + zfcp_get_busid_by_adapter(adapter), port->wwpn); + goto out; + + failed_send: + zfcp_fsf_req_free(fsf_req); + + failed_req: + out: + write_unlock_irqrestore(&adapter->request_queue.queue_lock, + lock_flags); + + return ret; +} + +/** + * zfcp_fsf_send_els_handler - handler for ELS commands + * @fsf_req: pointer to struct zfcp_fsf_req + * + * Data specific for the ELS command is passed by + * fsf_req->data.send_els + * Usually a specific handler for the command is called via + * fsf_req->data.send_els->handler at end of this function. + */ +static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) +{ + struct zfcp_adapter *adapter; + struct zfcp_port *port; + struct fsf_qtcb_header *header; + struct fsf_qtcb_bottom_support *bottom; + struct zfcp_send_els *send_els; + int retval = -EINVAL; + u16 subtable, rule, counter; + + adapter = fsf_req->adapter; + send_els = fsf_req->data.send_els; + port = send_els->port; + header = &fsf_req->qtcb->header; + bottom = &fsf_req->qtcb->bottom.support; + + if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) + goto skip_fsfstatus; + + switch (header->fsf_status) { + + case FSF_GOOD: + ZFCP_LOG_FLAGS(2, "FSF_GOOD\n"); + retval = 0; + break; + + case FSF_SERVICE_CLASS_NOT_SUPPORTED: + ZFCP_LOG_FLAGS(2, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n"); + if (adapter->fc_service_class <= 3) { + ZFCP_LOG_INFO("error: The adapter %s does " + "not support fibre-channel class %d.\n", + zfcp_get_busid_by_port(port), + adapter->fc_service_class); + } else { + ZFCP_LOG_INFO("bug: The fibre channel class at the " + "adapter %s is invalid. " + "(debug info %d)\n", + zfcp_get_busid_by_port(port), + adapter->fc_service_class); + } + /* stop operation for this adapter */ + debug_text_exception(adapter->erp_dbf, 0, "fsf_s_class_nsup"); + zfcp_erp_adapter_shutdown(port->adapter, 0); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + case FSF_ADAPTER_STATUS_AVAILABLE: ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n"); - switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { - case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: - ZFCP_LOG_FLAGS(2, - "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n"); - /* reopening link to port */ - debug_text_event(fsf_req->adapter->erp_dbf, 1, - "fsf_sq_ltest"); - zfcp_erp_port_forced_reopen(port, 0); + switch (header->fsf_status_qual.word[0]){ + case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: { + ZFCP_LOG_FLAGS(2,"FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n"); + debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ltest"); + if (send_els->ls_code != ZFCP_LS_ADISC) + zfcp_test_link(port); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; + } case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED: + ZFCP_LOG_FLAGS(2,"FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n"); /* ERP strategy will escalate */ - debug_text_event(fsf_req->adapter->erp_dbf, 1, - "fsf_sq_ulp"); + debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ulp"); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - - default: - ZFCP_LOG_NORMAL - ("bug: Wrong status qualifier 0x%x arrived.\n", - fsf_req->qtcb->header.fsf_status_qual.word[0]); + case FSF_SQ_RETRY_IF_POSSIBLE: + ZFCP_LOG_FLAGS(2, "FSF_SQ_RETRY_IF_POSSIBLE\n"); + debug_text_event(adapter->erp_dbf, 1, "fsf_sq_retry"); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; + default: + ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x\n", + header->fsf_status_qual.word[0]); + ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, + (char*)header->fsf_status_qual.word, 16); } break; - case FSF_GOOD: - ZFCP_LOG_FLAGS(2, "FSF_GOOD\n"); - retval = 0; + case FSF_ELS_COMMAND_REJECTED: + ZFCP_LOG_FLAGS(2, "FSF_ELS_COMMAND_REJECTED\n"); + ZFCP_LOG_INFO("The ELS command has been rejected because " + "a command filter in the FCP channel prohibited " + "sending of the ELS to the SAN " + "(adapter: %s, wwpn=0x%016Lx)\n", + zfcp_get_busid_by_port(port), port->wwpn); + + break; + + case FSF_PAYLOAD_SIZE_MISMATCH: + ZFCP_LOG_FLAGS(2, "FSF_PAYLOAD_SIZE_MISMATCH\n"); + ZFCP_LOG_INFO( + "ELS request size and ELS response size must be either " + "both 0, or both greater than 0 " + "(adapter: %s, req_buf_length=%d resp_buf_length=%d)\n", + zfcp_get_busid_by_port(port), + bottom->req_buf_length, + bottom->resp_buf_length); + break; + + case FSF_REQUEST_SIZE_TOO_LARGE: + ZFCP_LOG_FLAGS(2, "FSF_REQUEST_SIZE_TOO_LARGE\n"); + ZFCP_LOG_INFO( + "Length of the ELS request buffer, " + "specified in QTCB bottom, " + "exceeds the size of the buffers " + "that have been allocated for ELS request data " + "(adapter: %s, req_buf_length=%d)\n", + zfcp_get_busid_by_port(port), + bottom->req_buf_length); + break; + + case FSF_RESPONSE_SIZE_TOO_LARGE: + ZFCP_LOG_FLAGS(2, "FSF_RESPONSE_SIZE_TOO_LARGE\n"); + ZFCP_LOG_INFO( + "Length of the ELS response buffer, " + "specified in QTCB bottom, " + "exceeds the size of the buffers " + "that have been allocated for ELS response data " + "(adapter: %s, resp_buf_length=%d)\n", + zfcp_get_busid_by_port(port), + bottom->resp_buf_length); + break; + + case FSF_UNKNOWN_COMMAND: + ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_COMMAND\n"); + ZFCP_LOG_INFO( + "FSF command 0x%x is not supported by FCP adapter " + "(adapter: %s)\n", fsf_req->fsf_command, + zfcp_get_busid_by_port(port)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + + case FSF_ACCESS_DENIED: + ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n"); + ZFCP_LOG_NORMAL("Access denied, cannot send ELS " + "(adapter: %s, wwpn=0x%016Lx)\n", + zfcp_get_busid_by_port(port), port->wwpn); + counter = 0; + do { + subtable = header->fsf_status_qual.halfword[counter++]; + rule = header->fsf_status_qual.halfword[counter++]; + switch (subtable) { + case FSF_SQ_CFDC_SUBTABLE_OS: + case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: + case FSF_SQ_CFDC_SUBTABLE_PORT_DID: + case FSF_SQ_CFDC_SUBTABLE_LUN: + ZFCP_LOG_NORMAL("Access denied (%s rule %d)\n", + zfcp_act_subtable_type[subtable], rule); + break; + } + } while (counter < 4); + debug_text_event(adapter->erp_dbf, 1, "fsf_s_access"); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; default: - ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " - "(debug info 0x%x)\n", - fsf_req->qtcb->header.fsf_status); - debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval:"); - debug_exception(fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status_qual.word[0], - sizeof (u32)); + ZFCP_LOG_NORMAL( + "bug: An unknown FSF Status was presented " + "(adapter: %s, fsf_status=0x%08x)\n", + zfcp_get_busid_by_port(port), + header->fsf_status); + debug_text_event(adapter->erp_dbf, 0, "fsf_sq_inval"); + debug_exception(adapter->erp_dbf, 0, + &header->fsf_status_qual.word[0], sizeof(u32)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } - skip_fsfstatus: - /* callback */ - (fsf_req->data.send_generic.handler)(fsf_req); + +skip_fsfstatus: + send_els->status = retval; + + if (send_els->handler != 0) + send_els->handler(send_els->handler_data); + + kfree(send_els); + return retval; } @@ -1652,15 +2089,16 @@ zfcp_fsf_send_generic_handler(struct zfcp_fsf_req *fsf_req) int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) { - int retval = 0; + volatile struct qdio_buffer_element *sbale; unsigned long lock_flags; + int retval = 0; /* setup new FSF request */ retval = zfcp_fsf_req_create(erp_action->adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA, - &lock_flags, ZFCP_REQ_AUTO_CLEANUP, - &(erp_action->fsf_req)); + erp_action->adapter->pool.fsf_req_erp, + &lock_flags, &(erp_action->fsf_req)); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " "exchange configuration data request for" @@ -1669,8 +2107,14 @@ zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) goto out; } + sbale = zfcp_qdio_sbale_req(erp_action->fsf_req, + erp_action->fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + erp_action->fsf_req->erp_action = erp_action; - /* no information from us to adapter, set nothing */ + erp_action->fsf_req->qtcb->bottom.config.feature_selection = + FSF_FEATURE_CFDC; /* start QDIO request for this FSF request */ retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer); @@ -1702,7 +2146,8 @@ zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) * returns: */ static int -zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) { +zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) +{ int retval = -EIO; struct fsf_qtcb_bottom_config *bottom; struct zfcp_adapter *adapter = fsf_req->adapter; @@ -1730,6 +2175,17 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) { adapter->fsf_lic_version = bottom->lic_version; adapter->fc_topology = bottom->fc_topology; adapter->fc_link_speed = bottom->fc_link_speed; + adapter->supported_features = bottom->supported_features; + + if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){ + adapter->hardware_version = bottom->hardware_version; + /* copy just first 17 bytes */ + memcpy(adapter->serial_number, + bottom->serial_number, 17); + EBCASC(adapter->serial_number, + sizeof(adapter->serial_number)); + } + ZFCP_LOG_INFO("The adapter %s reported " "the following characteristics:\n" "WWNN 0x%16.16Lx, " @@ -1810,14 +2266,14 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) { zfcp_erp_adapter_shutdown(adapter, 0); goto skip_fsfstatus; } - if (bottom->max_qtcb_size < ZFCP_QTCB_SIZE) { + if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) { ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) " "allowed by the adapter %s " "is lower than the minimum " "required by the driver (%ld bytes).\n", bottom->max_qtcb_size, zfcp_get_busid_by_adapter(adapter), - ZFCP_QTCB_SIZE); + sizeof(struct fsf_qtcb)); debug_text_event(fsf_req->adapter->erp_dbf, 0, "qtcb-size"); debug_event(fsf_req->adapter->erp_dbf, 0, @@ -1828,13 +2284,14 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) { atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status); retval = 0; + break; + default: /* retval is -EIO by default */ debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf-stat-ng"); debug_event(fsf_req->adapter->erp_dbf, 0, &fsf_req->qtcb->header.fsf_status, sizeof (u32)); - zfcp_erp_adapter_shutdown(adapter, 0); } skip_fsfstatus: return retval; @@ -1851,15 +2308,16 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) { int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action) { - int retval = 0; + volatile struct qdio_buffer_element *sbale; unsigned long lock_flags; + int retval = 0; /* setup new FSF request */ retval = zfcp_fsf_req_create(erp_action->adapter, FSF_QTCB_OPEN_PORT_WITH_DID, - &lock_flags, ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, - &(erp_action->fsf_req)); + erp_action->adapter->pool.fsf_req_erp, + &lock_flags, &(erp_action->fsf_req)); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " "open port request for " @@ -1870,6 +2328,11 @@ zfcp_fsf_open_port(struct zfcp_erp_action *erp_action) goto out; } + sbale = zfcp_qdio_sbale_req(erp_action->fsf_req, + erp_action->fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + erp_action->fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id; atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status); erp_action->fsf_req->data.open_port.port = erp_action->port; @@ -1912,8 +2375,11 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) int retval = -EINVAL; struct zfcp_port *port; struct fsf_plogi *plogi; + struct fsf_qtcb_header *header; + u16 subtable, rule, counter; port = fsf_req->data.open_port.port; + header = &fsf_req->qtcb->header; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { /* don't change port status in our bookkeeping */ @@ -1921,7 +2387,7 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) } /* evaluate FSF status in QTCB */ - switch (fsf_req->qtcb->header.fsf_status) { + switch (header->fsf_status) { case FSF_PORT_ALREADY_OPEN: ZFCP_LOG_FLAGS(0, "FSF_PORT_ALREADY_OPEN\n"); @@ -1937,6 +2403,30 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) */ break; + case FSF_ACCESS_DENIED: + ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n"); + ZFCP_LOG_NORMAL("Access denied, cannot open port " + "with WWPN 0x%Lx connected to the adapter %s\n", + port->wwpn, zfcp_get_busid_by_port(port)); + counter = 0; + do { + subtable = header->fsf_status_qual.halfword[counter++]; + rule = header->fsf_status_qual.halfword[counter++]; + switch (subtable) { + case FSF_SQ_CFDC_SUBTABLE_OS: + case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: + case FSF_SQ_CFDC_SUBTABLE_PORT_DID: + case FSF_SQ_CFDC_SUBTABLE_LUN: + ZFCP_LOG_NORMAL("Access denied (%s rule %d)\n", + zfcp_act_subtable_type[subtable], rule); + break; + } + } while (counter < 4); + debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access"); + zfcp_erp_port_failed(port); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED: ZFCP_LOG_FLAGS(1, "FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED\n"); ZFCP_LOG_INFO("error: The FSF adapter is out of resources. " @@ -1952,7 +2442,7 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) case FSF_ADAPTER_STATUS_AVAILABLE: ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n"); - switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { + switch (header->fsf_status_qual.word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n"); @@ -1982,12 +2472,12 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) default: ZFCP_LOG_NORMAL ("bug: Wrong status qualifier 0x%x arrived.\n", - fsf_req->qtcb->header.fsf_status_qual.word[0]); + header->fsf_status_qual.word[0]); debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval:"); debug_exception( fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status_qual.word[0], + &header->fsf_status_qual.word[0], sizeof (u32)); break; } @@ -1996,7 +2486,7 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) case FSF_GOOD: ZFCP_LOG_FLAGS(3, "FSF_GOOD\n"); /* save port handle assigned by FSF */ - port->handle = fsf_req->qtcb->header.port_handle; + port->handle = header->port_handle; ZFCP_LOG_INFO("The remote port (WWPN=0x%Lx) via adapter " "(busid=%s) was opened, it's " "port handle is 0x%x\n", @@ -2055,11 +2545,10 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) default: ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " "(debug info 0x%x)\n", - fsf_req->qtcb->header.fsf_status); + header->fsf_status); debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); debug_exception(fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status, - sizeof (u32)); + &header->fsf_status, sizeof (u32)); break; } @@ -2079,15 +2568,16 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action) { - int retval = 0; + volatile struct qdio_buffer_element *sbale; unsigned long lock_flags; + int retval = 0; /* setup new FSF request */ retval = zfcp_fsf_req_create(erp_action->adapter, FSF_QTCB_CLOSE_PORT, - &lock_flags, ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, - &(erp_action->fsf_req)); + erp_action->adapter->pool.fsf_req_erp, + &lock_flags, &(erp_action->fsf_req)); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create a " "close port request for WWPN 0x%Lx connected to " @@ -2097,6 +2587,11 @@ zfcp_fsf_close_port(struct zfcp_erp_action *erp_action) goto out; } + sbale = zfcp_qdio_sbale_req(erp_action->fsf_req, + erp_action->fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status); erp_action->fsf_req->data.close_port.port = erp_action->port; erp_action->fsf_req->erp_action = erp_action; @@ -2153,7 +2648,7 @@ zfcp_fsf_close_port_handler(struct zfcp_fsf_req *fsf_req) ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n"); ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x " "for the port with WWPN 0x%Lx connected to " - "the adapter %s is" + "the adapter %s is " "not valid. This may happen occasionally.\n", port->handle, port->wwpn, zfcp_get_busid_by_port(port)); @@ -2221,9 +2716,9 @@ zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action) /* setup new FSF request */ retval = zfcp_fsf_req_create(erp_action->adapter, FSF_QTCB_CLOSE_PHYSICAL_PORT, - &lock_flags, ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, - &erp_action->fsf_req); + erp_action->adapter->pool.fsf_req_erp, + &lock_flags, &erp_action->fsf_req); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create a " "close physical port request for " @@ -2280,8 +2775,11 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) int retval = -EINVAL; struct zfcp_port *port; struct zfcp_unit *unit; + struct fsf_qtcb_header *header; + u16 subtable, rule, counter; port = fsf_req->data.close_physical_port.port; + header = &fsf_req->qtcb->header; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { /* don't change port status in our bookkeeping */ @@ -2289,7 +2787,7 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) } /* evaluate FSF status in QTCB */ - switch (fsf_req->qtcb->header.fsf_status) { + switch (header->fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n"); @@ -2302,7 +2800,7 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) zfcp_get_busid_by_port(port)); ZFCP_LOG_DEBUG("status qualifier:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, - (char *) &fsf_req->qtcb->header.fsf_status_qual, + (char *) &header->fsf_status_qual, sizeof (union fsf_status_qual)); debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_phand_nv"); @@ -2310,6 +2808,30 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; + case FSF_ACCESS_DENIED: + ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n"); + ZFCP_LOG_NORMAL("Access denied, cannot close " + "physical port with WWPN 0x%Lx connected to " + "the adapter %s\n", port->wwpn, + zfcp_get_busid_by_port(port)); + counter = 0; + do { + subtable = header->fsf_status_qual.halfword[counter++]; + rule = header->fsf_status_qual.halfword[counter++]; + switch (subtable) { + case FSF_SQ_CFDC_SUBTABLE_OS: + case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: + case FSF_SQ_CFDC_SUBTABLE_PORT_DID: + case FSF_SQ_CFDC_SUBTABLE_LUN: + ZFCP_LOG_NORMAL("Access denied (%s rule %d)\n", + zfcp_act_subtable_type[subtable], rule); + break; + } + } while (counter < 4); + debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access"); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + case FSF_PORT_BOXED: ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n"); ZFCP_LOG_DEBUG("The remote port with WWPN 0x%Lx on the adapter " @@ -2325,7 +2847,7 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) case FSF_ADAPTER_STATUS_AVAILABLE: ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n"); - switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { + switch (header->fsf_status_qual.word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n"); @@ -2345,13 +2867,12 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) default: ZFCP_LOG_NORMAL ("bug: Wrong status qualifier 0x%x arrived.\n", - fsf_req->qtcb->header.fsf_status_qual.word[0]); + header->fsf_status_qual.word[0]); debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval:"); debug_exception( fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status_qual.word[0], - sizeof (u32)); + &header->fsf_status_qual.word[0], sizeof (u32)); break; } break; @@ -2375,11 +2896,10 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) default: ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " "(debug info 0x%x)\n", - fsf_req->qtcb->header.fsf_status); + header->fsf_status); debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); debug_exception(fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status, - sizeof (u32)); + &header->fsf_status, sizeof (u32)); break; } @@ -2403,15 +2923,16 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action) { - int retval = 0; + volatile struct qdio_buffer_element *sbale; unsigned long lock_flags; + int retval = 0; /* setup new FSF request */ retval = zfcp_fsf_req_create(erp_action->adapter, FSF_QTCB_OPEN_LUN, - &lock_flags, ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, - &(erp_action->fsf_req)); + erp_action->adapter->pool.fsf_req_erp, + &lock_flags, &(erp_action->fsf_req)); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " "open unit request for FCP-LUN 0x%Lx connected " @@ -2423,6 +2944,11 @@ zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action) goto out; } + sbale = zfcp_qdio_sbale_req(erp_action->fsf_req, + erp_action->fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle; erp_action->fsf_req->qtcb->bottom.support.fcp_lun = @@ -2430,6 +2956,8 @@ zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action) atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status); erp_action->fsf_req->data.open_unit.unit = erp_action->unit; erp_action->fsf_req->erp_action = erp_action; + erp_action->fsf_req->qtcb->bottom.support.option = + FSF_OPEN_LUN_SUPPRESS_BOXING; /* start QDIO request for this FSF request */ retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer); @@ -2467,8 +2995,11 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) { int retval = -EINVAL; struct zfcp_unit *unit; + struct fsf_qtcb_header *header; + u16 subtable, rule, counter; unit = fsf_req->data.open_unit.unit; + header = &fsf_req->qtcb->header; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { /* don't change unit status in our bookkeeping */ @@ -2476,19 +3007,19 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) } /* evaluate FSF status in QTCB */ - switch (fsf_req->qtcb->header.fsf_status) { + switch (header->fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n"); ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x " "for the port with WWPN 0x%Lx connected to " - "the adapter %s is" + "the adapter %s is " "not valid. This may happen occasionally.\n", unit->port->handle, unit->port->wwpn, zfcp_get_busid_by_unit(unit)); ZFCP_LOG_DEBUG("status qualifier:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, - (char *) &fsf_req->qtcb->header.fsf_status_qual, + (char *) &header->fsf_status_qual, sizeof (union fsf_status_qual)); debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_ph_nv"); zfcp_erp_adapter_reopen(unit->port->adapter, 0); @@ -2508,6 +3039,32 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; + case FSF_ACCESS_DENIED: + ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n"); + ZFCP_LOG_NORMAL("Access denied, cannot open unit " + "with FCP-LUN 0x%Lx at the remote port with " + "WWPN 0x%Lx connected to the adapter %s\n", + unit->fcp_lun, unit->port->wwpn, + zfcp_get_busid_by_unit(unit)); + counter = 0; + do { + subtable = header->fsf_status_qual.halfword[counter++]; + rule = header->fsf_status_qual.halfword[counter++]; + switch (subtable) { + case FSF_SQ_CFDC_SUBTABLE_OS: + case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: + case FSF_SQ_CFDC_SUBTABLE_PORT_DID: + case FSF_SQ_CFDC_SUBTABLE_LUN: + ZFCP_LOG_NORMAL("Access denied (%s rule %d)\n", + zfcp_act_subtable_type[subtable], rule); + break; + } + } while (counter < 4); + debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access"); + zfcp_erp_unit_failed(unit); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + case FSF_PORT_BOXED: ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n"); ZFCP_LOG_DEBUG("The remote port " @@ -2520,8 +3077,8 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) ZFCP_STATUS_FSFREQ_RETRY; break; - case FSF_LUN_IN_USE: - ZFCP_LOG_FLAGS(0, "FSF_LUN_IN_USE\n"); + case FSF_LUN_SHARING_VIOLATION : + ZFCP_LOG_FLAGS(2, "FSF_LUN_SHARING_VIOLATION\n"); ZFCP_LOG_NORMAL("error: FCP-LUN 0x%Lx at " "the remote port with WWPN 0x%Lx connected " "to the adapter %s " @@ -2530,12 +3087,23 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) unit->fcp_lun, unit->port->wwpn, zfcp_get_busid_by_unit(unit)); + subtable = header->fsf_status_qual.halfword[4]; + rule = header->fsf_status_qual.halfword[5]; + switch (subtable) { + case FSF_SQ_CFDC_SUBTABLE_OS: + case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: + case FSF_SQ_CFDC_SUBTABLE_PORT_DID: + case FSF_SQ_CFDC_SUBTABLE_LUN: + ZFCP_LOG_NORMAL("Access denied (%s rule %d)\n", + zfcp_act_subtable_type[subtable], rule); + break; + } ZFCP_LOG_NORMAL("Additional sense data is presented:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, - (char *) &fsf_req->qtcb->header.fsf_status_qual, + (char *) &header->fsf_status_qual, sizeof (union fsf_status_qual)); debug_text_event(fsf_req->adapter->erp_dbf, 2, - "fsf_s_l_in_use"); + "fsf_s_l_sh_vio"); zfcp_erp_unit_failed(unit); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -2558,7 +3126,7 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) case FSF_ADAPTER_STATUS_AVAILABLE: ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n"); - switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { + switch (header->fsf_status_qual.word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n"); @@ -2579,12 +3147,11 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) default: ZFCP_LOG_NORMAL ("bug: Wrong status qualifier 0x%x arrived.\n", - fsf_req->qtcb->header.fsf_status_qual.word[0]); + header->fsf_status_qual.word[0]); debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval:"); - debug_exception( - fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status_qual.word[0], + debug_exception(fsf_req->adapter->erp_dbf, 0, + &header->fsf_status_qual.word[0], sizeof (u32)); } break; @@ -2592,7 +3159,7 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) case FSF_GOOD: ZFCP_LOG_FLAGS(3, "FSF_GOOD\n"); /* save LUN handle assigned by FSF */ - unit->handle = fsf_req->qtcb->header.lun_handle; + unit->handle = header->lun_handle; ZFCP_LOG_TRACE("unit (FCP_LUN=0x%Lx) of remote port " "(WWPN=0x%Lx) via adapter (busid=%s) opened, " "port handle 0x%x \n", @@ -2608,11 +3175,10 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) default: ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented " "(debug info 0x%x)\n", - fsf_req->qtcb->header.fsf_status); + header->fsf_status); debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); debug_exception(fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status, - sizeof (u32)); + &header->fsf_status, sizeof (u32)); break; } @@ -2637,15 +3203,16 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action) { - int retval = 0; + volatile struct qdio_buffer_element *sbale; unsigned long lock_flags; + int retval = 0; /* setup new FSF request */ retval = zfcp_fsf_req_create(erp_action->adapter, FSF_QTCB_CLOSE_LUN, - &lock_flags, ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP, - &(erp_action->fsf_req)); + erp_action->adapter->pool.fsf_req_erp, + &lock_flags, &(erp_action->fsf_req)); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create a " "close unit request for FCP-LUN 0x%Lx " @@ -2657,6 +3224,11 @@ zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action) goto out; } + sbale = zfcp_qdio_sbale_req(erp_action->fsf_req, + erp_action->fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle; erp_action->fsf_req->qtcb->header.lun_handle = erp_action->unit->handle; @@ -2846,21 +3418,19 @@ zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *fsf_req) int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, struct zfcp_unit *unit, - Scsi_Cmnd * scsi_cmnd, int req_flags) + struct scsi_cmnd * scsi_cmnd, int req_flags) { struct zfcp_fsf_req *fsf_req = NULL; struct fcp_cmnd_iu *fcp_cmnd_iu; - volatile struct qdio_buffer_element *buffere; unsigned int sbtype; unsigned long lock_flags; int real_bytes = 0; int retval = 0; /* setup new FSF request */ - - retval = zfcp_fsf_req_create(adapter, - FSF_QTCB_FCP_CMND, - &lock_flags, req_flags, &(fsf_req)); + retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags, + adapter->pool.fsf_req_scsi, + &lock_flags, &fsf_req); if (unlikely(retval < 0)) { ZFCP_LOG_DEBUG("error: Out of resources. Could not create an " "FCP command request for FCP-LUN 0x%Lx " @@ -2912,8 +3482,8 @@ zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, * data direction bits in FCP_CMND IU */ switch (scsi_cmnd->sc_data_direction) { - case SCSI_DATA_NONE: - ZFCP_LOG_FLAGS(3, "SCSI_DATA_NONE\n"); + case DMA_NONE: + ZFCP_LOG_FLAGS(3, "DMA_NONE\n"); fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND; /* * FIXME(qdio): @@ -2922,20 +3492,20 @@ zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, */ sbtype = SBAL_FLAGS0_TYPE_READ; break; - case SCSI_DATA_READ: - ZFCP_LOG_FLAGS(3, "SCSI_DATA_READ\n"); + case DMA_FROM_DEVICE: + ZFCP_LOG_FLAGS(3, "DMA_FROM_DEVICE\n"); fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ; sbtype = SBAL_FLAGS0_TYPE_READ; fcp_cmnd_iu->rddata = 1; break; - case SCSI_DATA_WRITE: - ZFCP_LOG_FLAGS(3, "SCSI_DATA_WRITE\n"); + case DMA_TO_DEVICE: + ZFCP_LOG_FLAGS(3, "DMA_TO_DEVICE\n"); fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE; sbtype = SBAL_FLAGS0_TYPE_WRITE; fcp_cmnd_iu->wddata = 1; break; - case SCSI_DATA_UNKNOWN: - ZFCP_LOG_FLAGS(0, "SCSI_DATA_UNKNOWN not supported\n"); + case DMA_BIDIRECTIONAL: + ZFCP_LOG_FLAGS(0, "DMA_BIDIRECTIONAL not supported\n"); default: /* * dummy, catch this condition earlier @@ -2943,9 +3513,6 @@ zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, */ goto failed_scsi_cmnd; } - buffere = - &(adapter->request_queue.buffer[fsf_req->sbal_index]->element[0]); - buffere->flags |= sbtype; /* set FC service class in QTCB (3 per default) */ fsf_req->qtcb->bottom.io.service_class = adapter->fc_service_class; @@ -2984,29 +3551,24 @@ zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, fcp_cmnd_iu->add_fcp_cdb_length + sizeof (fcp_dl_t); /* generate SBALEs from data buffer */ - real_bytes = zfcp_create_sbals_from_sg(fsf_req, - scsi_cmnd, - sbtype, - 0, ZFCP_MAX_SBALS_PER_REQ); - /* Note: >= and not = because the combined scatter-gather entries - * may be larger than request_bufflen according to the mailing list - */ - if (likely(real_bytes >= scsi_cmnd->request_bufflen)) { - ZFCP_LOG_TRACE("Data fits\n"); - } else if (likely(real_bytes == 0)) { - ZFCP_LOG_DEBUG("Data did not fit into available buffer(s), " + real_bytes = zfcp_qdio_sbals_from_scsicmnd(fsf_req, sbtype, scsi_cmnd); + if (unlikely(real_bytes < 0)) { + if (fsf_req->sbal_number < ZFCP_MAX_SBALS_PER_REQ) { + ZFCP_LOG_DEBUG( + "Data did not fit into available buffer(s), " "waiting for more...\n"); retval = -EIO; - goto no_fit; } else { ZFCP_LOG_NORMAL("error: No truncation implemented but " - "required. Shutting down unit (busid=%s, " - "WWPN=0x%16.16Lx, FCP_LUN=0x%16.16Lx)\n", + "required. Shutting down unit " + "(busid=%s, WWPN=0x%16.16Lx, " + "FCP_LUN=0x%16.16Lx)\n", zfcp_get_busid_by_unit(unit), unit->port->wwpn, unit->fcp_lun); zfcp_erp_unit_shutdown(unit, 0); retval = -EINVAL; + } goto no_fit; } @@ -3077,11 +3639,12 @@ zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter, int retval = 0; struct fcp_cmnd_iu *fcp_cmnd_iu; unsigned long lock_flags; - volatile struct qdio_buffer_element *buffere; + volatile struct qdio_buffer_element *sbale; /* setup new FSF request */ - retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, - &lock_flags, req_flags, &(fsf_req)); + retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags, + adapter->pool.fsf_req_scsi, + &lock_flags, &fsf_req); if (retval < 0) { ZFCP_LOG_INFO("error: Out of resources. Could not create an " "FCP command (task management) request for " @@ -3113,10 +3676,9 @@ zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter, fsf_req->qtcb->bottom.io.fcp_cmnd_length = sizeof (struct fcp_cmnd_iu) + sizeof (fcp_dl_t); - buffere = - &(adapter->request_queue.buffer[fsf_req->sbal_index]->element[0]); - buffere[0].flags |= SBAL_FLAGS0_TYPE_WRITE; - buffere[1].flags |= SBAL_FLAGS_LAST_ENTRY; + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE; + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; /* set FCP related fields in FCP_CMND IU in QTCB */ fcp_cmnd_iu = (struct fcp_cmnd_iu *) @@ -3164,6 +3726,10 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) { int retval = -EINVAL; struct zfcp_unit *unit; + struct fsf_qtcb_header *header; + u16 subtable, rule, counter; + + header = &fsf_req->qtcb->header; if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)) unit = fsf_req->data.send_fcp_command_task_management.unit; @@ -3176,7 +3742,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) } /* evaluate FSF status in QTCB */ - switch (fsf_req->qtcb->header.fsf_status) { + switch (header->fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n"); @@ -3186,7 +3752,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) unit->port->handle, unit->port->wwpn, zfcp_get_busid_by_unit(unit)); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, - (char *) &fsf_req->qtcb->header.fsf_status_qual, + (char *) &header->fsf_status_qual, sizeof (union fsf_status_qual)); debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_phand_nv"); @@ -3207,7 +3773,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_get_busid_by_unit(unit)); ZFCP_LOG_NORMAL("Status qualifier data:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, - (char *) &fsf_req->qtcb->header.fsf_status_qual, + (char *) &header->fsf_status_qual, sizeof (union fsf_status_qual)); debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_uhand_nv"); @@ -3229,14 +3795,14 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_get_busid_by_unit(unit)); ZFCP_LOG_NORMAL("status qualifier:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, - (char *) &fsf_req->qtcb->header.fsf_status_qual, + (char *) &header->fsf_status_qual, sizeof (union fsf_status_qual)); debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_hand_mis"); zfcp_erp_adapter_reopen(unit->port->adapter, 0); zfcp_cmd_dbf_event_fsf("handmism", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3261,7 +3827,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_shutdown(unit->port->adapter, 0); zfcp_cmd_dbf_event_fsf("unsclass", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3278,18 +3844,43 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) unit->handle); ZFCP_LOG_DEBUG("status qualifier:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, - (char *) &fsf_req->qtcb->header.fsf_status_qual, + (char *) &header->fsf_status_qual, sizeof (union fsf_status_qual)); debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_fcp_lun_nv"); zfcp_erp_port_reopen(unit->port, 0); zfcp_cmd_dbf_event_fsf("fluninv", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; + case FSF_ACCESS_DENIED: + ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n"); + ZFCP_LOG_NORMAL("Access denied, cannot send FCP " + "command to the unit with FCP-LUN 0x%Lx at the " + "remote port with WWPN 0x%Lx connected to the " + "adapter %s\n", unit->fcp_lun, unit->port->wwpn, + zfcp_get_busid_by_unit(unit)); + counter = 0; + do { + subtable = header->fsf_status_qual.halfword[counter++]; + rule = header->fsf_status_qual.halfword[counter++]; + switch (subtable) { + case FSF_SQ_CFDC_SUBTABLE_OS: + case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN: + case FSF_SQ_CFDC_SUBTABLE_PORT_DID: + case FSF_SQ_CFDC_SUBTABLE_LUN: + ZFCP_LOG_NORMAL("Access denied (%s rule %d)\n", + zfcp_act_subtable_type[subtable], rule); + break; + } + } while (counter < 4); + debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access"); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + break; + case FSF_DIRECTION_INDICATOR_NOT_VALID: ZFCP_LOG_FLAGS(0, "FSF_DIRECTION_INDICATOR_NOT_VALID\n"); ZFCP_LOG_INFO("bug: Invalid data direction given for the unit " @@ -3306,7 +3897,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_shutdown(unit->port->adapter, 0); zfcp_cmd_dbf_event_fsf("dirinv", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3326,7 +3917,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_shutdown(unit->port->adapter, 0); zfcp_cmd_dbf_event_fsf("idleninv", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3346,7 +3937,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) "fsf_s_out_dl_nv"); zfcp_erp_adapter_shutdown(unit->port->adapter, 0); zfcp_cmd_dbf_event_fsf("odleninv", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3367,7 +3958,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_shutdown(unit->port->adapter, 0); zfcp_cmd_dbf_event_fsf("cleninv", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3381,15 +3972,32 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_pboxed"); zfcp_erp_port_reopen(unit->port, 0); zfcp_cmd_dbf_event_fsf("portbox", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | ZFCP_STATUS_FSFREQ_RETRY; break; + case FSF_LUN_BOXED: + ZFCP_LOG_FLAGS(0, "FSF_LUN_BOXED\n"); + ZFCP_LOG_NORMAL( + "The remote unit with FCP-LUN 0x%Lx " + "at the remote port with WWPN 0x%Lx " + "connected to the adapter %s needs to be reopened\n", + unit->fcp_lun, unit->port->wwpn, + zfcp_get_busid_by_unit(unit)); + debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_lboxed"); + zfcp_erp_unit_reopen(unit, 0); + zfcp_cmd_dbf_event_fsf("unitbox", fsf_req, + &header->fsf_status_qual, + sizeof(union fsf_status_qual)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR + | ZFCP_STATUS_FSFREQ_RETRY; + break; + case FSF_ADAPTER_STATUS_AVAILABLE: ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n"); - switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { + switch (header->fsf_status_qual.word[0]) { case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n"); @@ -3400,7 +4008,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_cmd_dbf_event_fsf( "sqltest", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3414,7 +4022,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_cmd_dbf_event_fsf( "sqdeperp", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, + &header->fsf_status_qual, sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3422,14 +4030,12 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) /* FIXME: shall we consider this a successful transfer? */ ZFCP_LOG_NORMAL ("bug: Wrong status qualifier 0x%x arrived.\n", - fsf_req->qtcb->header.fsf_status_qual.word[0]); + header->fsf_status_qual.word[0]); debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval:"); - debug_exception( - fsf_req->adapter->erp_dbf, - 0, - &fsf_req->qtcb->header.fsf_status_qual.word[0], - sizeof (u32)); + debug_exception(fsf_req->adapter->erp_dbf, 0, + &header->fsf_status_qual.word[0], + sizeof(u32)); break; } break; @@ -3445,8 +4051,7 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) default: debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_inval:"); debug_exception(fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status, - sizeof (u32)); + &header->fsf_status, sizeof(u32)); break; } @@ -3471,8 +4076,7 @@ static int zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) { int retval = 0; - - Scsi_Cmnd *scpnt; + struct scsi_cmnd *scpnt; struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *) &(fsf_req->qtcb->bottom.io.fcp_rsp); struct fcp_cmnd_iu *fcp_cmnd_iu = (struct fcp_cmnd_iu *) @@ -3874,6 +4478,310 @@ zfcp_fsf_send_fcp_command_task_management_handler(struct zfcp_fsf_req *fsf_req) return retval; } + +/* + * function: zfcp_fsf_control_file + * + * purpose: Initiator of the control file upload/download FSF requests + * + * returns: 0 - FSF request is successfuly created and queued + * -EOPNOTSUPP - The FCP adapter does not have Control File support + * -EINVAL - Invalid direction specified + * -ENOMEM - Insufficient memory + * -EPERM - Cannot create FSF request or or place it in QDIO queue + */ +int +zfcp_fsf_control_file(struct zfcp_adapter *adapter, + struct zfcp_fsf_req **fsf_req_ptr, + u32 fsf_command, + u32 option, + struct zfcp_sg_list *sg_list) +{ + struct zfcp_fsf_req *fsf_req; + struct fsf_qtcb_bottom_support *bottom; + volatile struct qdio_buffer_element *sbale; + unsigned long lock_flags; + int req_flags = 0; + int direction; + int retval = 0; + +#if 0 + if (!(adapter->features & FSF_FEATURE_CFDC)) { + ZFCP_LOG_INFO( + "Adapter %s does not support control file\n", + zfcp_get_busid_by_adapter(adapter)); + retval = -EOPNOTSUPP; + goto no_act_support; + } +#endif + + switch (fsf_command) { + + case FSF_QTCB_DOWNLOAD_CONTROL_FILE: + direction = SBAL_FLAGS0_TYPE_WRITE; + if ((option != FSF_CFDC_OPTION_FULL_ACCESS) && + (option != FSF_CFDC_OPTION_RESTRICTED_ACCESS)) + req_flags = ZFCP_WAIT_FOR_SBAL; + break; + + case FSF_QTCB_UPLOAD_CONTROL_FILE: + direction = SBAL_FLAGS0_TYPE_READ; + break; + + default: + ZFCP_LOG_INFO("Invalid FSF command code 0x%08x\n", fsf_command); + goto invalid_command; + } + + retval = zfcp_fsf_req_create(adapter, fsf_command, req_flags, + NULL, &lock_flags, &fsf_req); + if (retval < 0) { + ZFCP_LOG_INFO("error: Could not create FSF request for the " + "adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + retval = -EPERM; + goto out; + } + + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + sbale[0].flags |= direction; + + bottom = &fsf_req->qtcb->bottom.support; + bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE; + bottom->option = option; + + if (sg_list->count > 0) { + int bytes; + + bytes = zfcp_qdio_sbals_from_sg(fsf_req, direction, + sg_list->sg, sg_list->count, + ZFCP_MAX_SBALS_PER_REQ); + if (bytes != ZFCP_CFDC_MAX_CONTROL_FILE_SIZE) { + ZFCP_LOG_INFO( + "error: Could not create sufficient number of " + "SBALS for an FSF request to the adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + retval = -ENOMEM; + goto sbals_failed; + } + } else { + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + } + + retval = zfcp_fsf_req_send(fsf_req, NULL); + if (retval < 0) { + ZFCP_LOG_INFO( + "error: Could not send FSF request to the adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + retval = -EPERM; + goto queue_failed; + } + + ZFCP_LOG_NORMAL( + "Control file %s FSF request has been sent to the adapter %s\n", + fsf_command == FSF_QTCB_DOWNLOAD_CONTROL_FILE ? + "download" : "upload", + zfcp_get_busid_by_adapter(adapter)); + + *fsf_req_ptr = fsf_req; + + goto out; + +sbals_failed: +queue_failed: + zfcp_fsf_req_free(fsf_req); + +out: + write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); + +invalid_command: + return retval; +} + + +/* + * function: zfcp_fsf_control_file_handler + * + * purpose: Handler of the control file upload/download FSF requests + * + * returns: 0 - FSF request successfuly processed + * -EAGAIN - Operation has to be repeated because of a temporary problem + * -EACCES - There is no permission to execute an operation + * -EPERM - The control file is not in a right format + * -EIO - There is a problem with the FCP adapter + * -EINVAL - Invalid operation + * -EFAULT - User space memory I/O operation fault + */ +static int +zfcp_fsf_control_file_handler(struct zfcp_fsf_req *fsf_req) +{ + struct zfcp_adapter *adapter = fsf_req->adapter; + struct fsf_qtcb_header *header = &fsf_req->qtcb->header; + struct fsf_qtcb_bottom_support *bottom = &fsf_req->qtcb->bottom.support; + int retval = 0; + + if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { + retval = -EINVAL; + goto skip_fsfstatus; + } + + switch (header->fsf_status) { + + case FSF_GOOD: + ZFCP_LOG_FLAGS(2, "FSF_GOOD\n"); + ZFCP_LOG_NORMAL( + "The FSF request has been successfully completed " + "on the adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + break; + + case FSF_OPERATION_PARTIALLY_SUCCESSFUL: + ZFCP_LOG_FLAGS(2, "FSF_OPERATION_PARTIALLY_SUCCESSFUL\n"); + if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) { + switch (header->fsf_status_qual.word[0]) { + + case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE: + ZFCP_LOG_NORMAL( + "CFDC of the adapter %s could not " + "be saved on the SE\n", + zfcp_get_busid_by_adapter(adapter)); + break; + + case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2: + ZFCP_LOG_NORMAL( + "CFDC of the adapter %s could not " + "be copied to the secondary SE\n", + zfcp_get_busid_by_adapter(adapter)); + break; + + default: + ZFCP_LOG_NORMAL( + "CFDC could not be hardened " + "on the adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + } + } + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EAGAIN; + break; + + case FSF_AUTHORIZATION_FAILURE: + ZFCP_LOG_FLAGS(2, "FSF_AUTHORIZATION_FAILURE\n"); + ZFCP_LOG_NORMAL( + "Adapter %s does not accept privileged commands\n", + zfcp_get_busid_by_adapter(adapter)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EACCES; + break; + + case FSF_CFDC_ERROR_DETECTED: + ZFCP_LOG_FLAGS(2, "FSF_CFDC_ERROR_DETECTED\n"); + ZFCP_LOG_NORMAL( + "Error at position %d in the CFDC, " + "CFDC is discarded by the adapter %s\n", + header->fsf_status_qual.word[0], + zfcp_get_busid_by_adapter(adapter)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EPERM; + break; + + case FSF_CONTROL_FILE_UPDATE_ERROR: + ZFCP_LOG_FLAGS(2, "FSF_CONTROL_FILE_UPDATE_ERROR\n"); + ZFCP_LOG_NORMAL( + "Adapter %s cannot harden the control file, " + "file is discarded\n", + zfcp_get_busid_by_adapter(adapter)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EIO; + break; + + case FSF_CONTROL_FILE_TOO_LARGE: + ZFCP_LOG_FLAGS(2, "FSF_CONTROL_FILE_TOO_LARGE\n"); + ZFCP_LOG_NORMAL( + "Control file is too large, file is discarded " + "by the adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EIO; + break; + + case FSF_ACCESS_CONFLICT_DETECTED: + ZFCP_LOG_FLAGS(2, "FSF_ACCESS_CONFLICT_DETECTED\n"); + if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) + ZFCP_LOG_NORMAL( + "CFDC has been discarded by the adapter %s, " + "because activation would impact " + "%d active connection(s)\n", + zfcp_get_busid_by_adapter(adapter), + header->fsf_status_qual.word[0]); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EIO; + break; + + case FSF_CONFLICTS_OVERRULED: + ZFCP_LOG_FLAGS(2, "FSF_CONFLICTS_OVERRULED\n"); + if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) + ZFCP_LOG_NORMAL( + "CFDC has been activated on the adapter %s, " + "but activation has impacted " + "%d active connection(s)\n", + zfcp_get_busid_by_adapter(adapter), + header->fsf_status_qual.word[0]); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EIO; + break; + + case FSF_UNKNOWN_COMMAND: + ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_COMMAND\n"); + ZFCP_LOG_NORMAL( + "FSF command 0x%x is not supported by the adapter %s\n", + fsf_req->fsf_command, + zfcp_get_busid_by_adapter(adapter)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EINVAL; + break; + + case FSF_UNKNOWN_OP_SUBTYPE: + ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_OP_SUBTYPE\n"); + ZFCP_LOG_NORMAL( + "Invalid operation subtype 0x%x has been specified " + "in QTCB bottom sent to the adapter %s\n", + bottom->operation_subtype, + zfcp_get_busid_by_adapter(adapter)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EINVAL; + break; + + case FSF_INVALID_COMMAND_OPTION: + ZFCP_LOG_FLAGS(2, "FSF_INVALID_COMMAND_OPTION\n"); + ZFCP_LOG_NORMAL( + "Invalid option 0x%x has been specified " + "in QTCB bottom sent to the adapter %s\n", + bottom->option, + zfcp_get_busid_by_adapter(adapter)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EINVAL; + break; + + default: + ZFCP_LOG_NORMAL( + "bug: An unknown/unexpected FSF status 0x%08x " + "was presented on the adapter %s\n", + header->fsf_status, + zfcp_get_busid_by_adapter(adapter)); + debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval"); + debug_exception(fsf_req->adapter->erp_dbf, 0, + &header->fsf_status_qual.word[0], sizeof(u32)); + fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = -EINVAL; + break; + } + +skip_fsfstatus: + return retval; +} + + /* * function: zfcp_fsf_req_wait_and_cleanup * @@ -3931,6 +4839,54 @@ zfcp_fsf_req_create_sbal_check(unsigned long *flags, } /* + * set qtcb pointer in fsf_req and initialize QTCB + */ +static inline void +zfcp_fsf_req_qtcb_init(struct zfcp_fsf_req *fsf_req, u32 fsf_cmd) +{ + if (likely(fsf_req->qtcb != NULL)) { + fsf_req->qtcb->prefix.req_id = (unsigned long)fsf_req; + fsf_req->qtcb->prefix.ulp_info = ZFCP_ULP_INFO_VERSION; + fsf_req->qtcb->prefix.qtcb_type = fsf_qtcb_type[fsf_cmd]; + fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION; + fsf_req->qtcb->header.req_handle = (unsigned long)fsf_req; + fsf_req->qtcb->header.fsf_command = fsf_cmd; + } +} + +/** + * zfcp_fsf_req_sbal_get - try to get one SBAL in the request queue + * @adapter: adapter for which request queue is examined + * @req_flags: flags indicating whether to wait for needed SBAL or not + * @lock_flags: lock_flags is queue_lock is taken + * + * locking: on success the queue_lock for the request queue of the adapter + * is held + */ +static int +zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter, int req_flags, + unsigned long *lock_flags) +{ + int condition; + unsigned long timeout = ZFCP_SBAL_TIMEOUT; + struct zfcp_qdio_queue *req_queue = &adapter->request_queue; + + if (unlikely(req_flags & ZFCP_WAIT_FOR_SBAL)) { + ZFCP_WAIT_EVENT_TIMEOUT(adapter->request_wq, timeout, + (condition = + (zfcp_fsf_req_create_sbal_check) + (lock_flags, req_queue, 1))); + if (!condition) { + return -EIO; + } + } else if (!zfcp_fsf_req_create_sbal_check(lock_flags, req_queue, 1)) { + return -EIO; + } + + return 0; +} + +/* * function: zfcp_fsf_req_create * * purpose: create an FSF request at the specified adapter and @@ -3947,149 +4903,65 @@ zfcp_fsf_req_create_sbal_check(unsigned long *flags, * but is held on completion (write, irqsave) */ int -zfcp_fsf_req_create(struct zfcp_adapter *adapter, - u32 fsf_cmd, - unsigned long *lock_flags, - int req_flags, +zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags, + mempool_t *pool, unsigned long *lock_flags, struct zfcp_fsf_req **fsf_req_p) { + volatile struct qdio_buffer_element *sbale; struct zfcp_fsf_req *fsf_req = NULL; - int retval = 0; + int ret = 0; struct zfcp_qdio_queue *req_queue = &adapter->request_queue; - volatile struct qdio_buffer_element *buffere; - unsigned long timeout; - int condition; /* allocate new FSF request */ - fsf_req = zfcp_fsf_req_alloc(adapter, fsf_cmd, GFP_ATOMIC); - if (unlikely(!fsf_req)) { + fsf_req = zfcp_fsf_req_alloc(pool, req_flags); + if (unlikely(NULL == fsf_req)) { ZFCP_LOG_DEBUG("error: Could not put an FSF request into" "the outbound (send) queue.\n"); - retval = -ENOMEM; + ret = -ENOMEM; goto failed_fsf_req; } - /* save pointer to "parent" adapter */ - fsf_req->adapter = adapter; + + zfcp_fsf_req_qtcb_init(fsf_req, fsf_cmd); /* initialize waitqueue which may be used to wait on this request completion */ init_waitqueue_head(&fsf_req->completion_wq); + ret = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags); + if(ret < 0) { + goto failed_sbals; + } + + /* set magics */ fsf_req->common_magic = ZFCP_MAGIC; fsf_req->specific_magic = ZFCP_MAGIC_FSFREQ; + fsf_req->adapter = adapter; /* pointer to "parent" adapter */ fsf_req->fsf_command = fsf_cmd; - if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP)) - fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; + fsf_req->sbal_number = 1; + fsf_req->sbal_first = req_queue->free_index; + fsf_req->sbal_curr = req_queue->free_index; + fsf_req->sbale_curr = 1; - /* initialize QTCB */ - if (likely(fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS)) { - ZFCP_LOG_TRACE("fsf_req->qtcb=0x%lx\n", - (unsigned long) fsf_req->qtcb); - fsf_req->qtcb->prefix.req_id = (unsigned long) fsf_req; - fsf_req->qtcb->prefix.ulp_info = ZFCP_ULP_INFO_VERSION; - fsf_req->qtcb->prefix.qtcb_type = fsf_qtcb_type[fsf_cmd]; - fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION; - fsf_req->qtcb->header.req_handle = (unsigned long) fsf_req; - fsf_req->qtcb->header.fsf_command = fsf_cmd; - /* - * Request Sequence Number is set later when the request is - * actually sent. - */ + if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP)) { + fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; } - /* - * try to get needed SBALs in request queue (get queue lock on success) - */ - ZFCP_LOG_TRACE("try to get free BUFFER in request queue\n"); - if (unlikely(req_flags & ZFCP_WAIT_FOR_SBAL)) { - timeout = ZFCP_SBAL_TIMEOUT; - ZFCP_WAIT_EVENT_TIMEOUT(adapter->request_wq, - timeout, - (condition = - (zfcp_fsf_req_create_sbal_check) - (lock_flags, req_queue, 1))); - if (!condition) { - retval = -EIO; - goto failed_sbals; - } - } else { - if (!zfcp_fsf_req_create_sbal_check(lock_flags, req_queue, 1)) { - retval = -EIO; - goto failed_sbals; - } - } - fsf_req->sbal_count = 1; - fsf_req->sbal_index = req_queue->free_index; + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); - ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n", - fsf_req->sbal_count, fsf_req->sbal_index); - buffere = req_queue->buffer[fsf_req->sbal_index]->element; /* setup common SBALE fields */ - buffere[0].addr = fsf_req; - buffere[0].flags |= SBAL_FLAGS0_COMMAND; - if (likely(fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS)) { - buffere[1].addr = (void *) fsf_req->qtcb; - buffere[1].length = ZFCP_QTCB_SIZE; + sbale[0].addr = fsf_req; + sbale[0].flags |= SBAL_FLAGS0_COMMAND; + if (likely(fsf_req->qtcb != NULL)) { + sbale[1].addr = (void *) fsf_req->qtcb; + sbale[1].length = sizeof(struct fsf_qtcb); } - /* set specific common SBALE and QTCB fields */ - switch (fsf_cmd) { - case FSF_QTCB_FCP_CMND: - ZFCP_LOG_FLAGS(3, "FSF_QTCB_FCP_CMND\n"); - /* - * storage-block type depends on actual - * SCSI command and is set by calling - * routine according to transfer direction - * of data buffers associated with SCSI - * command - */ - break; - case FSF_QTCB_ABORT_FCP_CMND: - case FSF_QTCB_OPEN_PORT_WITH_DID: - case FSF_QTCB_OPEN_LUN: - case FSF_QTCB_CLOSE_LUN: - case FSF_QTCB_CLOSE_PORT: - case FSF_QTCB_CLOSE_PHYSICAL_PORT: - case FSF_QTCB_SEND_ELS: /* FIXME: ELS needs separate case */ - ZFCP_LOG_FLAGS(3, "FSF_QTCB_*\n"); - /* - * FIXME(qdio): - * what is the correct type for commands - * without 'real' data buffers? - */ - buffere[0].flags |= SBAL_FLAGS0_TYPE_READ; - buffere[1].flags |= SBAL_FLAGS_LAST_ENTRY; - break; - case FSF_QTCB_EXCHANGE_CONFIG_DATA: - ZFCP_LOG_FLAGS(3, "FSF_QTCB_EXCHANGE_CONFIG_DATA\n"); - buffere[0].flags |= SBAL_FLAGS0_TYPE_READ; - buffere[1].flags |= SBAL_FLAGS_LAST_ENTRY; - break; - - case FSF_QTCB_SEND_GENERIC: - ZFCP_LOG_FLAGS(3, "FSF_QTCB_SEND_GENERIC\n"); - buffere[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ; - break; - - case FSF_QTCB_UNSOLICITED_STATUS: - ZFCP_LOG_FLAGS(3, "FSF_QTCB_UNSOLICITED_STATUS\n"); - buffere[0].flags |= SBAL_FLAGS0_TYPE_STATUS; - buffere[2].flags |= SBAL_FLAGS_LAST_ENTRY; - break; - - default: - ZFCP_LOG_NORMAL("bug: An attempt to send an unsupported " - "command has been detected. " - "(debug info 0x%x)\n", fsf_cmd); - goto unsupported_fsf_cmd; - } - - /* yes, we did it - skip all cleanups for different failures */ - goto out; + ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n", + fsf_req->sbal_number, fsf_req->sbal_first); - unsupported_fsf_cmd: + goto success; failed_sbals: #ifdef ZFCP_STAT_QUEUES @@ -4101,9 +4973,9 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, failed_fsf_req: write_lock_irqsave(&req_queue->queue_lock, *lock_flags); - out: + success: *fsf_req_p = fsf_req; - return retval; + return ret; } /* @@ -4117,23 +4989,24 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, static int zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer) { - int retval = 0; - struct zfcp_adapter *adapter = fsf_req->adapter; - struct zfcp_qdio_queue *req_queue = &adapter->request_queue; - volatile struct qdio_buffer_element *buffere; - int inc_seq_no = 1; + struct zfcp_adapter *adapter; + struct zfcp_qdio_queue *req_queue; + volatile struct qdio_buffer_element *sbale; int new_distance_from_int; unsigned long flags; + int inc_seq_no = 1; + int retval = 0; + + adapter = fsf_req->adapter; + req_queue = &adapter->request_queue, - u8 sbal_index = fsf_req->sbal_index; /* FIXME(debug): remove it later */ - buffere = &(req_queue->buffer[sbal_index]->element[0]); - ZFCP_LOG_DEBUG("zeroeth BUFFERE flags=0x%x \n ", buffere->flags); - buffere = &(req_queue->buffer[sbal_index]->element[1]); - ZFCP_LOG_TRACE("HEX DUMP OF 0eth BUFFERE PAYLOAD:\n"); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) buffere->addr, - buffere->length); + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_first, 0); + ZFCP_LOG_DEBUG("SBALE0 flags=0x%x\n", sbale[0].flags); + ZFCP_LOG_TRACE("HEX DUMP OF SBALE1 PAYLOAD:\n"); + ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) sbale[1].addr, + sbale[1].length); /* set sequence counter in QTCB */ if (likely(fsf_req->qtcb)) { @@ -4168,24 +5041,22 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer) "index_in_queue=%i, count=%i, buffers=0x%lx\n", zfcp_get_busid_by_adapter(adapter), QDIO_FLAG_SYNC_OUTPUT, - 0, - sbal_index, - fsf_req->sbal_count, - (unsigned long) &req_queue->buffer[sbal_index]); + 0, fsf_req->sbal_first, fsf_req->sbal_number, + (unsigned long) &req_queue->buffer[fsf_req->sbal_first]); /* * adjust the number of free SBALs in request queue as well as * position of first one */ - atomic_sub(fsf_req->sbal_count, &req_queue->free_count); + atomic_sub(fsf_req->sbal_number, &req_queue->free_count); ZFCP_LOG_TRACE("free_count=%d\n", atomic_read(&req_queue->free_count)); - req_queue->free_index += fsf_req->sbal_count; /* increase */ + req_queue->free_index += fsf_req->sbal_number; /* increase */ req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap if needed */ new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req); retval = do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, - 0, fsf_req->sbal_index, fsf_req->sbal_count, NULL); + 0, fsf_req->sbal_first, fsf_req->sbal_number, NULL); if (unlikely(retval)) { /* Queues are down..... */ @@ -4204,9 +5075,9 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer) * position of first one */ zfcp_qdio_zero_sbals(req_queue->buffer, - fsf_req->sbal_index, fsf_req->sbal_count); - atomic_add(fsf_req->sbal_count, &req_queue->free_count); - req_queue->free_index -= fsf_req->sbal_count; /* increase */ + fsf_req->sbal_first, fsf_req->sbal_number); + atomic_add(fsf_req->sbal_number, &req_queue->free_count); + req_queue->free_index -= fsf_req->sbal_number; /* increase */ req_queue->free_index += QDIO_MAX_BUFFERS_PER_Q; req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */ ZFCP_LOG_DEBUG @@ -4270,35 +5141,4 @@ zfcp_fsf_req_cleanup(struct zfcp_fsf_req *fsf_req) zfcp_fsf_req_free(fsf_req); } -/* - * try to allocate fsf_req with QTCB, - * alternately try to get hold of fsf_req+QTCB provided by the specified memory - * pool element, this routine is called for all kinds of fsf requests other than - * status read since status read does neither require kmalloc involvement - * nor a QTCB - */ -static struct zfcp_fsf_req * -zfcp_fsf_req_get(int kmalloc_flags, mempool_t * pool) -{ - struct zfcp_fsf_req *fsf_req; - - fsf_req = kmalloc(ZFCP_QTCB_AND_REQ_SIZE, kmalloc_flags); - if (likely(fsf_req)) { - memset(fsf_req, 0, ZFCP_QTCB_AND_REQ_SIZE); - } else { - fsf_req = mempool_alloc(pool, kmalloc_flags); - if (likely(fsf_req)) { - memset(fsf_req, 0, ZFCP_QTCB_AND_REQ_SIZE); - fsf_req->status |= ZFCP_STATUS_FSFREQ_POOL; - } - } - if (likely(fsf_req)) - fsf_req->qtcb = - (struct fsf_qtcb *) ((unsigned long) fsf_req + - sizeof (struct zfcp_fsf_req)); - - return fsf_req; -} - #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 4f7c9365af35..d5f70386119b 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -4,11 +4,12 @@ * * FCP adapter driver for IBM eServer zSeries * - * Copyright 2002 IBM Corporation + * (C) Copyright IBM Corp. 2002, 2004 + * * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> - * Aron Zeh <arzeh@de.ibm.com> - * Wolfgang Taphorn <taphorn@de.ibm.com> + * Aron Zeh + * Wolfgang Taphorn * Stefan Bader <stefan.bader@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> * @@ -44,11 +45,22 @@ #define FSF_QTCB_SEND_ELS 0x0000000B #define FSF_QTCB_SEND_GENERIC 0x0000000C #define FSF_QTCB_EXCHANGE_CONFIG_DATA 0x0000000D +#define FSF_QTCB_EXCHANGE_PORT_DATA 0x0000000E +#define FSF_QTCB_DOWNLOAD_CONTROL_FILE 0x00000012 +#define FSF_QTCB_UPLOAD_CONTROL_FILE 0x00000013 /* FSF QTCB types */ #define FSF_IO_COMMAND 0x00000001 #define FSF_SUPPORT_COMMAND 0x00000002 #define FSF_CONFIG_COMMAND 0x00000003 +#define FSF_PORT_COMMAND 0x00000004 + +/* FSF control file upload/download operations' subtype and options */ +#define FSF_CFDC_OPERATION_SUBTYPE 0x00020001 +#define FSF_CFDC_OPTION_NORMAL_MODE 0x00000000 +#define FSF_CFDC_OPTION_FORCE 0x00000001 +#define FSF_CFDC_OPTION_FULL_ACCESS 0x00000002 +#define FSF_CFDC_OPTION_RESTRICTED_ACCESS 0x00000004 /* FSF protocol stati */ #define FSF_PROT_GOOD 0x00000001 @@ -71,9 +83,9 @@ #define FSF_HANDLE_MISMATCH 0x00000005 #define FSF_SERVICE_CLASS_NOT_SUPPORTED 0x00000006 #define FSF_FCPLUN_NOT_VALID 0x00000009 -//#define FSF_ACCESS_DENIED 0x00000010 +#define FSF_ACCESS_DENIED 0x00000010 #define FSF_ACCESS_TYPE_NOT_VALID 0x00000011 -#define FSF_LUN_IN_USE 0x00000012 +#define FSF_LUN_SHARING_VIOLATION 0x00000012 #define FSF_COMMAND_ABORTED_ULP 0x00000020 #define FSF_COMMAND_ABORTED_ADAPTER 0x00000021 #define FSF_FCP_COMMAND_DOES_NOT_EXIST 0x00000022 @@ -87,13 +99,24 @@ #define FSF_RESPONSE_BUF_NOT_VALID 0x00000043 #define FSF_ELS_COMMAND_REJECTED 0x00000050 #define FSF_GENERIC_COMMAND_REJECTED 0x00000051 -//#define FSF_AUTHORIZATION_FAILURE 0x00000053 +#define FSF_OPERATION_PARTIALLY_SUCCESSFUL 0x00000052 +#define FSF_AUTHORIZATION_FAILURE 0x00000053 +#define FSF_CFDC_ERROR_DETECTED 0x00000054 +#define FSF_CONTROL_FILE_UPDATE_ERROR 0x00000055 +#define FSF_CONTROL_FILE_TOO_LARGE 0x00000056 +#define FSF_ACCESS_CONFLICT_DETECTED 0x00000057 +#define FSF_CONFLICTS_OVERRULED 0x00000058 #define FSF_PORT_BOXED 0x00000059 -//#define FSF_LUN_BOXED 0x0000005A +#define FSF_LUN_BOXED 0x0000005A +#define FSF_PAYLOAD_SIZE_MISMATCH 0x00000060 +#define FSF_REQUEST_SIZE_TOO_LARGE 0x00000061 +#define FSF_RESPONSE_SIZE_TOO_LARGE 0x00000062 #define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD #define FSF_FCP_RSP_AVAILABLE 0x000000AF #define FSF_UNKNOWN_COMMAND 0x000000E2 -//#define FSF_ERROR 0x000000FF +#define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3 +#define FSF_INVALID_COMMAND_OPTION 0x000000E5 +/* #define FSF_ERROR 0x000000FF */ #define FSF_STATUS_QUALIFIER_SIZE 16 @@ -107,6 +130,15 @@ #define FSF_SQ_COMMAND_ABORTED 0x06 #define FSF_SQ_NO_RETRY_POSSIBLE 0x07 +/* FSF status qualifier for CFDC commands */ +#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE 0x00000001 +#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2 0x00000002 +/* CFDC subtable codes */ +#define FSF_SQ_CFDC_SUBTABLE_OS 0x0001 +#define FSF_SQ_CFDC_SUBTABLE_PORT_WWPN 0x0002 +#define FSF_SQ_CFDC_SUBTABLE_PORT_DID 0x0003 +#define FSF_SQ_CFDC_SUBTABLE_LUN 0x0004 + /* FSF status qualifier (most significant 4 bytes), local link down */ #define FSF_PSQ_LINK_NOLIGHT 0x00000004 #define FSF_PSQ_LINK_WRAPPLUG 0x00000008 @@ -124,11 +156,20 @@ #define FSF_STATUS_READ_BIT_ERROR_THRESHOLD 0x00000004 #define FSF_STATUS_READ_LINK_DOWN 0x00000005 /* FIXME: really? */ #define FSF_STATUS_READ_LINK_UP 0x00000006 +#define FSF_STATUS_READ_NOTIFICATION_LOST 0x00000009 +#define FSF_STATUS_READ_CFDC_UPDATED 0x0000000A +#define FSF_STATUS_READ_CFDC_HARDENED 0x0000000B /* status subtypes in status read buffer */ #define FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT 0x00000001 #define FSF_STATUS_READ_SUB_ERROR_PORT 0x00000002 +/* status subtypes for CFDC */ +#define FSF_STATUS_READ_SUB_LOST_CFDC_UPDATED 0x00000020 +#define FSF_STATUS_READ_SUB_LOST_CFDC_HARDENED 0x00000040 +#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE 0x00000002 +#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2 0x0000000F + /* topologie that is detected by the adapter */ #define FSF_TOPO_ERROR 0x00000000 #define FSF_TOPO_P2P 0x00000001 @@ -149,10 +190,48 @@ /* SBAL chaining */ #define FSF_MAX_SBALS_PER_REQ 36 +#define FSF_MAX_SBALS_PER_ELS_REQ 2 /* logging space behind QTCB */ #define FSF_QTCB_LOG_SIZE 1024 +/* channel features */ +#define FSF_FEATURE_QTCB_SUPPRESSION 0x00000001 +#define FSF_FEATURE_CFDC 0x00000002 +#define FSF_FEATURE_SENSEDATA_REPLICATION 0x00000004 +#define FSF_FEATURE_LOST_SAN_NOTIFICATION 0x00000008 +#define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010 +#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 + +/* option */ +#define FSF_OPEN_LUN_SUPPRESS_BOXING 0x00000001 + +/* adapter types */ +#define FSF_ADAPTER_TYPE_FICON 0x00000001 +#define FSF_ADAPTER_TYPE_FICON_EXPRESS 0x00000002 + +/* port types */ +#define FSF_HBA_PORTTYPE_UNKNOWN 0x00000001 +#define FSF_HBA_PORTTYPE_NOTPRESENT 0x00000003 +#define FSF_HBA_PORTTYPE_NPORT 0x00000005 +#define FSF_HBA_PORTTYPE_PTP 0x00000021 +/* following are not defined and used by FSF Spec + but are additionally defined by FC-HBA */ +#define FSF_HBA_PORTTYPE_OTHER 0x00000002 +#define FSF_HBA_PORTTYPE_NOTPRESENT 0x00000003 +#define FSF_HBA_PORTTYPE_NLPORT 0x00000006 +#define FSF_HBA_PORTTYPE_FLPORT 0x00000007 +#define FSF_HBA_PORTTYPE_FPORT 0x00000008 +#define FSF_HBA_PORTTYPE_LPORT 0x00000020 + +/* port states */ +#define FSF_HBA_PORTSTATE_UNKNOWN 0x00000001 +#define FSF_HBA_PORTSTATE_ONLINE 0x00000002 +#define FSF_HBA_PORTSTATE_OFFLINE 0x00000003 +#define FSF_HBA_PORTSTATE_LINKDOWN 0x00000006 +#define FSF_HBA_PORTSTATE_ERROR 0x00000007 + + struct fsf_queue_designator; struct fsf_status_read_buffer; struct fsf_port_closed_payload; @@ -307,49 +386,92 @@ struct fsf_qtcb_bottom_io { } __attribute__ ((packed)); struct fsf_qtcb_bottom_support { - u8 res1[16]; + u32 operation_subtype; + u8 res1[12]; u32 d_id; - u32 res2; + u32 option; u64 fcp_lun; - u64 res3; + u64 res2; u64 req_handle; u32 service_class; - u8 res4[3]; + u8 res3[3]; u8 timeout; - u8 res5[184]; + u8 res4[184]; u32 els1_length; u32 els2_length; - u64 res6; + u32 req_buf_length; + u32 resp_buf_length; u8 els[256]; } __attribute__ ((packed)); struct fsf_qtcb_bottom_config { u32 lic_version; - u32 res1; + u32 feature_selection; u32 high_qtcb_version; u32 low_qtcb_version; u32 max_qtcb_size; - u8 res2[12]; + u32 max_data_transfer_size; + u32 supported_features; + u8 res1[4]; u32 fc_topology; u32 fc_link_speed; u32 adapter_type; u32 peer_d_id; - u8 res3[12]; + u8 res2[12]; u32 s_id; struct fsf_nport_serv_param nport_serv_param; - u8 res4[320]; + u8 res3[8]; + u32 adapter_ports; + u32 hardware_version; + u8 serial_number[32]; + u8 res4[272]; +} __attribute__ ((packed)); + +struct fsf_qtcb_bottom_port { + u8 res1[8]; + u32 fc_port_id; + u32 port_type; + u32 port_state; + u32 class_of_service; /* should be 0x00000006 for class 2 and 3 */ + u8 supported_fc4_types[32]; /* should be 0x00000100 for scsi fcp */ + u8 active_fc4_types[32]; + u32 supported_speed; /* 0x0001 for 1 GBit/s or 0x0002 for 2 GBit/s */ + u32 maximum_frame_size; /* fixed value of 2112 */ + u64 seconds_since_last_reset; + u64 tx_frames; + u64 tx_words; + u64 rx_frames; + u64 rx_words; + u64 lip; /* 0 */ + u64 nos; /* currently 0 */ + u64 error_frames; /* currently 0 */ + u64 dumped_frames; /* currently 0 */ + u64 link_failure; + u64 loss_of_sync; + u64 loss_of_signal; + u64 psp_error_counts; + u64 invalid_tx_words; + u64 invalid_crcs; + u64 input_requests; + u64 output_requests; + u64 control_requests; + u64 input_mb; /* where 1 MByte == 1.000.000 Bytes */ + u64 output_mb; /* where 1 MByte == 1.000.000 Bytes */ + u8 res2[256]; } __attribute__ ((packed)); union fsf_qtcb_bottom { struct fsf_qtcb_bottom_io io; struct fsf_qtcb_bottom_support support; struct fsf_qtcb_bottom_config config; + struct fsf_qtcb_bottom_port port; }; struct fsf_qtcb { struct fsf_qtcb_prefix prefix; struct fsf_qtcb_header header; union fsf_qtcb_bottom bottom; + u8 log[FSF_QTCB_LOG_SIZE]; } __attribute__ ((packed)); #endif /* FSF_H */ diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 97e667fbf51f..8ae02d969b38 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -5,11 +5,12 @@ * * QDIO related routines * - * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation + * (C) Copyright IBM Corp. 2002, 2004 + * * Authors: * Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> - * Wolfgang Taphorn <taphorn@de.ibm.com> + * Wolfgang Taphorn * Heiko Carstens <heiko.carstens@de.ibm.com> * * This program is free software; you can redistribute it and/or modify @@ -27,10 +28,28 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_QDIO_C_REVISION "$Revision: 1.10 $" +#define ZFCP_QDIO_C_REVISION "$Revision: 1.13 $" #include "zfcp_ext.h" +static inline void zfcp_qdio_sbal_limit(struct zfcp_fsf_req *, int); +static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_get + (struct zfcp_qdio_queue *, int, int); +static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_resp + (struct zfcp_fsf_req *, int, int); +static inline volatile struct qdio_buffer_element *zfcp_qdio_sbal_chain + (struct zfcp_fsf_req *, unsigned long); +static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_next + (struct zfcp_fsf_req *, unsigned long); +static inline int zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *, int, int); +static inline int zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *); +static inline void zfcp_qdio_sbale_fill + (struct zfcp_fsf_req *, unsigned long, void *, int); +static inline int zfcp_qdio_sbals_from_segment + (struct zfcp_fsf_req *, unsigned long, void *, unsigned long); +static inline int zfcp_qdio_sbals_from_buffer + (struct zfcp_fsf_req *, unsigned long, void *, unsigned long, int); + static qdio_handler_t zfcp_qdio_request_handler; static qdio_handler_t zfcp_qdio_response_handler; static int zfcp_qdio_handler_error_check(struct zfcp_adapter *, @@ -38,7 +57,6 @@ static int zfcp_qdio_handler_error_check(struct zfcp_adapter *, unsigned int, unsigned int); #define ZFCP_LOG_AREA ZFCP_LOG_AREA_QDIO -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_QDIO /* * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t @@ -318,7 +336,7 @@ zfcp_qdio_request_handler(struct ccw_device *ccw_device, atomic_add(elements_processed, &queue->free_count); ZFCP_LOG_DEBUG("free_count=%d\n", atomic_read(&queue->free_count)); wake_up(&adapter->request_wq); - ZFCP_LOG_DEBUG("Elements_processed = %d, free count=%d \n", + ZFCP_LOG_DEBUG("Elements_processed = %d, free count=%d\n", elements_processed, atomic_read(&queue->free_count)); out: return; @@ -365,7 +383,7 @@ zfcp_qdio_response_handler(struct ccw_device *ccw_device, */ buffere = &(queue->buffer[first_element]->element[0]); - ZFCP_LOG_DEBUG("first BUFFERE flags=0x%x \n ", buffere->flags); + ZFCP_LOG_DEBUG("first BUFFERE flags=0x%x \n", buffere->flags); /* * go through all SBALs from input queue currently * returned by QDIO layer @@ -516,8 +534,8 @@ zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, void *sbale_addr) (unsigned long) fsf_req, (unsigned long) fsf_req->qtcb); if (likely(fsf_req->qtcb)) { ZFCP_LOG_TRACE("HEX DUMP OF 1ST BUFFERE PAYLOAD (QTCB):\n"); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, - (char *) fsf_req->qtcb, ZFCP_QTCB_SIZE); + ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) fsf_req->qtcb, + sizeof(struct fsf_qtcb)); } /* finish the FSF request */ @@ -526,24 +544,346 @@ zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, void *sbale_addr) return retval; } +/** + * zfcp_qdio_sbale_get - return pointer to SBALE of qdio_queue + * @queue: queue from which SBALE should be returned + * @sbal: specifies number of SBAL in queue + * @sbale: specifes number of SBALE in SBAL + */ +static inline volatile struct qdio_buffer_element * +zfcp_qdio_sbale_get(struct zfcp_qdio_queue *queue, int sbal, int sbale) +{ + return &queue->buffer[sbal]->element[sbale]; +} + +/** + * zfcp_qdio_sbale_req - return pointer to SBALE of request_queue for + * a struct zfcp_fsf_req + */ +inline volatile struct qdio_buffer_element * +zfcp_qdio_sbale_req(struct zfcp_fsf_req *fsf_req, int sbal, int sbale) +{ + return zfcp_qdio_sbale_get(&fsf_req->adapter->request_queue, + sbal, sbale); +} + +/** + * zfcp_qdio_sbale_resp - return pointer to SBALE of response_queue for + * a struct zfcp_fsf_req + */ +static inline volatile struct qdio_buffer_element * +zfcp_qdio_sbale_resp(struct zfcp_fsf_req *fsf_req, int sbal, int sbale) +{ + return zfcp_qdio_sbale_get(&fsf_req->adapter->response_queue, + sbal, sbale); +} + +/** + * zfcp_qdio_sbale_curr - return current SBALE on request_queue for + * a struct zfcp_fsf_req + */ +inline volatile struct qdio_buffer_element * +zfcp_qdio_sbale_curr(struct zfcp_fsf_req *fsf_req) +{ + return zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, + fsf_req->sbale_curr); +} + +/** + * zfcp_qdio_sbal_limit - determine maximum number of SBALs that can be used + * on the request_queue for a struct zfcp_fsf_req + * @fsf_req: the number of the last SBAL that can be used is stored herein + * @max_sbals: used to pass an upper limit for the number of SBALs + * + * Note: We can assume at least one free SBAL in the request_queue when called. + */ +static inline void +zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals) +{ + int count = atomic_read(&fsf_req->adapter->request_queue.free_count); + count = min(count, max_sbals); + fsf_req->sbal_last = fsf_req->sbal_first; + fsf_req->sbal_last += (count - 1); + fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q; +} + +/** + * zfcp_qdio_sbal_chain - chain SBALs if more than one SBAL is needed for a + * request + * @fsf_req: zfcp_fsf_req to be processed + * @sbtype: SBAL flags which have to be set in first SBALE of new SBAL + * + * This function changes sbal_curr, sbale_curr, sbal_number of fsf_req. + */ +static inline volatile struct qdio_buffer_element * +zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype) +{ + volatile struct qdio_buffer_element *sbale; + + /* set last entry flag in current SBALE of current SBAL */ + sbale = zfcp_qdio_sbale_curr(fsf_req); + sbale->flags |= SBAL_FLAGS_LAST_ENTRY; + + /* don't exceed last allowed SBAL */ + if (fsf_req->sbal_curr == fsf_req->sbal_last) + return NULL; + + /* set chaining flag in first SBALE of current SBAL */ + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + sbale->flags |= SBAL_FLAGS0_MORE_SBALS; + + /* calculate index of next SBAL */ + fsf_req->sbal_curr++; + fsf_req->sbal_curr %= QDIO_MAX_BUFFERS_PER_Q; + + /* keep this requests number of SBALs up-to-date */ + fsf_req->sbal_number++; + + /* start at first SBALE of new SBAL */ + fsf_req->sbale_curr = 0; + + /* set storage-block type for new SBAL */ + sbale = zfcp_qdio_sbale_curr(fsf_req); + sbale->flags |= sbtype; + + return sbale; +} + +/** + * zfcp_qdio_sbale_next - switch to next SBALE, chain SBALs if needed + */ +static inline volatile struct qdio_buffer_element * +zfcp_qdio_sbale_next(struct zfcp_fsf_req *fsf_req, unsigned long sbtype) +{ + if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL) + return zfcp_qdio_sbal_chain(fsf_req, sbtype); + + fsf_req->sbale_curr++; + + return zfcp_qdio_sbale_curr(fsf_req); +} + +/** + * zfcp_qdio_sbals_zero - initialize SBALs between first and last in queue + * with zero from + */ +static inline int +zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *queue, int first, int last) +{ + struct qdio_buffer **buf = queue->buffer; + int curr = first; + int count = 0; + + for(;;) { + curr %= QDIO_MAX_BUFFERS_PER_Q; + count++; + memset(buf[curr], 0, sizeof(struct qdio_buffer)); + if (curr == last) + break; + curr++; + } + return count; +} + + +/** + * zfcp_qdio_sbals_wipe - reset all changes in SBALs for an fsf_req + */ +static inline int +zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req) +{ + return zfcp_qdio_sbals_zero(&fsf_req->adapter->request_queue, + fsf_req->sbal_first, fsf_req->sbal_curr); +} + + +/** + * zfcp_qdio_sbale_fill - set address and lenght in current SBALE + * on request_queue + */ +static inline void +zfcp_qdio_sbale_fill(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, + void *addr, int length) +{ + volatile struct qdio_buffer_element *sbale; + + sbale = zfcp_qdio_sbale_curr(fsf_req); + sbale->addr = addr; + sbale->length = length; + +#ifdef ZFCP_STAT_REQSIZES + if (sbtype == SBAL_FLAGS0_TYPE_READ) + zfcp_statistics_inc(&zfcp_data.read_sg_head, length); + else zfcp_statistics_inc(&zfcp_data.write_sg_head, length); +#endif +} + +/** + * zfcp_qdio_sbals_from_segment - map memory segment to SBALE(s) + * @fsf_req: request to be processed + * @sbtype: SBALE flags + * @start_addr: address of memory segment + * @total_length: length of memory segment + * + * Alignment and length of the segment determine how many SBALEs are needed + * for the memory segment. + */ +static inline int +zfcp_qdio_sbals_from_segment(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, + void *start_addr, unsigned long total_length) +{ + unsigned long remaining, length; + void *addr; + + /* split segment up heeding page boundaries */ + for (addr = start_addr, remaining = total_length; remaining > 0; + addr += length, remaining -= length) { + /* get next free SBALE for new piece */ + if (NULL == zfcp_qdio_sbale_next(fsf_req, sbtype)) { + /* no SBALE left, clean up and leave */ + zfcp_qdio_sbals_wipe(fsf_req); + return -EINVAL; + } + /* calculate length of new piece */ + length = min(remaining, + (PAGE_SIZE - ((unsigned long) addr & + (PAGE_SIZE - 1)))); + /* fill current SBALE with calculated piece */ + zfcp_qdio_sbale_fill(fsf_req, sbtype, addr, length); + } + return total_length; +} + + +/** + * zfcp_qdio_sbals_from_sg - fill SBALs from scatter-gather list + * @fsf_req: request to be processed + * @sbtype: SBALE flags + * @sg: scatter-gather list + * @sg_count: number of elements in scatter-gather list + * @max_sbals: upper bound for number of SBALs to be used + */ +inline int +zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, + struct scatterlist *sg, int sg_count, int max_sbals) +{ + int sg_index; + struct scatterlist *sg_segment; + int retval; + volatile struct qdio_buffer_element *sbale; + int bytes = 0; + + /* figure out last allowed SBAL */ + zfcp_qdio_sbal_limit(fsf_req, max_sbals); + + /* set storage-block type for current SBAL */ + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + sbale->flags |= sbtype; + + /* process all segements of scatter-gather list */ + for (sg_index = 0, sg_segment = sg, bytes = 0; + sg_index < sg_count; + sg_index++, sg_segment++) { + retval = zfcp_qdio_sbals_from_segment( + fsf_req, + sbtype, + zfcp_sg_to_address(sg_segment), + sg_segment->length); + if (retval < 0) { + bytes = retval; + goto out; + } else + bytes += retval; + } + /* assume that no other SBALEs are to follow in the same SBAL */ + sbale = zfcp_qdio_sbale_curr(fsf_req); + sbale->flags |= SBAL_FLAGS_LAST_ENTRY; + +out: +#ifdef ZFCP_STAT_REQSIZES + if (sbtype == SBAL_FLAGS0_TYPE_READ) { + zfcp_statistics_inc(&zfcp_data.read_sguse_head, sg_count); + zfcp_statistics_inc(&zfcp_data.read_req_head, bytes); + } else { + zfcp_statistics_inc(&zfcp_data.write_sguse_head, sg_count); + zfcp_statistics_inc(&zfcp_data.write_req_head, bytes); + } +#endif + + return bytes; +} + + +/** + * zfcp_qdio_sbals_from_buffer - fill SBALs from buffer + * @fsf_req: request to be processed + * @sbtype: SBALE flags + * @buffer: data buffer + * @length: length of buffer + * @max_sbals: upper bound for number of SBALs to be used + */ +static inline int +zfcp_qdio_sbals_from_buffer(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, + void *buffer, unsigned long length, int max_sbals) +{ + struct scatterlist sg_segment; + + zfcp_address_to_sg(buffer, &sg_segment); + sg_segment.length = length; + + return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, &sg_segment, 1, + max_sbals); +} + + +/** + * zfcp_qdio_sbals_from_scsicmnd - fill SBALs from scsi command + * @fsf_req: request to be processed + * @sbtype: SBALE flags + * @scsi_cmnd: either scatter-gather list or buffer contained herein is used + * to fill SBALs + */ +inline int +zfcp_qdio_sbals_from_scsicmnd(struct zfcp_fsf_req *fsf_req, + unsigned long sbtype, struct scsi_cmnd *scsi_cmnd) +{ + if (scsi_cmnd->use_sg) { + return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, + (struct scatterlist *) + scsi_cmnd->request_buffer, + scsi_cmnd->use_sg, + ZFCP_MAX_SBALS_PER_REQ); + } else { + return zfcp_qdio_sbals_from_buffer(fsf_req, sbtype, + scsi_cmnd->request_buffer, + scsi_cmnd->request_bufflen, + ZFCP_MAX_SBALS_PER_REQ); + } +} + +/** + * zfcp_qdio_determine_pci - set PCI flag in first SBALE on qdio queue if needed + */ int zfcp_qdio_determine_pci(struct zfcp_qdio_queue *req_queue, struct zfcp_fsf_req *fsf_req) { int new_distance_from_int; int pci_pos; + volatile struct qdio_buffer_element *sbale; new_distance_from_int = req_queue->distance_from_int + - fsf_req->sbal_count; + fsf_req->sbal_number; + if (unlikely(new_distance_from_int >= ZFCP_QDIO_PCI_INTERVAL)) { new_distance_from_int %= ZFCP_QDIO_PCI_INTERVAL; - pci_pos = fsf_req->sbal_index; - pci_pos += fsf_req->sbal_count; + pci_pos = fsf_req->sbal_first; + pci_pos += fsf_req->sbal_number; pci_pos -= new_distance_from_int; pci_pos -= 1; pci_pos %= QDIO_MAX_BUFFERS_PER_Q; - req_queue->buffer[pci_pos]->element[0].flags |= SBAL_FLAGS0_PCI; - ZFCP_LOG_TRACE("Setting PCI flag at pos %d\n", pci_pos); + sbale = zfcp_qdio_sbale_req(fsf_req, pci_pos, 0); + sbale->flags |= SBAL_FLAGS0_PCI; } return new_distance_from_int; } @@ -570,4 +910,3 @@ zfcp_qdio_zero_sbals(struct qdio_buffer *buf[], int first, int clean_count) } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 5a15a65694c4..b72851365a87 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -4,11 +4,12 @@ * * FCP adapter driver for IBM eServer zSeries * - * Copyright 2002 IBM Corporation + * (C) Copyright IBM Corp. 2002, 2004 + * * Author(s): Martin Peschke <mpeschke@de.ibm.com> * Raimund Schroeder <raimund.schroeder@de.ibm.com> - * Aron Zeh <arzeh@de.ibm.com> - * Wolfgang Taphorn <taphorn@de.ibm.com> + * Aron Zeh + * Wolfgang Taphorn * Stefan Bader <stefan.bader@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> * @@ -28,9 +29,9 @@ */ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_SCSI + /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_SCSI_REVISION "$Revision: 1.42 $" +#define ZFCP_SCSI_REVISION "$Revision: 1.52 $" #include <linux/blkdev.h> @@ -39,24 +40,14 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdp); static int zfcp_scsi_slave_alloc(struct scsi_device *sdp); static int zfcp_scsi_slave_configure(struct scsi_device *sdp); -static int zfcp_scsi_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); -static int zfcp_scsi_eh_abort_handler(Scsi_Cmnd *); -static int zfcp_scsi_eh_device_reset_handler(Scsi_Cmnd *); -static int zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd *); -static int zfcp_scsi_eh_host_reset_handler(Scsi_Cmnd *); +static int zfcp_scsi_queuecommand(struct scsi_cmnd *, + void (*done) (struct scsi_cmnd *)); +static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *); +static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *); +static int zfcp_scsi_eh_bus_reset_handler(struct scsi_cmnd *); +static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *); static int zfcp_task_management_function(struct zfcp_unit *, u8); -static int zfcp_create_sbales_from_segment(unsigned long, int, int *, - int, int, int *, int *, int, - int, struct qdio_buffer **, - char); - -static int zfcp_create_sbale(unsigned long, int, int *, int, int, int *, - int, int, int *, struct qdio_buffer **, - char); - -static struct zfcp_unit *zfcp_scsi_determine_unit(struct zfcp_adapter *, - Scsi_Cmnd *); static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, int, int); static struct device_attribute *zfcp_sysfs_sdev_attrs[]; @@ -225,59 +216,7 @@ zfcp_scsi_slave_destroy(struct scsi_device *sdpnt) } } -void -zfcp_scsi_block_requests(struct Scsi_Host *shpnt) -{ - scsi_block_requests(shpnt); - /* This is still somewhat racy but the best I could imagine */ - do { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(ZFCP_SCSI_HOST_FLUSH_TIMEOUT); - - } while (shpnt->host_busy || shpnt->eh_active); -} - /* - * Tries to associate a zfcp unit with the scsi device. - * - * returns: unit pointer if unit is found - * NULL otherwise - */ -struct zfcp_unit * -zfcp_scsi_determine_unit(struct zfcp_adapter *adapter, Scsi_Cmnd * scpnt) -{ - struct zfcp_unit *unit; - - /* - * figure out target device - * (stored there by zfcp_scsi_slave_alloc) - * ATTENTION: assumes hostdata initialized to NULL by - * mid layer (see scsi_scan.c) - */ - unit = (struct zfcp_unit *) scpnt->device->hostdata; - if (!unit) { - ZFCP_LOG_DEBUG("logical unit (%i %i %i %i) not configured\n", - scpnt->device->host->host_no, - scpnt->device->channel, - scpnt->device->id, scpnt->device->lun); - /* - * must fake SCSI command execution and scsi_done - * callback for non-configured logical unit - */ - /* return this as long as we are unable to process requests */ - set_host_byte(&scpnt->result, DID_NO_CONNECT); - zfcp_cmd_dbf_event_scsi("notconf", scpnt); - scpnt->scsi_done(scpnt); -#ifdef ZFCP_DEBUG_REQUESTS - debug_text_event(adapter->req_dbf, 2, "nc_done:"); - debug_event(adapter->req_dbf, 2, &scpnt, - sizeof (unsigned long)); -#endif /* ZFCP_DEBUG_REQUESTS */ - } - return unit; -} - -/* * called from scsi midlayer to allow finetuning of a device. */ static int @@ -290,124 +229,143 @@ zfcp_scsi_slave_configure(struct scsi_device *sdp) return 0; } -/* Complete a command immediately handing back DID_ERROR */ +/** + * zfcp_scsi_command_fail - set result in scsi_cmnd and call scsi_done function + * @scpnt: pointer to struct scsi_cmnd where result is set + * @result: result to be set in scpnt (e.g. DID_ERROR) + */ static void -zfcp_scsi_queuecommand_stop(Scsi_Cmnd * scpnt, - struct zfcp_adapter *adapter, - struct zfcp_unit *unit) +zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result) { - /* Always pass through to upper layer */ - scpnt->retries = scpnt->allowed - 1; - set_host_byte(&scpnt->result, DID_ERROR); - zfcp_cmd_dbf_event_scsi("stopping", scpnt); + set_host_byte(&scpnt->result, result); + zfcp_cmd_dbf_event_scsi("failing", scpnt); /* return directly */ scpnt->scsi_done(scpnt); - if (adapter && unit) { - ZFCP_LOG_INFO("Stopping SCSI IO on the unit with FCP LUN 0x%Lx " - "connected to the port with WWPN 0x%Lx at the " - "adapter %s.\n", - unit->fcp_lun, - unit->port->wwpn, - zfcp_get_busid_by_adapter(adapter)); -#ifdef ZFCP_DEBUG_REQUESTS - debug_text_event(adapter->req_dbf, 2, "de_done:"); - debug_event(adapter->req_dbf, 2, &scpnt, - sizeof (unsigned long)); -#endif /* ZFCP_DEBUG_REQUESTS */ - } else { - ZFCP_LOG_INFO("There is no adapter registered in the zfcp " - "module for the SCSI host with hostnumber %d. " - "Stopping IO.\n", scpnt->device->host->host_no); - } } -/* - * function: zfcp_scsi_queuecommand - * - * purpose: enqueues a SCSI command to the specified target device - * - * note: The scsi_done midlayer function may be called directly from - * within queuecommand provided queuecommand returns with - * success (0). - * If it fails, it is expected that the command could not be sent - * and is still available for processing. - * As we ensure that queuecommand never fails, we have the choice - * to call done directly wherever we please. - * Thus, any kind of send errors other than those indicating - * 'infinite' retries will be reported directly. - * Retry requests are put into a list to be processed under timer - * control once in a while to allow for other operations to - * complete in the meantime. +/** + * zfcp_scsi_command_async - worker for zfcp_scsi_queuecommand and + * zfcp_scsi_command_sync + * @adapter: adapter for where scsi command is issued + * @unit: unit to which scsi command is sent + * @scpnt: scsi command to be sent * - * returns: 0 - success, SCSI command enqueued - * !0 - failure, note that we never allow this to happen as the - * SCSI stack would block indefinitely should a non-zero return - * value be reported if there are no outstanding commands - * (as in when the queues are down) + * Note: In scsi_done function must be set in scpnt. */ int -zfcp_scsi_queuecommand(Scsi_Cmnd * scpnt, void (*done) (Scsi_Cmnd *)) +zfcp_scsi_command_async(struct zfcp_adapter *adapter, struct zfcp_unit *unit, + struct scsi_cmnd *scpnt) { + int tmp; int retval; - int temp_ret; - struct zfcp_unit *unit; - struct zfcp_adapter *adapter; retval = 0; - /* reset the status for this request */ - scpnt->result = 0; - /* save address of mid layer call back function */ - scpnt->scsi_done = done; - /* - * figure out adapter - * (previously stored there by the driver when - * the adapter was registered) - */ - adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0]; - /* NULL when the adapter was removed from the zfcp list */ - if (unlikely(adapter == NULL)) { - zfcp_scsi_queuecommand_stop(scpnt, NULL, NULL); - goto out; - } - unit = zfcp_scsi_determine_unit(adapter, scpnt); - if (unlikely(unit == NULL)) + BUG_ON((adapter == NULL) || (adapter != unit->port->adapter)); + BUG_ON(scpnt->scsi_done == NULL); + + if (unlikely(NULL == unit)) { + zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT); goto out; + } if (unlikely( atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status) || !atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status))) { - zfcp_scsi_queuecommand_stop(scpnt, adapter, unit); + ZFCP_LOG_DEBUG("Stopping SCSI IO on the unit with " + "FCP LUN 0x%Lx connected to the port " + "with WWPN 0x%Lx at the adapter %s.\n", + unit->fcp_lun, + unit->port->wwpn, + zfcp_get_busid_by_adapter(adapter)); + zfcp_scsi_command_fail(scpnt, DID_ERROR); goto out; } + if (unlikely( !atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status))) { ZFCP_LOG_DEBUG("adapter %s not ready or unit with LUN 0x%Lx " "on the port with WWPN 0x%Lx in recovery.\n", - zfcp_get_busid_by_adapter(adapter), + zfcp_get_busid_by_unit(unit), unit->fcp_lun, unit->port->wwpn); retval = SCSI_MLQUEUE_DEVICE_BUSY; goto out; } - temp_ret = zfcp_fsf_send_fcp_command_task(adapter, - unit, - scpnt, ZFCP_REQ_AUTO_CLEANUP); + tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, + ZFCP_REQ_AUTO_CLEANUP); - if (unlikely(temp_ret < 0)) { + if (unlikely(tmp < 0)) { ZFCP_LOG_DEBUG("error: Could not send a Send FCP Command\n"); retval = SCSI_MLQUEUE_HOST_BUSY; } else { + #ifdef ZFCP_DEBUG_REQUESTS debug_text_event(adapter->req_dbf, 3, "q_scpnt"); debug_event(adapter->req_dbf, 3, &scpnt, sizeof (unsigned long)); #endif /* ZFCP_DEBUG_REQUESTS */ } - out: + +out: return retval; } +void +zfcp_scsi_command_sync_handler(struct scsi_cmnd *scpnt) +{ + struct completion *wait = (struct completion *) scpnt->SCp.ptr; + complete(wait); +} + + +/** + * zfcp_scsi_command_sync - send a SCSI command and wait for completion + * returns 0, errors are indicated by scsi_cmnd->result + */ +int +zfcp_scsi_command_sync(struct zfcp_unit *unit, struct scsi_cmnd *scpnt) +{ + DECLARE_COMPLETION(wait); + + scpnt->SCp.ptr = (void *) &wait; /* silent re-use */ + scpnt->done = zfcp_scsi_command_sync_handler; + zfcp_scsi_command_async(unit->port->adapter, unit, scpnt); + wait_for_completion(&wait); + + return 0; +} + +/* + * function: zfcp_scsi_queuecommand + * + * purpose: enqueues a SCSI command to the specified target device + * + * returns: 0 - success, SCSI command enqueued + * !0 - failure + */ +int +zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt, + void (*done) (struct scsi_cmnd *)) +{ + struct zfcp_unit *unit; + struct zfcp_adapter *adapter; + + /* reset the status for this request */ + scpnt->result = 0; + /* save address of mid layer call back function */ + scpnt->scsi_done = done; + + /* + * figure out adapter and target device + * (stored there by zfcp_scsi_slave_alloc) + */ + adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0]; + unit = (struct zfcp_unit *) scpnt->device->hostdata; + + return zfcp_scsi_command_async(adapter, unit, scpnt); +} + /* * function: zfcp_unit_lookup * @@ -456,22 +414,18 @@ zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, int id, int lun) * FAILED - otherwise */ int -zfcp_scsi_eh_abort_handler(Scsi_Cmnd * scpnt) +zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) { int retval = SUCCESS; struct zfcp_fsf_req *new_fsf_req, *old_fsf_req; - struct zfcp_adapter *adapter; - struct zfcp_unit *unit; - struct zfcp_port *port; - struct Scsi_Host *scsi_host; + struct zfcp_adapter *adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0]; + struct zfcp_unit *unit = (struct zfcp_unit *) scpnt->device->hostdata; + struct zfcp_port *port = unit->port; + struct Scsi_Host *scsi_host = scpnt->device->host; union zfcp_req_data *req_data = NULL; unsigned long flags; u32 status = 0; - adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0]; - scsi_host = scpnt->device->host; - unit = (struct zfcp_unit *) scpnt->device->hostdata; - port = unit->port; #ifdef ZFCP_DEBUG_ABORTS /* the components of a abort_dbf record (fixed size record) */ @@ -657,7 +611,7 @@ zfcp_scsi_eh_abort_handler(Scsi_Cmnd * scpnt) * returns: */ int -zfcp_scsi_eh_device_reset_handler(Scsi_Cmnd * scpnt) +zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) { int retval; struct zfcp_unit *unit = (struct zfcp_unit *) scpnt->device->hostdata; @@ -764,7 +718,7 @@ zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags) * returns: */ int -zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd * scpnt) +zfcp_scsi_eh_bus_reset_handler(struct scsi_cmnd *scpnt) { int retval = 0; struct zfcp_unit *unit; @@ -793,7 +747,7 @@ zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd * scpnt) * returns: */ int -zfcp_scsi_eh_host_reset_handler(Scsi_Cmnd * scpnt) +zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) { int retval = 0; struct zfcp_unit *unit; @@ -887,332 +841,6 @@ zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter) } -/** - * zfcp_create_sbales_from_segment - creates SBALEs - * @addr: begin of this buffer segment - * @length_seg: length of this buffer segment - * @length_total: total length of buffer - * @length_min: roll back if generated buffer smaller than this - * @length_max: sum of all SBALEs (count) not larger than this - * @buffer_index: position of current BUFFER - * @buffere_index: position of current BUFFERE - * @buffer_first: first BUFFER used for this buffer - * @buffer_last: last BUFFER in request queue allowed - * @buffer: begin of SBAL array of request queue - * @sbtype: storage-block type - */ -static int -zfcp_create_sbales_from_segment(unsigned long addr, int length_seg, - int *length_total, int length_min, - int length_max, int *buffer_index, - int *buffere_index, int buffer_first, - int buffer_last, struct qdio_buffer *buffer[], - char sbtype) -{ - int retval = 0; - int length = 0; - - ZFCP_LOG_TRACE - ("SCSI data buffer segment with %i bytes from 0x%lx to 0x%lx\n", - length_seg, addr, (addr + length_seg) - 1); - - if (!length_seg) - goto out; - - if (addr & (PAGE_SIZE - 1)) { - length = - min((int) (PAGE_SIZE - (addr & (PAGE_SIZE - 1))), - length_seg); - ZFCP_LOG_TRACE - ("address 0x%lx not on page boundary, length=0x%x\n", - (unsigned long) addr, length); - retval = - zfcp_create_sbale(addr, length, length_total, length_min, - length_max, buffer_index, buffer_first, - buffer_last, buffere_index, buffer, - sbtype); - if (retval) { - /* no resources */ - goto out; - } - addr += length; - length = length_seg - length; - } else - length = length_seg; - - while (length > 0) { - retval = zfcp_create_sbale(addr, min((int) PAGE_SIZE, length), - length_total, length_min, length_max, - buffer_index, buffer_first, - buffer_last, buffere_index, buffer, - sbtype); - if (*buffere_index > ZFCP_LAST_SBALE_PER_SBAL) - ZFCP_LOG_NORMAL("bug: Filling output buffers with SCSI " - "data failed. Index ran out of bounds. " - "(debug info %d)\n", *buffere_index); - if (retval) { - /* no resources */ - goto out; - } - length -= PAGE_SIZE; - addr += PAGE_SIZE; - } - out: - return retval; -} - -/** - * zfcp_create_sbale - creates a single SBALE - * @addr: begin of this buffer segment - * @length: length of this buffer segment - * @length_total: total length of buffer - * @length_min: roll back if generated buffer smaller than this - * @length_max: sum of all SBALEs (count) not larger than this - * @buffer_index: position of current BUFFER - * @buffer_first: first BUFFER used for this buffer - * @buffer_last: last BUFFER allowed for this buffer - * @buffere_index: position of current BUFFERE of current BUFFER - * @buffer: begin of SBAL array of request queue - * @sbtype: storage-block type - */ -static int -zfcp_create_sbale(unsigned long addr, int length, int *length_total, - int length_min, int length_max, int *buffer_index, - int buffer_first, int buffer_last, int *buffere_index, - struct qdio_buffer *buffer[], char sbtype) -{ - int retval = 0; - int length_real, residual; - int buffers_used; - - volatile struct qdio_buffer_element *buffere = - &(buffer[*buffer_index]->element[*buffere_index]); - - /* check whether we hit the limit */ - residual = length_max - *length_total; - if (residual == 0) { - ZFCP_LOG_TRACE("skip remaining %i bytes since length_max hit\n", - length); - goto out; - } - length_real = min(length, residual); - - /* - * figure out next BUFFERE - * (first BUFFERE of first BUFFER is skipped - - * this is ok since it is reserved for the QTCB) - */ - if (*buffere_index == ZFCP_LAST_SBALE_PER_SBAL) { - /* last BUFFERE in this BUFFER */ - buffere->flags |= SBAL_FLAGS_LAST_ENTRY; - /* need further BUFFER */ - if (*buffer_index == buffer_last) { - /* queue full or last allowed BUFFER */ - buffers_used = (buffer_last - buffer_first) + 1; - /* avoid modulo operation on negative value */ - buffers_used += QDIO_MAX_BUFFERS_PER_Q; - buffers_used %= QDIO_MAX_BUFFERS_PER_Q; - ZFCP_LOG_DEBUG("reached limit of number of BUFFERs " - "allowed for this request\n"); - /* FIXME (design) - This check is wrong and enforces the - * use of one SBALE less than possible - */ - if ((*length_total < length_min) - || (buffers_used < ZFCP_MAX_SBALS_PER_REQ)) { - ZFCP_LOG_DEBUG("Rolling back SCSI command as " - "there are insufficient buffers " - "to cover the minimum required " - "amount of data\n"); - /* - * roll back complete list of BUFFERs generated - * from the scatter-gather list associated - * with this SCSI command - */ - zfcp_qdio_zero_sbals(buffer, - buffer_first, - buffers_used); - *length_total = 0; - } else { - /* DEBUG */ - ZFCP_LOG_NORMAL("Not enough buffers available. " - "Can only transfer %i bytes of " - "data\n", - *length_total); - } - retval = -ENOMEM; - goto out; - } else { /* *buffer_index != buffer_last */ - /* chain BUFFERs */ - *buffere_index = 0; - buffere = - &(buffer[*buffer_index]->element[*buffere_index]); - buffere->flags |= SBAL_FLAGS0_MORE_SBALS; - (*buffer_index)++; - *buffer_index %= QDIO_MAX_BUFFERS_PER_Q; - buffere = - &(buffer[*buffer_index]->element[*buffere_index]); - buffere->flags |= sbtype; - ZFCP_LOG_DEBUG - ("Chaining previous BUFFER %i to BUFFER %i\n", - ((*buffer_index != - 0) ? *buffer_index - 1 : QDIO_MAX_BUFFERS_PER_Q - - 1), *buffer_index); - } - } else { /* *buffere_index != (QDIO_MAX_ELEMENTS_PER_BUFFER - 1) */ - (*buffere_index)++; - buffere = &(buffer[*buffer_index]->element[*buffere_index]); - } - - /* ok, found a place for this piece, put it there */ - buffere->addr = (void *) addr; - buffere->length = length_real; - -#ifdef ZFCP_STAT_REQSIZES - if (sbtype == SBAL_FLAGS0_TYPE_READ) - zfcp_statistics_inc(&zfcp_data.read_sg_head, length_real); - else - zfcp_statistics_inc(&zfcp_data.write_sg_head, length_real); -#endif - - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) addr, length_real); - ZFCP_LOG_TRACE("BUFFER no %i (0x%lx) BUFFERE no %i (0x%lx): BUFFERE " - "data addr 0x%lx, BUFFERE length %i, BUFFER type %i\n", - *buffer_index, - (unsigned long) &buffer[*buffer_index], *buffere_index, - (unsigned long) buffere, addr, length_real, sbtype); - *length_total += length_real; - out: - return retval; -} - -/* - * function: zfcp_create_sbals_from_sg - * - * purpose: walks through scatter-gather list of specified SCSI command - * and creates a corresponding list of SBALs - * - * returns: size of generated buffer in bytes - * - * context: - */ -int -zfcp_create_sbals_from_sg(struct zfcp_fsf_req *fsf_req, Scsi_Cmnd * scpnt, - char sbtype, /* storage-block type */ - int length_min, /* roll back if generated buffer */ - int buffer_max) /* max numbers of BUFFERs */ -{ - int length_total = 0; - int buffer_index = 0; - int buffer_last = 0; - int buffere_index = 1; /* elements 0 and 1 are req-id and qtcb */ - volatile struct qdio_buffer_element *buffere = NULL; - struct zfcp_qdio_queue *req_q = NULL; - int length_max = scpnt->request_bufflen; - - req_q = &fsf_req->adapter->request_queue; - - buffer_index = req_q->free_index; - buffer_last = req_q->free_index + - min(buffer_max, atomic_read(&req_q->free_count)) - 1; - buffer_last %= QDIO_MAX_BUFFERS_PER_Q; - - ZFCP_LOG_TRACE - ("total SCSI data buffer size is (scpnt->request_bufflen) %i\n", - scpnt->request_bufflen); - ZFCP_LOG_TRACE - ("BUFFERs from (buffer_index)%i to (buffer_last)%i available\n", - buffer_index, buffer_last); - ZFCP_LOG_TRACE("buffer_max=%d, req_q->free_count=%d\n", buffer_max, - atomic_read(&req_q->free_count)); - - if (scpnt->use_sg) { - int sg_index; - struct scatterlist *list - = (struct scatterlist *) scpnt->request_buffer; - - ZFCP_LOG_DEBUG("%i (scpnt->use_sg) scatter-gather segments\n", - scpnt->use_sg); - - // length_max+=0x2100; - -#ifdef ZFCP_STAT_REQSIZES - if (sbtype == SBAL_FLAGS0_TYPE_READ) - zfcp_statistics_inc(&zfcp_data.read_sguse_head, - scpnt->use_sg); - else - zfcp_statistics_inc(&zfcp_data.write_sguse_head, - scpnt->use_sg); -#endif - - for (sg_index = 0; sg_index < scpnt->use_sg; sg_index++, list++) - { - if (zfcp_create_sbales_from_segment( - (page_to_pfn (list->page) << PAGE_SHIFT) + - list->offset, - list->length, - &length_total, - length_min, - length_max, - &buffer_index, - &buffere_index, - req_q->free_index, - buffer_last, - req_q->buffer, - sbtype)) - break; - } - } else { - ZFCP_LOG_DEBUG("no scatter-gather list\n"); -#ifdef ZFCP_STAT_REQSIZES - if (sbtype == SBAL_FLAGS0_TYPE_READ) - zfcp_statistics_inc(&zfcp_data.read_sguse_head, 1); - else - zfcp_statistics_inc(&zfcp_data.write_sguse_head, 1); -#endif - zfcp_create_sbales_from_segment( - (unsigned long) scpnt->request_buffer, - scpnt->request_bufflen, - &length_total, - length_min, - length_max, - &buffer_index, - &buffere_index, - req_q->free_index, - buffer_last, - req_q->buffer, - sbtype); - } - - fsf_req->sbal_index = req_q->free_index; - - if (buffer_index >= fsf_req->sbal_index) { - fsf_req->sbal_count = (buffer_index - fsf_req->sbal_index) + 1; - } else { - fsf_req->sbal_count = - (QDIO_MAX_BUFFERS_PER_Q - fsf_req->sbal_index) + - buffer_index + 1; - } - /* HACK */ - if ((scpnt->request_bufflen != 0) && (length_total == 0)) - goto out; - -#ifdef ZFCP_STAT_REQSIZES - if (sbtype == SBAL_FLAGS0_TYPE_READ) - zfcp_statistics_inc(&zfcp_data.read_req_head, length_total); - else - zfcp_statistics_inc(&zfcp_data.write_req_head, length_total); -#endif - - buffere = &(req_q->buffer[buffer_index]->element[buffere_index]); - buffere->flags |= SBAL_FLAGS_LAST_ENTRY; - out: - ZFCP_LOG_DEBUG("%i BUFFER(s) from %i to %i needed\n", - fsf_req->sbal_count, fsf_req->sbal_index, buffer_index); - ZFCP_LOG_TRACE("total QDIO data buffer size is %i\n", length_total); - - return length_total; -} - void zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *adapter) { @@ -1293,4 +921,3 @@ static struct device_attribute *zfcp_sysfs_sdev_attrs[] = { }; #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c index 8e09e042c3b3..6808930ef819 100644 --- a/drivers/s390/scsi/zfcp_sysfs_adapter.c +++ b/drivers/s390/scsi/zfcp_sysfs_adapter.c @@ -5,7 +5,8 @@ * * sysfs adapter related routines * - * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation + * (C) Copyright IBM Corp. 2003, 2004 + * * Authors: * Martin Peschke <mpeschke@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> @@ -25,14 +26,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_SYSFS_ADAPTER_C_REVISION "$Revision: 1.26 $" +#define ZFCP_SYSFS_ADAPTER_C_REVISION "$Revision: 1.30 $" #include <asm/ccwdev.h> #include "zfcp_ext.h" #include "zfcp_def.h" #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG static const char fc_topologies[5][25] = { {"<error>"}, @@ -66,12 +66,15 @@ ZFCP_DEFINE_ADAPTER_ATTR(status, "0x%08x\n", atomic_read(&adapter->status)); ZFCP_DEFINE_ADAPTER_ATTR(wwnn, "0x%016llx\n", adapter->wwnn); ZFCP_DEFINE_ADAPTER_ATTR(wwpn, "0x%016llx\n", adapter->wwpn); ZFCP_DEFINE_ADAPTER_ATTR(s_id, "0x%06x\n", adapter->s_id); -ZFCP_DEFINE_ADAPTER_ATTR(hw_version, "0x%04x\n", adapter->hydra_version); +ZFCP_DEFINE_ADAPTER_ATTR(card_version, "0x%04x\n", adapter->hydra_version); ZFCP_DEFINE_ADAPTER_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version); ZFCP_DEFINE_ADAPTER_ATTR(fc_link_speed, "%d Gb/s\n", adapter->fc_link_speed); ZFCP_DEFINE_ADAPTER_ATTR(fc_service_class, "%d\n", adapter->fc_service_class); ZFCP_DEFINE_ADAPTER_ATTR(fc_topology, "%s\n", fc_topologies[adapter->fc_topology]); +ZFCP_DEFINE_ADAPTER_ATTR(hardware_version, "0x%08x\n", + adapter->hardware_version); +ZFCP_DEFINE_ADAPTER_ATTR(serial_number, "%17s\n", adapter->serial_number); /** * zfcp_sysfs_adapter_in_recovery_show - recovery state of adapter @@ -259,11 +262,6 @@ zfcp_sysfs_adapter_failed_store(struct device *dev, goto out; } - /* restart error recovery only if adapter is online */ - if (adapter->ccw_device->online != 1) { - retval = -ENXIO; - goto out; - } zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED); @@ -304,13 +302,15 @@ static struct attribute *zfcp_adapter_attrs[] = { &dev_attr_wwnn.attr, &dev_attr_wwpn.attr, &dev_attr_s_id.attr, - &dev_attr_hw_version.attr, + &dev_attr_card_version.attr, &dev_attr_lic_version.attr, &dev_attr_fc_link_speed.attr, &dev_attr_fc_service_class.attr, &dev_attr_fc_topology.attr, &dev_attr_scsi_host_no.attr, &dev_attr_status.attr, + &dev_attr_hardware_version.attr, + &dev_attr_serial_number.attr, NULL }; @@ -343,4 +343,3 @@ zfcp_sysfs_adapter_remove_files(struct device *dev) } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_sysfs_driver.c b/drivers/s390/scsi/zfcp_sysfs_driver.c index 9908f2566268..988dceab5820 100644 --- a/drivers/s390/scsi/zfcp_sysfs_driver.c +++ b/drivers/s390/scsi/zfcp_sysfs_driver.c @@ -5,7 +5,8 @@ * * sysfs driver related routines * - * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation + * (C) Copyright IBM Corp. 2003, 2004 + * * Authors: * Martin Peschke <mpeschke@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> @@ -25,14 +26,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_SYSFS_DRIVER_C_REVISION "$Revision: 1.8 $" +#define ZFCP_SYSFS_DRIVER_C_REVISION "$Revision: 1.12 $" #include <asm/ccwdev.h> #include "zfcp_ext.h" #include "zfcp_def.h" #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG /** * ZFCP_DEFINE_DRIVER_ATTR - define for all loglevels sysfs attributes @@ -67,7 +67,8 @@ static ssize_t zfcp_sysfs_loglevel_##_name##_store(struct device_driver *drv, \ static ssize_t zfcp_sysfs_loglevel_##_name##_show(struct device_driver *dev, \ char *buf) \ { \ - return sprintf(buf,"%d\n", ZFCP_LOG_VALUE(ZFCP_LOG_AREA_##_define)); \ + return sprintf(buf,"%d\n", \ + ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA_##_define)); \ } \ \ static DRIVER_ATTR(loglevel_##_name, S_IWUSR | S_IRUGO, \ @@ -83,6 +84,14 @@ ZFCP_DEFINE_DRIVER_ATTR(qdio, QDIO); ZFCP_DEFINE_DRIVER_ATTR(erp, ERP); ZFCP_DEFINE_DRIVER_ATTR(fc, FC); +static ssize_t zfcp_sysfs_version_show(struct device_driver *dev, + char *buf) +{ + return sprintf(buf, "%s\n", ZFCP_VERSION); +} + +static DRIVER_ATTR(version, S_IRUGO, zfcp_sysfs_version_show, NULL); + static struct attribute *zfcp_driver_attrs[] = { &driver_attr_loglevel_other.attr, &driver_attr_loglevel_scsi.attr, @@ -92,6 +101,7 @@ static struct attribute *zfcp_driver_attrs[] = { &driver_attr_loglevel_qdio.attr, &driver_attr_loglevel_erp.attr, &driver_attr_loglevel_fc.attr, + &driver_attr_version.attr, NULL }; @@ -124,4 +134,3 @@ zfcp_sysfs_driver_remove_files(struct device_driver *drv) } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_sysfs_port.c b/drivers/s390/scsi/zfcp_sysfs_port.c index 2dc451b0b005..0ead4498930f 100644 --- a/drivers/s390/scsi/zfcp_sysfs_port.c +++ b/drivers/s390/scsi/zfcp_sysfs_port.c @@ -5,7 +5,8 @@ * * sysfs port related routines * - * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation + * (C) Copyright IBM Corp. 2003, 2004 + * * Authors: * Martin Peschke <mpeschke@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> @@ -25,7 +26,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_SYSFS_PORT_C_REVISION "$Revision: 1.32 $" +#define ZFCP_SYSFS_PORT_C_REVISION "$Revision: 1.37 $" #include <linux/init.h> #include <linux/module.h> @@ -34,7 +35,6 @@ #include "zfcp_def.h" #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG /** * zfcp_sysfs_port_release - gets called when a struct device port is released @@ -209,11 +209,6 @@ zfcp_sysfs_port_failed_store(struct device *dev, const char *buf, size_t count) goto out; } - /* restart error recovery only if adapter is online */ - if (port->adapter->ccw_device->online != 1) { - retval = -ENXIO; - goto out; - } zfcp_erp_modify_port_status(port, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED); zfcp_erp_wait(port->adapter); @@ -268,6 +263,10 @@ zfcp_sysfs_port_in_recovery_show(struct device *dev, char *buf) static DEVICE_ATTR(in_recovery, S_IRUGO, zfcp_sysfs_port_in_recovery_show, NULL); +/** + * zfcp_port_common_attrs + * sysfs attributes that are common for all kind of fc ports. + */ static struct attribute *zfcp_port_common_attrs[] = { &dev_attr_failed.attr, &dev_attr_in_recovery.attr, @@ -281,6 +280,10 @@ static struct attribute_group zfcp_port_common_attr_group = { .attrs = zfcp_port_common_attrs, }; +/** + * zfcp_port_no_ns_attrs + * sysfs attributes not to be used for nameserver ports. + */ static struct attribute *zfcp_port_no_ns_attrs[] = { &dev_attr_unit_add.attr, &dev_attr_unit_remove.attr, @@ -330,4 +333,3 @@ zfcp_sysfs_port_remove_files(struct device *dev, u32 flags) } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/s390/scsi/zfcp_sysfs_unit.c b/drivers/s390/scsi/zfcp_sysfs_unit.c index 88af5b635348..2f65d9165c40 100644 --- a/drivers/s390/scsi/zfcp_sysfs_unit.c +++ b/drivers/s390/scsi/zfcp_sysfs_unit.c @@ -5,7 +5,8 @@ * * sysfs unit related routines * - * Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation + * (C) Copyright IBM Corp. 2003, 2004 + * * Authors: * Martin Peschke <mpeschke@de.ibm.com> * Heiko Carstens <heiko.carstens@de.ibm.com> @@ -25,7 +26,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_SYSFS_UNIT_C_REVISION "$Revision: 1.19 $" +#define ZFCP_SYSFS_UNIT_C_REVISION "$Revision: 1.23 $" #include <linux/init.h> #include <linux/module.h> @@ -34,7 +35,6 @@ #include "zfcp_def.h" #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG -#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG /** * zfcp_sysfs_unit_release - gets called when a struct device unit is released @@ -104,13 +104,9 @@ zfcp_sysfs_unit_failed_store(struct device *dev, const char *buf, size_t count) goto out; } - /* restart error recovery only if adapter is online */ - if (unit->port->adapter->ccw_device->online != 1) { - retval = -ENXIO; - goto out; - } zfcp_erp_modify_unit_status(unit, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED); + zfcp_erp_wait(unit->port->adapter); out: up(&zfcp_data.config_sema); return retval ? retval : count; @@ -199,4 +195,3 @@ zfcp_sysfs_unit_remove_files(struct device *dev) } #undef ZFCP_LOG_AREA -#undef ZFCP_LOG_AREA_PREFIX diff --git a/drivers/scsi/cpqfcTSworker.c b/drivers/scsi/cpqfcTSworker.c index 2b5735863b65..026b613c328b 100644 --- a/drivers/scsi/cpqfcTSworker.c +++ b/drivers/scsi/cpqfcTSworker.c @@ -32,12 +32,8 @@ #include <linux/smp_lock.h> #include <linux/pci.h> -#define __KERNEL_SYSCALLS__ - #define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) -#include <linux/unistd.h> - #include <asm/system.h> #include <asm/irq.h> #include <asm/dma.h> diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 3009f8ab6a7f..5c5211c42fd6 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1,8 +1,8 @@ /* libata-core.c - helper library for ATA - Copyright 2003 Red Hat, Inc. All rights reserved. - Copyright 2003 Jeff Garzik + Copyright 2003-2004 Red Hat, Inc. All rights reserved. + Copyright 2003-2004 Jeff Garzik The contents of this file are subject to the Open Software License version 1.1 that can be found at @@ -1653,18 +1653,41 @@ void ata_fill_sg(struct ata_queued_cmd *qc) { struct scatterlist *sg = qc->sg; struct ata_port *ap = qc->ap; - unsigned int i; + unsigned int idx, nelem; assert(sg != NULL); assert(qc->n_elem > 0); - for (i = 0; i < qc->n_elem; i++) { - ap->prd[i].addr = cpu_to_le32(sg_dma_address(&sg[i])); - ap->prd[i].flags_len = cpu_to_le32(sg_dma_len(&sg[i])); - VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", - i, le32_to_cpu(ap->prd[i].addr), le32_to_cpu(ap->prd[i].flags_len)); + idx = 0; + for (nelem = qc->n_elem; nelem; nelem--,sg++) { + u32 addr, boundary; + u32 sg_len, len; + + /* determine if physical DMA addr spans 64K boundary. + * Note h/w doesn't support 64-bit, so we unconditionally + * truncate dma_addr_t to u32. + */ + addr = (u32) sg_dma_address(sg); + sg_len = sg_dma_len(sg); + + while (sg_len) { + boundary = (addr & ~0xffff) + (0xffff + 1); + len = sg_len; + if ((addr + sg_len) > boundary) + len = boundary - addr; + + ap->prd[idx].addr = cpu_to_le32(addr); + ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff); + VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); + + idx++; + sg_len -= len; + addr += len; + } } - ap->prd[qc->n_elem - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); + + if (idx) + ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); } /** @@ -2386,41 +2409,6 @@ static inline unsigned int ata_host_intr (struct ata_port *ap, } /** - * ata_chk_spurious_int - Check for spurious interrupts - * @ap: port to which command is being issued - * - * Examines the DMA status registers and clears - * unexpected interrupts. Created to work around - * hardware bug on Intel ICH5, but is applied to all - * chipsets using the standard irq handler, just for safety. - * If the bug is not present, this is simply a single - * PIO or MMIO read addition to the irq handler. - * - * LOCKING: - */ -static inline void ata_chk_spurious_int(struct ata_port *ap) { - int host_stat; - - if (ap->flags & ATA_FLAG_MMIO) { - void *mmio = (void *) ap->ioaddr.bmdma_addr; - host_stat = readb(mmio + ATA_DMA_STATUS); - } else - host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); - - if ((host_stat & (ATA_DMA_INTR | ATA_DMA_ERR | ATA_DMA_ACTIVE)) == ATA_DMA_INTR) { - if (ap->flags & ATA_FLAG_MMIO) { - void *mmio = (void *) ap->ioaddr.bmdma_addr; - writeb(host_stat & ~ATA_DMA_ERR, mmio + ATA_DMA_STATUS); - } else - outb(host_stat & ~ATA_DMA_ERR, ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); - - DPRINTK("ata%u: Caught spurious interrupt, status 0x%X\n", ap->id, host_stat); - udelay(1); - } -} - - -/** * ata_interrupt - * @irq: * @dev_instance: @@ -2452,7 +2440,6 @@ irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs) qc = ata_qc_from_tag(ap, ap->active_tag); if (qc && ((qc->flags & ATA_QCFLAG_POLL) == 0)) handled += ata_host_intr(ap, qc); - ata_chk_spurious_int(ap); } } diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index ff01a69d8182..5b28a33a4f5c 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -123,6 +123,7 @@ int ata_scsi_slave_config(struct scsi_device *sdev) { sdev->use_10_for_rw = 1; sdev->use_10_for_ms = 1; + blk_queue_max_phys_segments(sdev->request_queue, ATA_MAX_PRD / 2); return 0; /* scsi layer doesn't check return value, sigh */ } diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h index 79db90a501ac..5046ddfb9db1 100644 --- a/drivers/scsi/libata.h +++ b/drivers/scsi/libata.h @@ -26,7 +26,7 @@ #define __LIBATA_H__ #define DRV_NAME "libata" -#define DRV_VERSION "1.00" /* must be exactly four chars */ +#define DRV_VERSION "1.01" /* must be exactly four chars */ struct ata_scsi_args { struct ata_port *ap; diff --git a/drivers/scsi/qla2xxx/qla_os.h b/drivers/scsi/qla2xxx/qla_os.h index 67345d6269d4..3cc6d2f17f59 100644 --- a/drivers/scsi/qla2xxx/qla_os.h +++ b/drivers/scsi/qla2xxx/qla_os.h @@ -41,8 +41,6 @@ #include <linux/slab.h> #include <linux/mempool.h> #include <linux/vmalloc.h> -#define __KERNEL_SYSCALLS__ -#include <linux/unistd.h> #include <linux/smp_lock.h> #include <linux/bio.h> #include <linux/moduleparam.h> diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c index 014c825bf033..1293a3a90049 100644 --- a/drivers/scsi/sata_promise.c +++ b/drivers/scsi/sata_promise.c @@ -35,7 +35,7 @@ #include <asm/io.h> #define DRV_NAME "sata_promise" -#define DRV_VERSION "0.89" +#define DRV_VERSION "0.90" enum { @@ -115,7 +115,12 @@ enum { PDC_DIMM_SPD_SYSTEM_FREQ = 126, PDC_CTL_STATUS = 0x08, PDC_DIMM_WINDOW_CTLR = 0x0C, + PDC_TIME_CONTROL = 0x3C, + PDC_TIME_PERIOD = 0x40, + PDC_TIME_COUNTER = 0x44, PDC_GENERAL_CTLR = 0x484, + PCI_PLL_INIT = 0x8A531824, + PCI_X_TCOUNT = 0xEE1E5CFF }; @@ -1454,13 +1459,64 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) int speed, size, length; u32 addr,spd0,pci_status; u32 tmp=0; + u32 time_period=0; + u32 tcount=0; + u32 ticks=0; + u32 clock=0; + u32 fparam=0; void *mmio = pe->mmio_base; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; + /* Initialize PLL based upon PCI Bus Frequency */ + + /* Initialize Time Period Register */ + writel(0xffffffff, mmio + PDC_TIME_PERIOD); + time_period = readl(mmio + PDC_TIME_PERIOD); + VPRINTK("Time Period Register (0x40): 0x%x\n", time_period); + + /* Enable timer */ + writel(0x00001a0, mmio + PDC_TIME_CONTROL); + readl(mmio + PDC_TIME_CONTROL); + + /* Wait 3 seconds */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(3 * HZ); + + /* + When timer is enabled, counter is decreased every internal + clock cycle. + */ + + tcount = readl(mmio + PDC_TIME_COUNTER); + VPRINTK("Time Counter Register (0x44): 0x%x\n", tcount); + + /* + If SX4 is on PCI-X bus, after 3 seconds, the timer counter + register should be >= (0xffffffff - 3x10^8). + */ + if(tcount >= PCI_X_TCOUNT) { + ticks = (time_period - tcount); + VPRINTK("Num counters 0x%x (%d)\n", ticks, ticks); + + clock = (ticks / 300000); + VPRINTK("10 * Internal clk = 0x%x (%d)\n", clock, clock); + + clock = (clock * 33); + VPRINTK("10 * Internal clk * 33 = 0x%x (%d)\n", clock, clock); + + /* PLL F Param (bit 22:16) */ + fparam = (1400000 / clock) - 2; + VPRINTK("PLL F Param: 0x%x (%d)\n", fparam, fparam); + + /* OD param = 0x2 (bit 31:30), R param = 0x5 (bit 29:25) */ + pci_status = (0x8a001824 | (fparam << 16)); + } else + pci_status = PCI_PLL_INIT; + /* Initialize PLL. */ - pci_status = 0x8a531824; + VPRINTK("pci_status: 0x%x\n", pci_status); writel(pci_status, mmio + PDC_CTL_STATUS); readl(mmio + PDC_CTL_STATUS); diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index f42569be4890..ae14cbbea3fe 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -35,7 +35,7 @@ #include <linux/libata.h> #define DRV_NAME "sata_sil" -#define DRV_VERSION "0.52" +#define DRV_VERSION "0.53" enum { sil_3112 = 0, @@ -44,6 +44,11 @@ enum { SIL_SYSCFG = 0x48, SIL_MASK_IDE0_INT = (1 << 22), SIL_MASK_IDE1_INT = (1 << 23), + SIL_MASK_IDE2_INT = (1 << 24), + SIL_MASK_IDE3_INT = (1 << 25), + SIL_MASK_2PORT = SIL_MASK_IDE0_INT | SIL_MASK_IDE1_INT, + SIL_MASK_4PORT = SIL_MASK_2PORT | + SIL_MASK_IDE2_INT | SIL_MASK_IDE3_INT, SIL_IDE0_TF = 0x80, SIL_IDE0_CTL = 0x8A, @@ -59,6 +64,7 @@ enum { SIL_IDE2_CTL = 0x28A, SIL_IDE2_BMDMA = 0x200, SIL_IDE2_SCR = 0x300, + SIL_INTR_STEERING = (1 << 1), SIL_IDE3_TF = 0x2C0, SIL_IDE3_CTL = 0x2CA, @@ -304,7 +310,7 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) unsigned long base; void *mmio_base; int rc; - u32 tmp; + u32 tmp, irq_mask; if (!printed_version++) printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); @@ -365,14 +371,6 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) probe_ent->port[1].scr_addr = base + SIL_IDE1_SCR; ata_std_ports(&probe_ent->port[1]); - /* make sure IDE0/1 interrupts are not masked */ - tmp = readl(mmio_base + SIL_SYSCFG); - if (tmp & (SIL_MASK_IDE0_INT | SIL_MASK_IDE1_INT)) { - tmp &= ~(SIL_MASK_IDE0_INT | SIL_MASK_IDE1_INT); - writel(tmp, mmio_base + SIL_SYSCFG); - readl(mmio_base + SIL_SYSCFG); /* flush */ - } - if (ent->driver_data == sil_3114) { probe_ent->port[2].cmd_addr = base + SIL_IDE2_TF; probe_ent->port[2].ctl_addr = base + SIL_IDE2_CTL; @@ -385,6 +383,25 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) probe_ent->port[3].bmdma_addr = base + SIL_IDE3_BMDMA; probe_ent->port[3].scr_addr = base + SIL_IDE3_SCR; ata_std_ports(&probe_ent->port[3]); + + irq_mask = SIL_MASK_4PORT; + + /* flip the magic "make 4 ports work" bit */ + tmp = readl(mmio_base + SIL_IDE2_BMDMA); + if ((tmp & SIL_INTR_STEERING) == 0) + writel(tmp | SIL_INTR_STEERING, + mmio_base + SIL_IDE2_BMDMA); + + } else { + irq_mask = SIL_MASK_2PORT; + } + + /* make sure IDE0/1/2/3 interrupts are not masked */ + tmp = readl(mmio_base + SIL_SYSCFG); + if (tmp & irq_mask) { + tmp &= ~irq_mask; + writel(tmp, mmio_base + SIL_SYSCFG); + readl(mmio_base + SIL_SYSCFG); /* flush */ } pci_set_master(pdev); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3733a5a2ff47..763f259e84ae 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -462,7 +462,6 @@ config FB_I810_GTF config FB_MATROX tristate "Matrox acceleration" depends on FB && PCI - select I2C_ALGOBIT if FB_MATROX_I2C ---help--- Say Y here if you have a Matrox Millennium, Matrox Millennium II, Matrox Mystique, Matrox Mystique 220, Matrox Productiva G100, Matrox @@ -550,6 +549,7 @@ config FB_MATROX_G100 config FB_MATROX_I2C tristate "Matrox I2C support" depends on FB_MATROX && I2C + select I2C_ALGOBIT ---help--- This drivers creates I2C buses which are needed for accessing the DDC (I2C) bus present on all Matroxes, an I2C bus which @@ -628,6 +628,7 @@ config FB_RADEON tristate "ATI Radeon display support" depends on FB && PCI select I2C_ALGOBIT if FB_RADEON_I2C + select I2C if FB_RADEON_I2C help Choose this option if you want to use an ATI Radeon graphics card as a framebuffer device. There are both PCI and AGP versions. You @@ -645,7 +646,7 @@ config FB_RADEON config FB_RADEON_I2C bool "DDC/I2C for ATI Radeon support" - depends on FB_RADEON && I2C + depends on FB_RADEON default y help Say Y here if you want DDC/I2C support for your Radeon board. diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 8936d55cdbe5..89479a9eb7e0 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -86,9 +86,9 @@ #define DPRINTK(a,b...) #endif -#define BANSHEE_MAX_PIXCLOCK 270000.0 -#define VOODOO3_MAX_PIXCLOCK 300000.0 -#define VOODOO5_MAX_PIXCLOCK 350000.0 +#define BANSHEE_MAX_PIXCLOCK 270000 +#define VOODOO3_MAX_PIXCLOCK 300000 +#define VOODOO5_MAX_PIXCLOCK 350000 static struct fb_fix_screeninfo tdfx_fix __initdata = { .id = "3Dfx", diff --git a/fs/Kconfig b/fs/Kconfig index 65202333d179..80bc94106b72 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -951,6 +951,18 @@ config HFS_FS To compile this file system support as a module, choose M here: the module will be called hfs. +config HFSPLUS_FS + tristate "Apple Extended HFS file system support" + select NLS + help + If you say Y here, you will be able to mount extended format + Macintosh-formatted hard drive partitions with full read-write access. + + This file system is often called HFS+ and was introduced with + MacOS 8. It includes all Mac specific filesystem data such as + data forks and creator codes, but it also has several UNIX + style features such as file ownership and permissions. + config BEFS_FS tristate "BeOS file systemv(BeFS) support (read only) (EXPERIMENTAL)" depends on EXPERIMENTAL diff --git a/fs/Makefile b/fs/Makefile index 21021023447a..9647bebd4895 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_VFAT_FS) += vfat/ obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_DEVFS_FS) += devfs/ +obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ obj-$(CONFIG_HFS_FS) += hfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 639152014516..1f5241d0d4c1 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -147,7 +147,7 @@ static int afs_iget5_set(struct inode *inode, void *opaque) inline int afs_iget(struct super_block *sb, struct afs_fid *fid, struct inode **_inode) { - struct afs_iget_data data = { fid: *fid }; + struct afs_iget_data data = { .fid = *fid }; struct afs_super_info *as; struct afs_vnode *vnode; struct inode *inode; diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog deleted file mode 100644 index 723f2232d8fa..000000000000 --- a/fs/hfs/ChangeLog +++ /dev/null @@ -1,2506 +0,0 @@ -2000-01-02 a sun <asun@asun.cobalt.com> - - * file.c (hfs_get_block): added hfs_get_block for regular files. - -1999-04-12 a sun <asun@hecate.darksunrising.blah> - - * file_hdr.c (hdr_read): added rootinfo behaviour for DID header. - -1999-04-11 a sun <asun@hecate.darksunrising.blah> - - * super.c (parse_options): added s_version so that we can select - between different versions of the same layout. - -1999-04-05 a sun <asun@hecate.darksunrising.blah> - - * linux/hfs_fs.h: unified netatalk and appledouble header format. - added in all of the AFP attribute bits. - - * file_hdr.c: added netatalk appledouble v2 compatible headers. - -1999-01-30 a sun <asun@hecate.darksunrising.blah> - - * catalog.c (hfs_cat_move): fixed corruption problem with - renames. - -1999-01-27 a sun <asun@hecate.darksunrising.blah> - - * file_hdr.c (get/set_dates): got rid of broken afpd times. NOTE: - you must use netatalk-1.4b2+asun2.1.2 or newer for this. - -1998-12-20 a sun <asun@hecate.darksunrising.blah> - - * bdelete.c (del_root): assign bthLNode and bthFNode only if the - root node becomes a leaf node. Disk First Aid no longer - complains. Norton Utilities, of course, has decided that it - doesn't like the root node number. bleah. i think that it might be - due to Norton Utilities not expecting the root node to have moved. - -1998-12-16 a sun <asun@hecate.darksunrising.blah> - - * sysdep.c (hfs_revalidate_dentry): fix inode dates when there's a - timezone change. - -1998-12-15 root <root@hecate.darksunrising.blah> - - * extent.c (new_extent): expand block size variables to handle - u32. - - * mdb.c (hfs_mdb_get): AlBlkSiz shouldn't be capped at 65535. we - should be able to handle much larger volumes now. - -1998-11-21 a sun <asun@hecate.darksunrising.blah> - - * hfs_sysdep.h, hfs_fs.h: added hfs_from_utc/to_utc to deal with - date differences on hfs formatted media. - - NOTE: hfs extended keeps everything in utc, so we'll need to deal - with that when appropriate. - -1998-11-12 a sun <asun@hecate.darksunrising.blah> - - * extent.c (shrink_fork): added some lock_bitmap/unlock_bitmap's - to protect hfs_clear_vbm_bits. we should no longer have problems - with free_ablocks wrapping around. - -1998-11-02 a sun <asun@hecate.darksunrising.blah> - - * mdb.c (hfs_mdb_get): plugged up an mdb failed initialization - leak. - -1998-10-31 a sun <asun@hecate.darksunrising.blah> - - * version.c (hfs_version): bumped to version 0.96. - - * mdb.c (hfs_mdb_commit): you only write out the alternate MDB - when the catalog or extents overflow files grow. that just leaves - the btree corruption problems. bleah (whilst deleting a bunch of - files, more of the btree can get pruned away than desired). - -1998-10-30 a sun <asun@hecate.darksunrising.blah> - - * dir.c: fixed a bunch of silliness with deletions. make sure to - zero out stuff and set mark_inode_dirty(). - -1998-10-29 a sun <asun@hecate.darksunrising.blah> - - * string.c (hfs_strcmp, hfs_streq, hfs_strhash): converted them to - take name/len arguments instead of hfs_name to reduce copying. - - * dir.c, dir_nat.c, dir_cap.c, dir_dbl.c, sysdep.c: modified - relevant areas to reflect string.c changes. - -1998-10-28 a sun <asun@hecate.darksunrising.blah> - - * hfs.h (hfs_lookup_dentry): oh my. more silliness. make sure to - have the d_lookup use the same hash value as the one generated by - hfs_hash_dentry. i also changed the argument order. - (hfs_drop_special): change the argument order to be more in line - with what the dcache stuff looks like. - - * sysdep.c (hfs_compare_dentry): the compare was returning the - wrong value for correct matches and causing all sorts of - mischief. this fixes both directory counts and mounting on top of - hfs volumes. - - * file.c, file_cap.c, file_hdr.c: added mark_inode_dirty()'s in - the relevant places. - -1998-10-11 root <asun@hecate.darksunrising.blah> - - * mdb.c (hfs_mdb_get): moved initialization of mdb->entry_dirty - list to here to deal with trying to read a bad hfs volume. - -1998-10-10 a sun <asun@zoology.washington.edu> - - * inode.c, catalog.c, dir_*.c, sysdep.c: parts of the dcache - conversion didn't get done properly. specifically, i forgot to - move the hfs_cat_puts into the right place. that's fixed now. - -1998-09-11 a sun <asun@purgatorius.zoology.washington.edu> - - * mdb.c: altered mdb struct to reflect hfs plus usage. - -1998-08-27 a sun <asun@purgatorius.zoology.washington.edu> - - * file.c, file_hdr.c, file_cap.c: dealt with the remaining - copy_to/from_user() error cases. - -1998-08-26 a sun <asun@purgatorius.zoology.washington.edu> - - * super.c (hfs_read_super): fixed to deal with cdroms. why doesn't - the cdrom layer call the partition table code? - -Wed Jan 21 14:04:26 1998 a sun <asun@zoology.washington.edu> - - * inode.c, sysdep.c - use d_iput to uncache dentry from catalog entry instead of relying - on put_inode. no more NULL pointer dereferences! - - * catalog.c - cleaned up hfs_cat_put a little. - - ISSUES (non-fatal): mv dir dir2 while creating files in dir screws - up directory counts. - - deletion using netatalk screws up directory - counts. - -Thu Jan 15 19:14:28 1998 a sun <asun@zoology.washington.edu> - - * catalog.c - make deletion happen when requested instead of waiting until - an hfs_cat_put as the dcache can hold onto entries for quite - some time. - -Wed Jan 14 14:43:16 1998 a sun <asun@zoology.washington.edu> - - * catalog.c - the current catalog allocation scheme allocates - PAGE_SIZE/sizeof(struct hfs_cat_entry) entries at a time and keeps - a pool of free entries up to this allocation unit * 8. - - * inode.c - make sure to always hfs_cat_put if hfs_iget is going to return - NULL. - - * string.c, catalog.c - use linux' hashing method to generate hashes. the old hashing was - getting collisions. catalog.c also has a larger hash table to - prevent collisions. - -Tue Jan 13 13:06:01 1998 a sun <asun@zoology.washington.edu> - - * version.c - bumped to 0.95+asun3 - - * catalog.c - re-wrote to dynamically allocate/delete catalog entries. on a 486, - entries fit into the size-256 slab. - -Wed Jan 7 19:33:33 1998 a sun <asun@zoology.washington.edu> - - * inode.c - don't hfs_cat_put gratuitously in hfs_iget. that's a bad - idea and results in screwed up entry counts. - -Tue Jan 6 14:38:24 1998 a sun <asun@zoology.washington.edu> - - * version.c - changed it to 0.95+asun2 - - * sysdep.c - altered catalog entry pruning to make sure that an iput - gets done. for some reason, shrink_dcache_parent wasn't - doing it. - - * catalog.c - added a global dirty list to check for pruning. - -Tue Jan 6 12:29:52 1998 a sun <asun@zoology.washington.edu> - - * catalog.c - re-wrote it to be similar to 2.1.x inode.c. this should - at least make catalog.c SMP safe. - - * hfs.h, linux/hfs_fs.h - moved dentry operations into hfs.h. these probably should - be moved somewhere else. - - * super.c, dir_cap.c, dir_nat.c, dir_dbl.c, sysdep.c - added dentry ops to hash everything to lowercase. - -Sun Dec 28 22:48:53 1997 a sun <asun@zoology.washington.edu> - - * sysdep.c, catalog.c, hfs.h - as a temporary workaround until catalog.c gets re-written, - i flush the dcache if we need more entries. - -Fri Dec 19 15:11:21 1997 a sun <asun@zoology.washington.edu> - - * dir_dbl.c - statically allocate tmp_name instead of doing it dynamically. - - NOTE: well, those pesky hfs_cat_put messages still aren't gone. in - addition, catalog.c needs to be modified to free up some entries - when the cache gets filled up. - -Sun Dec 14 11:51:11 1997 a sun <asun@zoology.washington.edu> - - * linux/hfs_fs.h - moved the dentry stuff into within the #ifdef __KERNEL__ - part of hfs_fs.h and cleaned up a little. - -Sun Dec 14 11:24:54 1997 a sun <asun@zoology.washington.edu> - - * dir.c - changed hfs_rename to delete all old dentries. hfs_cat_put - messages on umount should be a thing of the past now. - -Sun Dec 14 01:12:58 1997 a sun <asun@zoology.washington.edu> - - * dir.c - changed mark_inodes_deleted to dget/d_delete/dput the dentry - instead of just dropping it. the bytes available should now - be updated properly upon deletion. - -Wed Dec 10 00:01:25 1997 a sun <asun@zoology.washington.edu> - - * dir.c - changed mark_inodes_deleted to drop the dentry instead of - just deleting it. - - TODO: bytes available aren't being properly updated when a - resource fork gets deleted. - -Mon Dec 8 23:22:40 1997 a sun <asun@zoology.washington.edu> - - * dir_cap.c, dir_nat.c, dir_dbl.c, dir.c - * hfs.h, linux/hfs_sysdep.h, linux/hfs_fs_i.h - Added code to drop ({dbl,cap,nat}_drop_dentry) invalid - dentries when creating or moving a file. - - * inode.c - Added code to delete cached dentries when a file gets deleted. - - * current yuckiness: there's an extra hfs_cat_put somewhere. it's - harmless but bothersome. - -Thu Dec 4 00:14:03 1997 a sun <asun@zoology.washington.edu> - - * dir.c, dir_cap.c, dir_nat.c, file.c, file_hdr.c, inode.c, - * linux/{hfs_sysdep.h, hfs_fs.h}, version.c: - Completed first code dentrification sweep. It mounts! It copies! - It dcaches! - -Mon Apr 28 06:58:44 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, INSTALL.sgml, HFS.sgml: - Bump version to 0.95 (Woohoo! We're beta!) - - * linux/hfs_fs.h: - Modify HFS_SB() and HFS_I() when compiled into the kernel. - - * FAQ.sgml: - Add a new question (and its answer): - Why does my Macintosh show generic application and document icons? - - * HFS.sgml: - Add some URLs and remove the (now empty) FAQ section. - -Sun Apr 27 22:17:01 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * HFS.sgml: - Don't call the version 1 headers "slightly modified". - - * file_hdr.c, dir_nat.c: - Comment some AFPD compatibility stuff. - - * FAQ.sgml: - Update for version 0.95. - - * BUG_INFO: - Remove the BIG_INFO script since we no longer mention it. - - * README.sgml, INSTALL.sgml, HFS.sgml, Makefile: - Split README.sgml into HFS.sgml and INSTALL.sgml. - Stop including the document sources in snapshots. - - * file_hdr.c: - Fix hdr_truncate() not to truncate the data fork. - -Wed Apr 16 23:56:25 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * FAQ.sgml: - Bump version to 0.8.4 and add two answers: - How to fsck an HFS filesystem. - How to generate linux/version.h. - - * version.c, README.sgml: - Bump version to 0.8.4. - - * README.sgml, FAQ.sgml, Makefile: - Separate the FAQ from the README. - - * linux/hfs_fs.h: - Add (struct hfs_fork) to the forward declarations. - -Thu Apr 10 05:47:16 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * linux/hfs_sysdep.h: - Work around the non-const declaration of test_bit()'s second argument. - - * Makefile: - Use .config from the kernel source to check for MODVERSIONS. - -Wed Apr 9 07:57:17 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * bnode.c: - Check the record table in each bnode as we read it from disk. - - * super.c, mdb.c, hfs.h: - Deal with the ATTRIB_CLEAN bit of the MDB properly (in mdb.c). - - * super.c, hfs.h, mdb.c: - Search for the alt-MDB rather than using the device size to find it. - -Wed Apr 9 03:39:05 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version to 0.8.3. - -Mon Apr 7 20:09:56 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * part_tbl.c: - Fix to allow bootable CDROMs (which have blocksize != 512) to mount. - - * super.c: - Check that blk_size[MAJOR(dev)] is non-NULL before dereferencing. - -Sat Apr 5 10:44:42 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_btree.h, binsert.c, brec.c, bfind.c, bins_del.c, bdelete.c: - Make btree operations less likely to do - nasty things if the tree is corrupted. - - * part_tbl.c, README.sgml: - Count partitions from 0 rather than from 1. - -Wed Apr 2 23:26:51 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * bdelete.c: - Don't bother checking for oversized keys in hfs_bdelete(). - - * bdelete.c, bfind.c, binsert.c: - Verify key lengths against the maximum given for the tree. - - * Makefile: - Check that /usr/include/linux/modversions.h exists before including it. - This allows compilation without CONFIG_MODVERSIONS enabled. - -Sat Mar 29 13:17:53 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * linux/hfs_fs.h, super.c, file_hdr.c, hfs.h, extent.c, file_cap.c, - dir_dbl.c, dir_nat.c, dir.c, dir_cap.c, binsert.c, catalog.c, - bfind.c: - Make (struct hfs_bkey) and (struct hfs_brec) more "abstract". - - * binsert.c: - Remove redundant test in hfs_binsert(). - -Sat Mar 29 05:24:23 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Fix formatting problems in README.sgml and bump version to 0.8.2. - - * extent.c: - Fix bug that caused serious headaches with fragmented files. - -Fri Mar 28 00:23:18 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version to 0.8.1. - - * btree.c, balloc.c: - Commit map nodes to buffers when new map nodes are added. - -Thu Mar 27 22:41:07 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * Makefile: - Include linux/modversions.h from the gcc command line. - - * mdb.c: - Was updating modified date twice in hfs_mdb_commit(). - - * linux/hfs_sysdep.h, linux/hfs_fs.h, linux/hfs_fs_i.h, - linux/hfs_fs_sb.h, sysdep.c, trans.c, super.c, hfs_sysdep.h, inode.c, - hfs_fs_i.h, hfs_fs_sb.h, hfs_fs.h, hfs.h, file_cap.c, file_hdr.c, - file.c, dir_nat.c, dir_cap.c, dir_dbl.c, Makefile, dir.c: - Rearrange headers in preparation for inclusion in the kernel. - - * hfs_fs_sb.h, hfs_fs.h: - Add forward declarations so other code can include these headers. - - * hfs_sysdep.h: - Include __constant_hton[ls]() for little-endian machines. - - * hfs_fs.h, hfs_sysdep.h, hfs.h: - Move typedefs of hfs_{byte,word,lword}_t from hfs.h to hfs_sysdep.h. - Include hfs_sysdep.h from hfs_fs.h. - - * trans.c, super.c, part_tbl.c, string.c, inode.c, mdb.c, hfs_fs_sb.h, - hfs_sysdep.h, hfs_fs.h, hfs.h, hfs_btree.h, file_cap.c, file_hdr.c, - file.c, dir_nat.c, extent.c, dir_dbl.c, dir.c, dir_cap.c, catalog.c, - btree.c, bnode.c, brec.c, bitmap.c, bitops.c, bins_del.c, binsert.c, - bdelete.c, bfind.c, balloc.c: - Big type system changes in preparation for kernel inclusion: - '[US](8|16|32)' -> 'hfs_[us](8|16|32)' (avoids name space pollution) - 'hfs_name_t' -> 'struct hfs_name' (allows forward declaration) - - * super.c, hfs_fs.h: - Add init_hfs_fs() to super.c for non-module compilation. - -Wed Mar 26 07:53:59 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version to 0.8. - - * README.sgml: - Special compilation note for DEC Alpha. - - * README.sgml: - Note status on non-Intel processors. - - * hfs_fs.h: - Use long's for read() and write() on the Alpha. - - * README.sgml: - Document the afpd mount option. - - * inode.c: - Make files always writable for owner in afpd mode. - -Tue Mar 25 23:21:39 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * part_tbl.c: - Clean up the error checking code a bit. - -Sat Mar 22 19:43:40 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * part_tbl.c: - Fixed uninitialized variable in old-style partition code. - - * bins_del.c, bdelete.c: - Fix extraneous "bad argument to shift_{left,right}" messages. - - * bitops.c: - Note that these routines are now tested on Intel, PPC and Alpha. - - * Makefile: - Add -fno-builtin to the CFLAGS. - -Fri Feb 14 10:50:14 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_sysdep.h: - Don't include <asm/*.h> until after <linux/types.h>. - - * catalog.c: - Use volume create date in hashfn() rather than casting pointer to int. - - * hfs.h, mdb.c: - Maintaing volume create, modify and backup dates in struct hfs_mdb. - - * hfs_fs.h: - Include the header for put_user BEFORE using it! - - * string.c, hfs.h: - Make hfs_strhash() return an unsigned int. - - * trans.c, version.c, super.c, mdb.c, part_tbl.c, string.c, inode.c, - hfs_sysdep.h, hfs_fs.h, hfs_fs_sb.h, hfs_btree.h, hfs.h, file_cap.c, - file_hdr.c, extent.c, dir_dbl.c, dir_nat.c, dir_cap.c, dir.c, - catalog.c, btree.c, bnode.c, brec.c, bitmap.c, binsert.c, - bins_del.c, bdelete.c, balloc.c, README.sgml, Makefile: - Updated copyright notices. - - * trans.c, part_tbl.c, string.c, super.c, inode.c, mdb.c, hfs_fs.h, - hfs_fs_sb.h, hfs_sysdep.h, hfs_btree.h, hfs.h, file_cap.c, - file_hdr.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c, catalog.c, - dir.c, brec.c, btree.c, bitmap.c, bnode.c, bdelete.c, bins_del.c, - binsert.c, Makefile, TODO, balloc.c: - First shot at portability to the DEC Alpha and non-gcc compilers. - This involved a significant overhaul of the type system. - -Tue Feb 4 04:26:54 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version to "pre-0.8-4". - - * dir_nat.c: - Allow creat() in Netatalk .AppleDouble directories. - - * dir_dbl.c: - Make local functions static. - - * dir_dbl.c: - Removed unnecessary 'extern' qualifiers from forward declarations. - - * file_hdr.c, TODO: - Fixed the 30-year time warp with afpd. - - * TODO, trans.c: - Don't mangle the name .AppleDesktop under fork=netatalk. - -Mon Feb 3 23:18:45 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * inode.c: - Make header files always writable when the afpd mount option is given. - Otherwise it is impossible to unlock a locked file. - - * TODO, inode.c: - Let afpd think chmod() always succeeds, so "New Folder" works right. - - * super.c: - The 'afpd' mount option now makes 'fork=n,names=n' the default. - - * TODO: - List the current known afpd-compatibility problems as bugs. - - * file_hdr.c: - Make certain date changes through header files get written to disk. - -Sat Feb 1 02:24:12 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * mdb.c: - Work around for Linux rounding device sizes to 1k increments. - - * README.sgml: - Fixed a typo: "the a". - -Sat Dec 28 20:41:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * TODO: - Add ioctl() interface as a "missing feature." - - * dir_nat.c: - Finish implementing the afpd-compatibility - mode using the new 'afpd' mount option. - - * hfs_fs_sb.h, super.c: - Add new 'afpd' mount option. - - * file_cap.c: - Spelling fix. - -Wed Dec 11 23:16:08 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * TODO, README.sgml: - Optimistically document the hybrid CD problem as fixed. - - * part_tbl.c: - Fix the partition code so at least some of the hybrid - CDROMs that were previously rejected are now accepted. - - * hfs.h: - Make fs_start a 32-bit integer rather than 16-bits. - The 16-bit value would overflow if a partition started - beyond the 32M mark (e.g. the Executor 2 Beta 1 CDROM). - - * extent.c: - Fixed a typo in an error message. - -Tue Dec 10 14:43:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * dir_nat.c: - Merge in the (still dormant) afpd-compatibility changes. - - * inode.c: - Make the .AppleDouble directory writable (again). - - * version.c, README.sgml: - Bump version up to "pre-0.8-3". - - * hfs_fs.h, file_cap.c, file_hdr.c: - Move AFP constants to hfs_fs.h and prefix them with "HFS_". - - * dir_nat.c, inode.c: - Back-out changes that allowed writing to the .AppleDouble directory. - - * Makefile: - Update rules for linuxdoc-sgml v1.5. - - * extent.c: - Fixed serious bug in decode_extent() with handling of empty extents. - - * file.c: - Rewrote hfs_getblk(). - It will no longer hang if hfs_extent_map() is buggy. - Also halves the worst-case number of calls to hfs_extent_map(). - - * extent.c: - Fixed serious bug in decode_extent() with handling of empty extents. - - * hfs_fs.h: - Small change so the PPC (and maybe other architectures?) - pick up the prototypes for the user-space access functions. - - * super.c, file_cap.c, file_hdr.c, hfs_fs.h, file.c: - Updated for new user-space memory interface. - -Sun Dec 8 11:49:36 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * dir_nat.c: - Add special code for unlink(), and rename() in the .AppleDouble - directory and rmdir() of the .AppleDouble directory. - - * inode.c: - Make the .AppleDouble directory writable. - - * file_hdr.c: - Use AFP flags in version 1 headers (for Netatalk compatibility). - - * trans.c: - Fixed bug with long names causing kernel Oops. - -Mon Oct 7 06:05:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs.h, file_cap.c, file_hdr.c, hfs.h, extent.c, file.c, dir.c: - Fix types for various read/write/truncate computations. - Also allows compilation with 2.1.x kernels. - -Thu Sep 19 10:28:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * README.sgml, version.c: - Bump version up to "pre-0.8-2". - - * TODO: - Reformat the To Do list introducing prioritized categories. - - * file_hdr.c, file.c: - Move comments about mmap() for headers from file.c to file_hdr.c. - Also revise the reasoning for not yet having it implemented. - - * dir_nat.c, dir_cap.c, dir_dbl.c: - Remove 'hfs_' prefix from names of some purely local functions. - - * dir_dbl.c, TODO: - Under AppleDouble make create(), mkdir(), mknod(), unlink(), rename() - and rename() check against header files when arguments start with '%'. - - * super.c, hfs_fs_sb.h, hfs_fs.h, dir_dbl.c, dir_nat.c, dir_cap.c, - dir.c, README.sgml: - Fix problem that prevented creating %RootInfo or .rootinfo in all - directories in addition to preventing deletion from the root directory. - - * TODO: - Remove writable header files from the To Do list. - - * README.sgml: - Add extensive discussion of writing to HFS filesystems and - the format of the special files. - - * file_hdr.c: - Generate the 'homefs' field for version 1 header files. - -Wed Sep 18 23:07:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs.h, file_cap.c: - Comment the definition of (struct hfs_cap_info). - - * version.c, README.sgml: - Bump version up to "pre-0.8-1" and update the "How can I write?" FAQ. - - * file_hdr.c: - Implement hdr_write() and hdr_truncate()!! - - * hfs_fs_i.h, inode.c: - Make hdr_layout per-inode (not per-file) so hdr_truncate() will work. - - * file.c, hfs.h, catalog.c, extent.c, balloc.c: - hfs_extent_adj() now uses fork->lsize to determine the target file size. - -Sun Sep 15 07:55:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * README.sgml, trans.c: - Prevent creation of files & directories with '\0' or ':' in their names. - - * string.c, hfs_fs.h, hfs.h, dir_dbl.c, dir_nat.c, dir_cap.c: - With case=lower could have run off end of string. - -Tue Sep 10 12:05:47 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * inode.c: - Small clean up of HFS_FIL_LOCK handling. - - * inode.c: - Fix notify_change() not to accidentally make metadata executable. - - * hfs_fs.h: - AppleSingle files should have HFS_ITYPE_NORM. - - * inode.c: - Return to old behavior where MdDat = i_mtime. - - * dir_dbl.c: - Fix serious bug in hfs_dbl_readdir() that would lock-up access to a - directory if one tried to write to a directory they had previously read. - - * file.c: - Fix hfs_do_write() to adjust the fork's 'lsize' if it changed. - - * inode.c, file_cap.c: - Allow truncate() to be called even on metadata. - Any size changes will last only until the next iput() of the inode. - Truncating a header file doesn't yet truncate the resource fork. - - * inode.c: - Allow chmod() on a directory if it doesn't actually change i_mode. - - * hfs_fs.h, trans.c, super.c: - Rename hfs_cap2mac() to hfs_colon2mac(). - Rename hfs_apl2mac() to hfs_prcnt2mac(). - - * file_hdr.c: - Move header construction out of hdr_read() to create hdr_build_meta(). - - * hfs.h: - Add byte-order independent conversions: U32->U16, U32->U8 and U16->U8. - - * file.c, file_cap.c, hfs_fs.h: - Rename fix_perms() to hfs_file_fix_mode() and - move it from file_cap.c to file.c. - - * README.sgml, super.c: - Make the default for the names mount option vary with the fork option. - - * file_cap.c: - The umask was applied incorrectly in fix_perms(). - -Mon Sep 9 13:11:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * README.sgml: - Note that it compiles on m68k machines, but needs more testing. - - * hfs_sysdep.h, Makefile: - Changes to compile unmodified on m68k (and possibly other machines). - - * dir_cap.c: - hfs_cap_readdir() was mistakenly producing .rootinfo entries for - the .finderinfo and .resource subdirectories of the root directory. - - * inode.c: - A directory's i_size was too small by 1 under CAP, so hfs_cap_readdir() - would omit the last directory entry. i_nlink was also too large by 1. - -Sun Sep 8 12:56:06 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * file_hdr.c: - Rewrite hdr_read() to be more efficient and to deal correctly with - descriptors having lengths that differ from the actual size of the data. - - * file_cap.c: - Add write support for CAP finderinfo files!! - - * super.c, inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c, - file_nat.c, file_hdr.c, file.c, file_cap.c, Makefile, dir.c: - Generate metadata (header files and CAP finderinfo files) on-the-fly. - The files file_{dbl,nat}.c are merged into file_hdr.c as a result. - -Sat Sep 7 08:09:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * README.sgml: - Fix silly grammatical error. - -Fri Sep 6 09:17:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs_sb.h, super.c: - No need to cast type of s_reserved. - - * file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c, file_cap.c, dir_cap.c: - Add the missing NULL readpage and writepage entries to the inode_ops. - - * file_dbl.c, file_nat.c, file.c, file_cap.c: - Cleanup error checking for read() and write(). - -Thu Sep 5 05:29:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version up to "0.7.2". - User-visible changes from 0.7.0: - + Corrected CAP finderinfo file format. - + Support for more features of CAP finderinfo files. - + No longer requires gcc 2.7.0 or newer. - + Now implements mknod() system call. - - * hfs_fs.h, dir_nat.c, file_cap.c, file_nat.c, README.sgml, dir_cap.c: - Include the CAP and Netatalk copyright notices. - - * hfs_fs.h, file_cap.c: - Repair and improve CAP support. - - * catalog.c: - Oops! The BkDat for new files and directories was in 1972 when - it should have been in 1904 (not that it matters that much). - - * inode.c: - The HFS MdDat should be the larger of the i_mtime and i_ctime. - - * README.sgml: - Change 'm_time' to 'i_mtime'. - -Wed Sep 4 13:27:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version up to "0.7.1". - User-visible changes from 0.7.0: - + Minor bug in CAP finderinfo file format fixed. - + No longer requires gcc 2.7.0 or newer. - + Now implements mknod() system call. - - * README.sgml: - Removed note about needing gcc 2.7.0 or newer. - - * file.c: - Optimize hfs_do_read() based on the fact that HFS has no holes in files. - Minor code formatting changes. - - * hfs.h, hfs_sysdep.h, mdb.c, extent.c, file.c, btree.c, catalog.c, - balloc.c, bnode.c: - Reorganize memory management routines. - hfs_malloc() and hfs_free() are the main routines. - The macros FREE() and MALLOC() are gone. - HFS_NEW() and HFS_DELETE() are new 'shorthand' macros. - - * btree.c: - Fix broken debugging code. - - * super.c, hfs.h, mdb.c, part_tbl.c, Makefile: - Separate partition table handling into its own file. - - * dir.c: - Spelling fixes. - - * sysdep.c: - Oops! Error check got sense reversed while editing. - - * mdb.c, sysdep.c, hfs.h, hfs_btree.h, hfs_sysdep.h, btree.c, extent.c, - bfind.c, bnode.c, balloc.c: - Make hfs_buffer a pointer to a buffer_head, rather than a buffer_head. - - * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, dir.c: - Add a mknod() entry to the inode_operations for normal directories. - All it is good for is letting root create regular files. - - * file_dbl.c, file_nat.c, file.c, file_cap.c, dir_cap.c, dir_dbl.c, - dir_nat.c: - Add the missing NULL entries to the end of the file_operations. - - * super.c, hfs_btree.h, hfs_fs.h, mdb.c, extent.c, hfs.h, catalog.c: - Make the remainder of the (untested) changes - to allow compilation with gcc 2.6.3. - - * hfs_fs.h: - Fix hfs_fs.h to work with gcc 2.6.3. - - * hfs_fs.h: - (struct hfs_cap_info) should never have been 'packed'. - - * BUG_INFO: - Use -V for getting version of module utilities. - - * super.c, sysdep.c, trans.c, hfs_fs_sb.h, inode.c, hfs_fs.h, - hfs_fs_i.h, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, - dir_nat.c, file.c, dir.c, dir_cap.c: - Fix up hfs_fs{,_i,_sb}.h in preparation for inclusion in kernel. - -Tue Sep 3 23:58:03 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs.h: - Change eventual destination to linux/fs/hfs rather than include/linux. - - * super.c, inode.c, mdb.c, hfs_btree.h, hfs_fs.h, hfs_sysdep.h, - file_dbl.c, file_nat.c, hfs.h, dir_nat.c, extent.c, dir_dbl.c, - catalog.c, dir_cap.c, brec.c, btree.c, binsert.c, bnode.c, bdelete.c, - bfind.c, bins_del.c, balloc.c: - Replace all the swap{16,32}() stuff w/ ntohl() and friends. - -Fri Aug 30 09:51:23 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Rewrite installation instructions and bump version up to "0.7.0". - - * Makefile: - Remove the INCDIR variable; we now rely on the - user to have the correct links in /usr/include. - -Mon Aug 26 12:25:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Reformat the documentation and bump version up to "pre-0.7-9". - Hopefully this will become version 0.7 in a few days. - -Thu Aug 22 08:00:44 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * README.sgml, version.c: - Bump version up to "pre-0.7-8". - - * file_nat.c, file_dbl.c: - AppleDouble headers had resource fork size in wrong byte order. - -Wed Aug 21 05:22:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version up to "pre-0.7-7". - - * bnode.c: - Fixed a long-standing bug in hfs_bnode_lock(). - This bug occasionally caused lock-up under heavy load. - -Tue Aug 20 09:15:10 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * README.sgml, version.c: - Bump version up to "pre-0.7-6". - - * catalog.c: - Fix a deadlock problem in catalog readers/writers locking. - - * bins_del.c: - hfs_bnode_update_key() was still corrupting the header node sometimes. - - * catalog.c, dir.c: - Fix problem with extending the catalog B-tree hanging hfs_cat_commit(). - Fix a race that could delete a non-empty directory. - -Sun Aug 18 23:16:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version to "pre-0.7-5" for test release. - - * dir_cap.c, README.sgml: - Change ".:rootinfo:" to ".rootinfo". - - * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c: - Mangle the names as first step in hfs_{cap,dbl,nat}_lookup(). - Use the new hfs_streq() to catch mixed case matches to the special - files and directories in hfs_{cap,dbl,nat}_lookup(). - Store reserved names only once. - - * dir.c, hfs.h, string.c: - Implement hfs_streq() which tests for string equality more - rapidly than hfs_strcmp() by checking for equal length first, - and use it when checking for reserved names. - - * inode.c, TODO, dir_cap.c, dir_dbl.c, README.sgml: - Provide the metadata for the root directory for the CAP and AppleDouble - schemes in the files ".:rootinfo:" and "%RootInfo", respectively. - - * TODO, super.c: - Add (untested) support for the old Mac Plus style of partition map. - - * bdelete.c, TODO: - Note the possibility of bdelete() to hanging on a corrupted B-tree. - - * TODO: - Add items corresponding to some of the 'XXX' comments in the sources. - - * dir_dbl.c, dir_cap.c: - Update comments, removing ref. to a comment that once existed in inode.c - - * catalog.c: - Remove some redundant locking and error checks - that had been previously marked as questionable. - -Sat Aug 17 08:06:56 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * binsert.c, bfind.c, bins_del.c, balloc.c, bdelete.c: - Edited some comments for correctness. - - * README.sgml, version.c: - Bump version up to "pre-0.7-4" in preparation for snapshot release. - - * Makefile: - Have 'make dep' delete the *.o and *.s files. - - * catalog.c, hfs.h, TODO, bfind.c: - Move looping from hfs_cat_next() into hfs_bsucc(), - where it can be done an entire node at a time. - -Fri Aug 16 05:02:59 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * TODO: - Add AppleShare support to the list of goals. - - * trans.c, super.c, hfs_fs.h, README.sgml: - Add a "names=netatalk" mount option, since - Netatalk quotes initial periods and CAP doesn't. - - * Makefile: - Oops! Had removed the 'include .depend' from Makefile. - - * inode.c, hfs_fs.h, file_nat.c, file_dbl.c, file.c, dir_nat.c, - dir_dbl.c, dir_cap.c, dir.c, README.sgml: - Update for 2.0.1 and newer kernels. - - * Makefile: - Get rid of ifeq stuff and use a .tmpdepend file to make sure - a failed 'make depend' doesn't allow a 'make hfs.o'. - -Wed Aug 14 01:03:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump version up to "pre-0.7-3" in preparation for snapshot release. - - * btree.c, extent.c, bnode.c: - Fix up some debugging code. - -Tue Aug 13 12:42:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * version.c, README.sgml: - Bump revision to "pre-0.7-2". - - * super.c, sysdep.c, mdb.c, file_nat.c, inode.c, file_cap.c, - file_dbl.c, file.c, extent.c, dir.c, catalog.c, btree.c, bnode.c, - balloc.c: - Added the remaining missing function comments. - - * Makefile, README.sgml: - Simplify the default make rule to build the dependency file AND hfs.o. - Change the installation instructions to reflect the change. - - * hfs.h: - Added missing structure comments. - - * bdelete.c: - Merge bdelete_brec() back into hfs_bdelete(). - Add missing function comments. - - - * extent.c: - Insignificant code change removing an unneeded indirection. - - * btree.c, hfs_btree.h, balloc.c, bnode.c: - Add a 'sys_mdb' field to (struct hfs_btree). - - * extent.c, hfs_sysdep.h, sysdep.c, bnode.c, balloc.c, bfind.c, - Makefile: - Move hfs_buffer_read() from hfs_sysdep.h to sysdep.c so it can use - the symbol HFS_SECTOR_SIZE rather than the manifest constant 512. - Have hfs_buffer_read() print an error message, - and remove redundant errors from the callers. - - * hfs_sysdep.h, mdb.c, super.c, file.c, hfs.h, hfs_btree.h, catalog.c, - extent.c, btree.c, balloc.c, bfind.c, bnode.c: - Get rid of the type hfs_device and the fields of that type, - using the type hfs_sysmdb and the 'sys_mdb' field in its place. - - * Makefile: - Fix definition of HDRS variable. - - * README.sgml, version.c: - Bump version up to "pre-0.7-1". - - * Makefile: - Separate sources and headers into three groups: - B-tree code, HFS code and Linux code. - - * bitmap.c, bitops.c, hfs.h, hfs_sysdep.h, balloc.c: - Implemented portable set of bit operations in hfs_sysdep.h - - * mdb.c, hfs_sysdep.h, hfs_btree.h, extent.c, btree.c, bitmap.c, - bnode.c, balloc.c: - Implement a portable set of buffer operations in hfs_sysdep.h - - * TODO: - Remove note about separating header files into two parts. - - * catalog.c: - Remove call to hfs_mdb_dirty(), since the hfs_brec_relse() does it. - - * hfs.h, extent.c, file.c: - Move hfs_getblk() from extent.c to file.c, since that is now the - only file that actually uses it. - - * balloc.c: - Replace use of hfs_getblk() in balloc.c with a local function - (get_new_node()) that doesn't retry, since B-trees can't shrink. - - * hfs.h, hfs_btree.h, hfs_sysdep.h, mdb.c, extent.c: - Make hfs_buffer a typedef. - - * inode.c, hfs.h, hfs_sysdep.h, dir.c: - Change hfs_sysentry to a typedef. - Rename 'sysentry' field of (struct hfs_cat_entry) to 'sys_entry'. - - * super.c, mdb.c, catalog.c: - Rename hfs_cat_sync() to hfs_cat_commit() and call it - from hfs_mdb_commit() rather than from hfs_write_super(). - - * catalog.c, file.c: - Minimize the calls to hfs_mdb_dirty(). Now called when: - 1) A buffer holding a volume bitmap block is dirtied. - 2) A dirty B-tree node is written back to the buffers. - 3) A dirty catalog entry is written back to the buffers. - - * hfs_sysdep.h, hfs.h: - Make hfs_sysmdb a typedef. - -Sun Aug 11 08:46:10 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_sysdep.h, extent.c, hfs.h: - Replace hfs_mdb_{lock,unlock} with more portable - scheme using a wait queue in the MDB. - - * hfs.h, hfs_btree.h, hfs_sysdep.h, bnode.c, catalog.c, binsert.c: - Make hfs_wait_queue a typedef'd pointer to a (struct wait_queue). - Rename hfs_wait_on() to hfs_sleep_on(). - - * catalog.c, hfs_sysdep.h, super.c, bfind.c, bnode.c, balloc.c: - Implemented hfs_dev_name() in hfs_sysdep.h - as a portable call to produce a device name. - - * super.c, hfs.h, mdb.c: - Rename hfs_mdb_read() to hfs_mdb_get(), and don't take a - 'sys_mdb' argument. That's the callers responsibility. - - * sysdep.c, Makefile: - Remove the pointless file sysdep.c - - * README.sgml: - Clean up the "System Requirements" section. - -Sat Aug 10 22:41:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * sysdep.h, sysdep.c, super.c, hfs_sysdep.h, mdb.c, string.c, - hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree_private.h, hfs_btree.h, - file_cap.c, file_dbl.c, file_nat.c, hfs.h, file.c, dir_nat.c, - extent.c, dir.c, dir_cap.c, dir_dbl.c, catalog.c, bnode.c, brec.c, - btree.c, binsert.c, bitmap.c, bitops.c, bfind.c, bins_del.c, - Makefile, balloc.c, bdelete.c: - Includes the hfs.h that was missing from the previous check in. - MAJOR include-file cleanup: - hfs_btree.h merged into hfs.h - hfs_btree_private.h renamed hfs_btree.h - sysdep.h renamed hfs_sysdep.h - Fixed some minor portability fixes shown up by the header split. - - * README.sgml: - Add instructions for a dealing with a missing linux/version.h - - * hfs_fs.h, mdb.c, string.c, catalog.c, extent.c, btree.c, bitmap.c, - bitops.c, bnode.c, brec.c, bins_del.c, binsert.c, bdelete.c, bfind.c, - balloc.c: - Major split of hfs_fs.h into Linux-specific - part (hfs_fs.h) and HFS-specific part (hfs.h). - - * file.c, extent.c: - Move hfs_getblk() from file.c to extent.c - - * sysdep.h, super.c, mdb.c, hfs_fs_sb.h, hfs_fs.h, file.c, extent.c, - catalog.c, bnode.c, bitmap.c: - Make the field 's_mdb' in (struct hfs_sb_info) a pointer to - the MDB, rather than the actual MDB. This allowed the definition - of (struct hfs_mdb) to be moved from hfs_fs_sb.h to hfs_fs.h. - - * ccache.c, hfs_fs.h, Makefile, catalog.c: - Merged ccache.c and catalog.c into the latter. - Moved definition of (struct hfs_cat_rec) into catalog.c - - * extent.c: - Oops! Last set of changes didn't compile but they're OK now. - - * hfs_btree.h, hfs_fs.h, mdb.c, ccache.c, extent.c, btree.c: - Move the definition of (struct hfs_raw_extent) inside - extent.c and treat it as simple array of U16's elsewhere. - - * hfs_fs.h, dir_dbl.c, dir_nat.c, ccache.c, catalog.c, dir_cap.c: - Make hfs_cat_next() return the CNID and cdrType of the entry. - Now catalog.c and ccache.c are the only files which - depend on the structure of a catalog record on disk. - - * dir.c, hfs_fs.h, catalog.c: - Replace hfs_cat_new_{file,dir}() with hfs_cat_{create,mkdir}() - which are wrappers for what used to be hfs_cat_create(). - - * hfs_fs.h, mdb.c, super.c, Makefile: - Split super.c into super.c (Linux stuff) and mdb.c (MDB stuff). - - * super.c, hfs_fs_sb.h: - Add the MDB field 'drAtrb' to (struct hfs_mdb) as the field 'attrib'. - - * hfs_fs_sb.h, super.c: - Split hfs_read_super() into hfs_read_super() and hfs_mdb_read(). - - * super.c, hfs_fs_sb.h: - Remove the unneeded 'hs' field from (struct hfs_mdb). - - * TODO: - Remove item about hfs_notify_change() needing to update metadata. - - * inode.c, hfs_fs.h, hfs_fs_sb.h, file_cap.c, file_dbl.c, file_nat.c, - file.c, dir.c: - Add a flags argument to hfs_{cap,dbl,nat}_buildmeta() so that - it only builds the parts that are currently out-of-date. - Call hfs_{cap,dbl,nat}_buildmeta() through hfs_update_meta() - in hfs_notify_change() and hfs_rename() to update the metadata. - - * dir.c: - Make test for normal dir in update_dirs_{plus,minus}() more explicit. - - * inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c, - file.c, README.sgml, dir_cap.c: - Resolve the "meta-data" vs. "metadata" rivalry in favor of the latter. - - * btree.c: - Simplify some debugging code. - - * hfs_btree_private.h, bnode.c, btree.c, balloc.c: - Put the in-core copy of the header node IN the - B-tree structure rather than just a pointer to it. - - * hfs_btree_private.h, btree.c, bnode.c: - Have hfs_btree_commit() call hfs_bnode_commit() - to commit the header and root nodes. - - * hfs_fs.h, super.c, hfs_btree_private.h, btree.c, hfs_btree.h, - balloc.c: - Change hfs_commit_mdb() to hfs_mdb_commit(). - Make hfs_mdb_commit() call hfs_btree_commit(). - Move code to update B-tree size and extent - from hfs_btree_extend() to hfs_btree_commit(). - Make hfs_btree_extend() call hfs_mdb_commit(). - - * super.c: - Change hfs_commit_super() to hfs_commit_mdb(). - - * btree.c, bnode.c, bfind.c: - Fixed up broken debugging code and error messages. - - * super.c, hfs_btree_private.h, btree.c, hfs_btree.h, bdelete.c, - binsert.c, balloc.c: - Now use write-back caching of B-tree header fields. - - * hfs_fs.h: - Get rid of the add{16,32}() inlines as they are no longer used. - - * hfs_btree_private.h, binsert.c, btree.c, bdelete.c, bfind.c, balloc.c: - All the needed fields of the B-tree header are - now cached for reading, but not yet writing. - - * TODO: - Remove "Implement write count" from TODO list. - - * file.c, super.c, bnode.c: - Implement write count. - - * catalog.c: - Fix directory entry counting in hfs_cat_move(). - - * balloc.c: - Simplify hfs_btree_extend(), since the allocation - request will get rounded up to the clumpsize. - - * extent.c: - Honor clumpsize when allocating blocks to files. - - * file_cap.c, file_dbl.c, file_nat.c, super.c, dir.c, file.c, - ccache.c, catalog.c, balloc.c: - Mark 44 functions in need of commenting. - - * hfs_fs_sb.h, super.c, extent.c, hfs_fs.h, ccache.c, btree.c, balloc.c: - Record clumpsize in allocation blocks rather than 512-byte blocks. - - * sysdep.h, super.c, TODO, balloc.c, hfs_fs_sb.h: - Now updates the backup MDB when a B-tree grows. - - * extent.c: - hfs_extent_free() had test against NULL backward. - The result is that access to a file with extents in the extents - B-tree would result in an infinite loop in hfs_cat_put(). - - * hfs_fs_sb.h, super.c, hfs_fs.h: - Reorganize partition map code to get size of partition - in preparation for dealing with the alternate MDB. - -Fri Aug 9 03:25:13 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * Makefile: - Add make rules for README.{ps,info} - - * README, README.sgml, DOC, FAQ, Makefile, .cvsignore, - Merge CHANGES into ChangeLog. - Merge DOC, FAQ and README into README.sgml. - Add make rules for building README.{txt,dvi} - - * BUG_INFO, Makefile: - Added a BUG_INFO script which attempts to collect some useful - information which I'd like to see in every bug report I receive. - - * Makefile, version.c: - Added version.c which contains a version string. - -Thu Aug 8 21:48:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * trans.c: - Fix Latin-1 -> Macintosh filename mapping to change colons to pipes. - - * trans.c: - Fixed Mac->Latin-1 translation to behave as documented for the - extended 8-bit characters without corresponding Latin-1 characters. - - * inode.c, super.c, file.c, hfs_fs_i.h, hfs_fs_sb.h, DOC: - Added a conv={binary,text,auto} mount option similar to that of the - msdos, hpfs and iso9660 filesystems, but applying only to data forks. - As compared to those filesystems, HFS has the advantage that only a - single CR need be converted to a NL, rather than a CR/NL sequence, so - it is quite safe to seek in the file. - Additionally the 'Type' field is far more reliable indicator of text - files than a file extension. - - * super.c: - Simplified parsing of mount options. - - * super.c: - Oops! The part=<n> mount option was being parsed in octal! - - * TODO: - Remove "case=lower" from the list of goals. - - * super.c, hfs_fs.h, hfs_fs_sb.h, string.c, dir_dbl.c, dir_nat.c, - dir_cap.c, DOC: - Resurrect the case={asis,lower} mount option. - - * dir.c: - Simpler test for "normal" directory in update_dirs_{plus,minus}(). - - * hfs_fs_sb.h, super.c, dir.c, hfs_fs.h, catalog.c, DOC: - Add mount options to specify what Type and Creator will be used for - new files and change the default from NULLs to "????". - -Wed Aug 7 11:32:22 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * catalog.c: - In hfs_cat_next() use entry->cnid rather than the key of the initial - brec to identify the end of the directory. - - * README: - Update for pre-0.7 version. - - * hfs_fs.h: - Create versioned module if CONFIG_MODVERSIONS is set in linux/config.h - - * TODO: - Note need for special steps for unaligned accesses on some machines. - - * FAQ: - Added Q0: What is HFS? - Added Q7: Does hfs_fs work w/ 400k and 800k diskettes? - Brought Q6 (about writability) up to date. - Made a few other answers more verbose. - -Tue Aug 6 00:58:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * Makefile: - Changed 'snapshot' rule to include cvs tag command. - - * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c: - Implemented readers half of dir locking scheme so readdir() should - produce consistent results and count_dir_entries() is not race prone. - - * catalog.c: - hfs_cat_move() was calling hfs_cat_decache() after changing - the key rather than before, corrupting the hash lists. - -Mon Aug 5 14:03:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs.h, catalog.c: - Implemented the writers half of a locking scheme for directories. - - * inode.c: - Fixed a serious bug in hfs_notify_change() that would allow a chmod() - on directory meta-data and would cause the directory inode (if it was - in memory at the time) to change into a file inode. - - * inode.c: - Fixed a problem with write permissions on directory meta-data. - - * dir_dbl.c, dir_nat.c, dir_cap.c: - hfs_{cap,dbl,nat}_readdir() now return the correct value in the 'd_ino' - field of the dirent for all cases, something I think has always been - done incorrectly until now. - - * dir_nat.c, inode.c, dir_cap.c: - In hfs_{cap,nat}_lookup() take advantage of the - 'file_type' field of (struct hfs_inode_info). - - * TODO: - Removed two accomplished goals (rename() and improved readdir()). - - * inode.c, dir_dbl.c, dir_nat.c, hfs_fs_i.h, dir.c, dir_cap.c: - Rewrite hfs_{cap,dbl,nat}_readdir() to take advantage of hfs_cat_next(). - They now use a uniform 'i_size' for all inodes for a given directory. - This simplifies update_dirs_{plus,minus}() and eliminates the need for - the 'file_size' and 'dir_link' fields of (struct hfs_inode_info). - For the CAP and Netatalk schemes the meta-data directories are now the - last entries rather than coming just after '.' and '..'. This is in - preparation for the day when we can write to the files in those - directories, and ensures that when using 'tar' to copy HFS filesystems - the file or directory will be created before the meta-data is written. - Otherwise we could be stuck writing meta-data and not knowing if it is - for a file or a directory! - - * ccache.c: - Updated count_dir_entries() for new hfs_cat_next(). - - * hfs_fs.h, catalog.c: - hfs_cat{nth,next}() no longer take a 'types' argument, - so they now return all entries. - hfs_cat_next() now uses the ParID of the key to detect - the end of the directory. - hfs_cat_nth() now accepts n=0 as a valid input, requesting the thread. - - * trans.c, string.c, super.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, - dir_dbl.c, catalog.c: - Rename (struct hfs_cname) to the more appropriate (struct hfs_pstr). - - * hfs_fs.h, hfs_btree.h: - Move some constants from hfs_fs.h to hfs_btree.h - - * bdelete.c, hfs_btree.h: - Remove hfs_bdelete_brec() from public B-tree interface. - - * hfs_btree_private.h, hfs_fs.h, btree.c, hfs_btree.h, bnode.c, brec.c, - bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c, Makefile: - Split B-tree stuff into public and private parts: - brec.c split into bfind.c and brec.c - hfs_btree.h split into hfs_btree.h and hfs_btree_private.c - - * inode.c: - The tests and sets of the HFS_FIL_LOCK bit where all reversed! - - * hfs_fs.h, ccache.c: - Redo some ccache stuff, removing the 'error' field from - (struct hfs_cat_entry) and ensuring that hfs_cat_put() - will not sleep on an uninitialized entry. - -Sun Aug 4 23:43:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * sysdep.h: - Change swap{16,32}() back to macros since hton[ls]() are functions. - - * hfs_fs.h, ccache.c: - Use only lowest order byte of parent CNID in hashing a catalog key. - - * bdelete.c: - The "improved" bdelete() was TOO paranoid looking for missing parents. - - * ccache.c: - Get rid of pointless swap16const(0). - - * hfs_fs.h, inode.c, extent.c, ccache.c, dir_cap.c, dir_nat.c, - binsert.c, catalog.c: - Store cnid and associated constants in big-endian byte order. - This reduces the number of byte-order swaps required. - - * sysdep.h: - Make swap32() and swap16() inline functions. - - * dir_nat.c, dir_cap.c, dir_dbl.c: - Added hfs_rename() to the inode_operations for normal directories. - - * dir.c, hfs_fs.h: - Added hfs_rename() and cleaned up hfs_{create,mkdir,unlink,rmdir}(). - - * catalog.c: - Added the missing check for moving a directory into itself. - - * catalog.c, ccache.c, hfs_fs.h: - Implement a nearly ideal hfs_cat_move(). - It still needs to prevent moving a directory into itself. - The functions hfs_cat_{create,delete,move}() still need work with - respect to their atomicity (especially vs. readdir). - - * bdelete.c: - Fixed a serious bug in hfs_bdelete_brec() that would yield a corrupted - b-tree when the first record in a bnode was deleted. - Made bdelete() more aggressive when checking for missing parents. - -Sat Aug 3 06:11:50 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * btree.c, super.c: - Fixed a problem that caused a kernel oops when no HFS filesystem - is found. - -Wed Jul 24 13:06:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * catalog.c: - Remove race in hfs_cat_create() that could overflow directory valence. - - * catalog.c: - Fix hfs_cat_create() so the parent directory doesn't get deleted - out from under it. Otherwise we could have created files and - directories in deleted directories. - - * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c: - Redo hfs_cat_{next,nth}() in terms of which entry types to - allow, rather than which to skip. - - * catalog.c: - The function hfs_cat_create() would fail to hfs_cat_put(entry) if - the 'record' argument was invalid or if the 'result' argument was NULL. - - * dir.c: - The functions hfs_{create,mkdir,unlink,rmdir} all failed to - call iput() when their arguments conflicted with a reserved name. - - * catalog.c, hfs_fs_sb.h: - Start over on rename(). Still unfinished. - Fix silly bug in hfs_cat_create() that made it always fail. - - * ccache.c: - Fix byte-order bug in write_entry(). - -Tue Jul 23 12:12:58 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c: - Remove the macros KEY() and PARENT() since the key is now easy - to access through the catalog entry. - Replace the macros NAME{IN,OUT}() with inline functions - hfs_name{in,out}() to gain type checking of arguments. - - * catalog.c: - Remove the macro TYPE(). - - * inode.c, file_dbl.c, file_nat.c, file.c, file_cap.c: - Remove the #define's of the unused macro KEY(). - - * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, dir.c: - Replace hfs_lookup_parent() in dir.c with hfs_cat_parent() in catalog.c. - This new function performs locking to protect against rename() changing - the parent during I/O. - It is also intended for use with files as well as directories. - Change hfs_{cap,dbl,nat}_lookup() to use the new function. - - * dir.c, hfs_fs.h, catalog.c: - Remerge hfs_cat_{create,mkdir}() into hfs_cat_create() and resurrect - hfs_cat_new_{file,dir}(). - Fix hfs_cat_{create,delete} to use the improved catalog cache for - locking in place of directory-level create/delete locks. - Fix hfs_{create,mkdir}() to use the new hfs_cat_create(). - - * hfs_fs.h, ccache.c: - Rewrite parts to remove need for specialized create/delete locking. - Use new case-independent hash function. - Fix bug in hfs_cat_get() that would read an entry w/o locking it. - Call hfs_relinquish() before retrying a deleted entry in hfs_cat_get. - If there is a read error, then don't retry in hfs_cat_get(). - Remove unused 'version' field from (struct hfs_cat_entry). - - * sysdep.h: - Add hfs_relinquish(), a system-independent alias for schedule(). - - * hfs_fs.h, string.c: - Add hfs_strhash(), a simplistic case-independent hash function. - - * hfs_fs.h, inode.c: - Make hfs_iget() an inline function. - - * TODO: - Add a few goals and removed those that have been achieved. - - * Makefile: - Add ccache.c to list of source files. - Add rule for *.s files and include them in the 'clean' rule. - -Wed Jul 17 17:22:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * sysdep.h, trans.c, string.c, super.c, hfs_fs_i.h, hfs_fs_sb.h, - inode.c, hfs_btree.h, hfs_fs.h, file_dbl.c, file_nat.c, extent.c, - file.c, file_cap.c, dir_dbl.c, dir_nat.c, ccache.c, dir.c, - dir_cap.c, btree.c, catalog.c, bnode.c, brec.c, balloc.c: - Total rewrite of the inode-handling stuff to be centered around - a catalog entry cache (ccache.c). This results not only in a far - more sensible way of doing things, but also removed many race - conditions. (The source and object code both got smaller too!) - Many small "undocumented features" were also fixed. - Replace HFS_CNAME with (struct hfs_cname). - rename() has been temporarily abandoned. - -Thu Jul 11 01:14:38 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * dir.c: - As written hfs_lookup_parent() had two overlapping read requests - in the catalog tree. This could have led to deadlock. - -Wed Jul 10 09:27:00 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * catalog.c, hfs_fs.h, bdelete.c: - More work on getting rename() fleshed out. Still not done. - Before I can finish it looks like I'll need to build a - mechanism for exclusive access to the catalog tree. There - just doesn't seem to be any other way to get proper POSIX - semantics without a bunch of race conditions elsewhere. - - * hfs_fs.h, inode.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c: - More work on the still incomplete rename() code. - Merge hfs_cat_add_{dir,file}() into hfs_cat_create(). - Add file-thread support to hfs_cat_{create,delete,rename}. - -Tue Jul 9 09:43:15 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * inode.c, dir_dbl.c, dir_nat.c, extent.c, dir_cap.c: - The indirect (struct hfs_file) was causing blocks not to be freed - when files where deleted, and an omission in hfs_put_inode() was - preventing the inode from getting freed. Both are now fixed. - - * hfs_fs.h, dir_dbl.c, dir_nat.c, hfs_btree.h, catalog.c, dir_cap.c, - bdelete.c: - Made unlink() and rmdir() more race resistant and did some more - work on the still incomplete code for rename(). - - * btree.c, bnode.c: - There was a serious race condition in the bnode cache, so - hfs_bnode_find() is now modeled after Linus's inode cache. - -Mon Jul 8 10:33:38 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs_i.h, inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, - dir_nat.c, file.c, dir.c, dir_cap.c: - More changes to layout of (struct hfs_inode_info). - - * super.c, inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs_i.h, - hfs_fs_sb.h, file_nat.c, hfs_fs.h, file.c, file_cap.c, file_dbl.c, - Makefile, catalog.c: - Implemented new layout for (struct hfs_inode_info) resulting in the - elimination of lots of duplicated code for hfs_*_write_inode(), - hfs_*_put_inode() and *_open() functions. - Merged inode_*.c files back into inode.c. - Not fully tested. - - * TODO: - Add a few more of my goals to the list. - - * README: - Documentation updates. - - * inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h, hfs_fs_i.h, - file.c, file_cap.c, file_dbl.c, file_nat.c, catalog.c: - (struct hfs_file) and metadata are read when file is opened or - truncated and are released by iput(). - -Sun Jul 7 23:55:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, dir_nat.c, hfs_fs.h, - hfs_fs_i.h, dir_cap.c, dir_dbl.c, catalog.c, dir.c: - (struct hfs_dir) is now inside (struct hfs_inode_info) once again. - - * inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, file_nat.c, - hfs_btree.h, hfs_fs.h, extent.c, file_cap.c, file_dbl.c, dir_nat.c, - dir_cap.c, dir_dbl.c, btree.c, catalog.c, dir.c, bpath.c, brec.c, - bins_del.c, binsert.c, bnode.c, bfind.c, balloc.c, bdelete.c, - Makefile: - Remerged (struct hfs_bpath) and (struct hfs_brec), merging the - files bfind.c and bpath.c as a resurrected brec.c. - -Sat Jul 6 21:47:05 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs.h, hfs_fs_i.h, - file_cap.c, file_dbl.c, file_nat.c, hfs_btree.h, dir_nat.c, extent.c, - dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bfind.c, bpath.c, - binsert.c, bdelete.c: - Renamed (struct hfs_brec_key) to (struct hfs_bkey). - -Tue May 28 07:53:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * inode_cap.c, catalog.c: - Spelling fixes. - - * inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h, - hfs_fs_i.h, hfs_fs_sb.h, file.c, file_dbl.c, file_nat.c, dir_dbl.c, - dir_nat.c, extent.c, dir.c, dir_cap.c, catalog.c: - Structures got too big, so I had to add a layer of indirection - to (struct hfs_inode_info). - This means we must clear_inode() in inode_put(). - -Mon May 27 01:32:42 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * catalog.c, file_cap.c: - Some sizeof() stuff now uses variable not type. - - * hfs_fs.h: - Make HFS_I() and HFS_SB() inline to gain type checking. - -Sun May 26 13:34:17 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * dir_nat.c: - Oops. Had left some debugging printk()s in place. - - * file_dbl.c, file_nat.c, file_cap.c: - Cleaned up variable names for consistency. - - * hfs_fs_sb.h: - Add a couple 'const's to function typedefs. - - * hfs_fs.h: - Add and update function prototypes. - Cleaned up type names. - Fix debugging malloc code. - Add hfs_iget_by_name() as an inline function. - - * sysdep.h: - Remove extra semicolon from macro definitions. - - * super.c: - Use new hfs_iget_by_name() to get root inode. - - * extent.c: - Cleaned up some variable naming for consistency. - - * catalog.c: - Added (untested) code for hfs_cat_move_file(). - - * catalog.c: - Fix one missed call to hfs_cat_build_key(). - Make hfs_cat_add_{file,dir}() take a cat_entry as an argument. - Add hfs_cat_new_{file,dir}() to generate new cat_entry's. - - * dir_dbl.c, dir_nat.c, dir.c, dir_cap.c: - Cleaned up type and variable names. - Updated calls to hfs_cat_build_key() and NAMEOUT() - Use new hfs_iget_by_*() calls. - - * inode_cap.c, inode_dbl.c, inode_nat.c: - Cleaned up type and variable names. - - * inode.c: - Update calls to hfs_cat_build_key(). - Cleaned up type and variable names. - Implemented a hierarchy of hfs_iget_by*() calls. - - * catalog.c: - Change hfs_cat_build_key() to take a HFS_CNAME as input. - - * btree.c: - Initialize lsize and psize fields of file. - - * trans.c: - Now passes type HFS_CNAME and has name/len in "normal" order. - -Tue May 21 07:02:34 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * bnode.c: - Attempt to read invalid bnode would have led to an infinite loop under - certain circumstances. One way to cause this was with an invalid - partition table which points beyond the end of the device. - -Sat May 11 12:38:42 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * sysdep.h, sysdep.c, inode_dbl.c, inode_nat.c, super.c, inode_cap.c, - inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c, file_nat.c, - hfs_btree.h, extent.c, file.c, file_cap.c, dir_nat.c, dir.c, - dir_cap.c, dir_dbl.c, btree.c, catalog.c, bitmap.c, bitops.c, - bnode.c, bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c: - Another big wave of portability-oriented changes. - -Tue May 7 11:28:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * super.c, sysdep.c, sysdep.h, inode_cap.c, inode_dbl.c, inode_nat.c, - hfs_fs_i.h, inode.c, file_nat.c, hfs_btree.h, hfs_fs.h, file.c, - file_cap.c, file_dbl.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c, - btree.c, catalog.c, dir.c, bnode.c, bpath.c, binsert.c, bitmap.c, - bitops.c, bdelete.c, bfind.c, bins_del.c, Makefile, balloc.c: - Start a big move to abstract all the Linux-specific stuff - out of the lower levels. Created sysdep.[ch] to hold it. - - * FAQ, TODO: - Bring some documentation up-to-date. - -Fri May 3 20:15:29 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * super.c, inode_dbl.c, inode_nat.c, inode.c, inode_cap.c, extent.c, - hfs_fs.h, hfs_fs_i.h, dir_dbl.c, dir_nat.c, catalog.c, dir.c, - dir_cap.c, bpath.c, btree.c, binsert.c, bnode.c: - "FID reform": 'fid' became 'cnid' (Catalog Node ID), and is now - a field in (struct hfs_file). The new name is more consistent - with Apple's documentation. The presence of 'cnid' in (struct - hfs_file) help move more of the code toward OS-independence. - - * inode_nat.c, super.c, trans.c, inode.c, inode_cap.c, inode_dbl.c, - hfs_fs.h, file_cap.c, file_dbl.c, file_nat.c, dir_nat.c, extent.c, - file.c, dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bnode.c, - bpath.c, bins_del.c, binsert.c, bitmap.c, bitops.c, bdelete.c, - bfind.c, balloc.c: - A lot of changes in what headers are included and in what order. - -Sat Apr 27 12:28:54 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * FAQ: - Updated for current writability status. - - * .cvsignore: - Added ChangeLog. - - * file_dbl.c, file_nat.c, file_cap.c, file.c, dir_dbl.c, dir_nat.c, - dir_cap.c: - Added the default fsync() to all file_operations structures. - - * dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c: - Add rmdir() for normal directories. - - * binsert.c: - I had messed up insertion so that is would sometime fail to - split the root, but its OK now. - - * dir.c: - hfs_do_unlink() decremented directory counts rather than file counts. - -Wed Apr 24 13:20:08 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs.h, bnode.c, hfs_btree.h: - Fixed a couple more type size assumptions. - - * hfs_fs.h, balloc.c, bitmap.c, bitops.c: - "Portable" bitmap handling was wrong for just about everything but - the i386 and the "inverse big-endian" bit ordering that I thought - the m68k port was using. It seems the m68k port is now using standard - big-endian bit-numbering conventions. - This code is now correct for the standard big- and little-endian bit - orderings. (which should cover all Linux systems?) - Also no longer assumes sizeof(long) == 4, though that might still be - a problem in other parts of the code. - -Tue Apr 23 19:19:27 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * FAQ: - Bring uptodate for this snapshot. - - * Makefile: - Add FAQ to $(MISC) - - * README, TODO: - Documentation updates. - - * bdelete.c: - Spelling fixes. - - * dir_cap.c: - In unlink() don't force metadata into memory if not present. - - * bdelete.c: - Some function comments and some clean up. - - * bins_del.c: - Added missing function comment for hfs_bnode_update_key(). - - * binsert.c, bitmap.c: - Spelling and grammar corrections to comments. - - * hfs_btree.h, hfs_fs.h, bins_del.c, binsert.c, Makefile, bdelete.c: - Clean up of hfs_bdelete(), splitting bins_del.c into three files: - bins_del.c, binsert.c and bdelete.c - - * bpath.c, bins_del.c: - hfs_bdelete() is now working "correctly", but needs some cleaning up. - -Mon Apr 22 05:35:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs.h, bpath.c, hfs_btree.h, bins_del.c, bnode.c, balloc.c, - bfind.c: - Rewrite bnode handling, heading toward a more write-behind approach. - Have done away with HFS_LOCK_BLIND. - - * inode_dbl.c, inode_nat.c, extent.c, hfs_fs_i.h, inode_cap.c: - Was trying to truncate resource fork of directories! - -Sun Apr 21 08:15:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * balloc.c: - Updated to use truncate() to grow full trees. - - * extent.c, hfs_fs.h, file.c, inode.c: - Added truncate() for normal files. - - * bins_del.c: - hfs_bdelete() fixes for handling removal of root. - - * inode_cap.c, inode_dbl.c, inode_nat.c: - Release storage for deleted files in hfs_*_put_inode(). - - * bitmap.c: - Make len=0 valid for hfs_{set,clear}_vbm_bits(). - - * super.c, inode.c, hfs_fs_i.h, hfs_fs_sb.h, btree.c, balloc.c: - Changed from clumpsize to clumpblks. - - * inode_nat.c, hfs_fs.h, inode_cap.c, inode_dbl.c, btree.c, extent.c, - balloc.c: - Some extent-related changes in preparation for truncate() support. - -Sat Apr 20 10:59:13 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, inode_dbl.c, - dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c: - Removed dir.valence from hfs inode. - Added unlink(), but still need truncate() and some more support - in hfs_*_put_inode() to free the disk space used by deleted files. - - * bnode.c: - Check for NULL bnode in hfs_bnode_relse(). - - * bins_del.c: - Fixed a byte-order problem in bdelete_nonempty(). - - * hfs_fs.h, bnode.c, bpath.c, hfs_btree.h, balloc.c, bins_del.c: - First attempt at hfs_bdelete(). - - * dir.c: - The Finder would display strange things if it couldn't set frView. - Therefore initialize frView field for new directories. - - * file_cap.c, file_dbl.c, file_nat.c, hfs_fs.h: - Define User/Finder info fields of catalog entry in more detail. - - * hfs_fs.h: - HFS_BFIND_DELETE should require exact match. - - * dir.c: - Set "record in use" bit of filFlags for new files. - - * inode.c: - Was doing the wrong thing with i_ctime. - - * dir_nat.c, dir_cap.c, dir_dbl.c: - Added some missing updates to the inode in hfs_*_{create,mkdir}(). - -Sun Apr 14 00:10:52 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs.h, file_dbl.c, file_nat.c, file.c: - Work around the ever-changing type of f_reada. - -Sat Apr 13 00:43:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * bpath.c, bfind.c: - Spelling corrections in comments. - - * bins_del.c: - ifdef out shift_left() until it is actually used. - - * hfs_btree.h, hfs_fs.h, bins_del.c, bpath.c, bfind.c: - Cleaned up code related to 'flags' argument to hfs_bpath_find(). - -Fri Apr 12 23:30:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * bpath.c: - Updated comments. - Rewrote hfs_bpath_init() and hfs_bpath_next(). - - * hfs_btree.h: - Updated prototype for hfs_bpath_init(). - - * bins_del.c: - Updated call to hfs_bpath_init(). - - * inode.c, inode_cap.c, inode_dbl.c, inode_nat.c, extent.c, file_cap.c, - file_dbl.c, file_nat.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, - dir.c: - Renamed hfs_brec_relse() to hfs_brelse(). - - * hfs_fs.h, hfs_btree.h: - Updated prototypes to reflect new names in bpath.c - - * bins_del.c: - Updated calls to functions in bpath.c - Updated comments. - - * Makefile: - Renamed brec.c to bpath.c - - * bfind.c: - Updated calls to functions in bpath.c - Added hfs_brelse() which was previously hfs_brec_relse() in brec.c - - * bpath.c: - brec.c renamed to bpath.c - Functions renamed to reflect their current actions. - Comments are still out of date. - hfs_brec_relse() renamed to hfs_brelse() and moved to bfind.c - - * brec.c: - brec.c renamed to bpath.c - -Wed Apr 10 07:20:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs.h, extent.c, hfs_btree.h, brec.c, dir.c, bfind.c, - bins_del.c: - Backed-out changes to hfs_binsert() that added the ability to - return the new record, since it will probably not ever be needed. - - * extent.c: - Since 1.3.45 truncate() has locked the file, so there is no need - for all the things I've been doing to hfs_file_extend() & new_extent(). - Those two functions have been cleaned up a bit (similar to older forms). - - * extent.c: - hfs_file_extend() now more "robust", but new_extent() is still - not fully "concurrency safe." - -Tue Apr 9 09:01:18 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * bins_del.c: - Made split() inline. - - * inode.c, dir_nat.c, hfs_fs.h, dir_cap.c: - Added hfs_itry() to get in-core inodes. - - * inode_dbl.c, inode_nat.c, hfs_fs.h, inode.c, inode_cap.c, file_dbl.c, - file_nat.c, hfs_btree.h, extent.c, file_cap.c, dir_cap.c, dir_dbl.c, - dir_nat.c, brec.c, catalog.c, dir.c, bins_del.c, bnode.c, - bfind.c: - Rewrite of all the (struct hfs_brec) stuff. - -Mon Apr 8 21:50:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * btree.c, extent.c, bnode.c: - Fixed format strings in a few debugging printk()'s. - - * brec.c, hfs_fs.h: - Removed hfs_brec_relse_one(). - - * hfs_fs.h, bnode.c, brec.c, hfs_btree.h, bfind.c, bins_del.c, balloc.c: - (struct hfs_bnode_ref)s are now returned by value rather than reference - and they are in (struct hfs_brec) rather than pointed to. Cuts down on - a lot of kmalloc() and kfree() traffic. - - * hfs_fs.h, dir.c, extent.c, bins_del.c: - Modified hfs_binsert() to be able to return the new record. - - * bins_del.c, hfs_btree.h: - Added shift_left(), still untested. - - * bins_del.c: - new_root() was missing its comment. - - * super.c, trans.c, hfs_fs_i.h, inode.c, inode_dbl.c, inode_nat.c, - file_nat.c, hfs_btree.h, hfs_fs.h, file.c, file_dbl.c, dir_dbl.c, - dir_nat.c, extent.c, dir.c, dir_cap.c, bitops.c, bnode.c, brec.c, - bfind.c, bins_del.c, bitmap.c, balloc.c: - Fixed lines over 80 characters and tabified files. - - * bins_del.c: - Fixed line(s) over 80 columns. - - * trans.c, inode_nat.c, string.c, super.c, inode.c, inode_cap.c, - inode_dbl.c, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree.h, hfs_fs.h, file.c, - file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, extent.c, btree.c, - dir_cap.c, bitops.c, bnode.c, brec.c, bfind.c, bins_del.c, bitmap.c, - DOC, README, TODO, balloc.c, CHANGES: - About 150 spelling corrections. - -Sun Apr 7 23:14:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * dir_cap.c, dir_dbl.c, dir_nat.c, dir.c: - Cleaned-up check for special names in mkdir(). - - * extent.c: - More verbose error message. - - * inode_dbl.c, inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, dir.c, - hfs_fs.h: - Limit directories to 32767 entries, since Mac uses 16-bit integer. - -Fri Apr 5 07:27:57 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * FAQ: - Initial version. - - * dir_dbl.c, dir_nat.c, bins_del.c, dir.c, dir_cap.c: - Added missing function comments. - -Wed Apr 3 06:38:36 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * brec.c: - Cleaned-up code for brec->flags. - - * extent.c: - Added function comments. - - * bins_del.c: - Added function comments. - hfs_binsert() was incrementing record count even on failure. - -Mon Apr 1 08:35:51 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * extent.c: - Rewrote find_ext() and new_extent() for new hfs_btree_extend(). - Moved hfs_btree_extend() to balloc.c - Fixed potential kernel OOPS in new_extent(). - - * brec.c: - Fixed potential kernel OOPS in hfs_brec_get_root(). - Removed hfs_brec_find_first(). - Fixed return value of hfs_brec_find(). - - * bins_del.c: - Updated call to hfs_btree_extend(). - - * balloc.c: - Merged hfs_bnode_add() and hfs_btree_extend() into the later. - Commented init_mapnode(). - - * bfind.c: - Removed hfs_bfind_first(). - - * hfs_fs.h, hfs_btree.h: - Updated prototypes. - -Sat Mar 30 22:56:47 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * CHANGES, README, TODO: - Updated documentation in preparation for 0.6 release. - - * inode.c, hfs_fs.h: - Got rid of HFS_FAKE_EXEC in favor of noexec mount option. - - * inode.c, super.c, DOC, hfs_fs_sb.h: - Added "quiet" mount option, like the fat filesystem. - - * inode.c, dir_cap.c, dir_nat.c: - Pseudo-directories are read-only (at least for now). - - * hfs_fs.h, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c: - mkdir() updated to check against reserved names, but the - AppleDouble scheme still has problems with names starting with '%'. - - * dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c: - Added mkdir(). (It only took 2 tries to get it right!!) - Only works in "normal" directories and doesn't yet stop - one from creating dirs with the reserved names. - - * brec.c, extent.c, bins_del.c: - Now have a way to get an EEXIST back from hfs_binsert(). - - * btree.c, inode.c, hfs_fs_i.h, file.c, bfind.c, bnode.c, balloc.c: - Added 'dev' field to struct hfs_file. - - * hfs_fs_i.h, inode.c, btree.c, extent.c, file.c, bnode.c, brec.c, - balloc.c: - Removed duplicated fields from struct hfs_file since - even B*-trees now have that information in the inode. - - * extent.c: - zero_blocks() neglected allocation block size in computing start. - -Fri Mar 29 16:04:37 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * super.c: - hfs_statfs(): f_files and f_ffree fields are now -1, which is - documented as the value for "undefined" fields in struct statfs. - - * trans.c, inode_nat.c, string.c, super.c, inode_dbl.c, inode_cap.c, - inode.c, file_nat.c, file_dbl.c, file_cap.c, file.c, dir_dbl.c, - extent.c, dir_cap.c, catalog.c, btree.c, brec.c, bnode.c, bitops.c, - bitmap.c, bins_del.c, balloc.c: - Stylistic editing: {} for all 'for', 'while' and 'if' blocks. - I hope I didn't screw-up anything. - - * hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c, dir_nat.c: - Added creation of normal files to all three fork schemes! - Strange things may happen when trying to create "non-normal" files. - - * brec.c: - Cleaned up some debugging code. - - * hfs_fs_i.h: - File and directory counts could have overflown 16-bit integer. - - * hfs_btree.h: - Added HFS_BREC_RIGHT to help fix insertion problem. - - * extent.c: - Various fixes to hfs_{file,btree}_extend(). - - * catalog.c: - Made hfs_build_cat_key() more "correct". - - * btree.c: - Added and fixed debugging code. - - * brec.c: - Fixed overflow detection. - Added some debugging code. - - * bnode.c: - Dirtied some buffers in places that might have been missed. - Fixed some debugging code that had broken. - - * bitops.c: - hfs_count_free_bits() was running off end of bitmap. - - * bins_del.c: - Fixed various bugs, mostly related to variable-length keys. - - * balloc.c: - Had forgotten to set a bit in new mapnodes. - Node counts were overflowing 16-bit integers. - - * bitmap.c: - Oops! clear/set did opposite operation on full words. - -Wed Mar 27 10:59:07 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * hfs_fs_i.h: - Updated struct hfs_extent for concurrent access. - Also caused a slight modification to struct hfs_file. - - * hfs_fs.h, hfs_btree.h: - Added/updated prototypes. - - * balloc.c: - hfs_bnode_alloc() finished but still untested. - - * bins_del.c: - Fixed up deadlock avoidance in hfs_binsert() again. - Perhaps I even got it right this time. - - * extent.c: - hfs_file_extend() now safe under concurrent operations? - - * file.c: - hfs_getblk() now safe under concurrent operations? - -Tue Mar 26 23:26:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * btree.c: - Added call to hfs_extent_trim() to fix memory leak. - - * extent.c: - Oops, had left a "#define static" in from debugging. - - * bins_del.c: - hfs_binsert() rewritten to avoid deadlock when extending - the extents B*-tree. - - * btree.c: - Moved hfs_btree_extend() to extent.c - - * inode_nat.c, inode_cap.c, inode_dbl.c: - hfs_*_put_inode() rewritten to call hfs_extent_trim(). - - * extent.c: - Big rewrite for new struct hfs_extent: - Now keep linked list of extents. - Cache is now a pointer to a list element. - Now have 'end' field to aid decode_extent(). - New functions: - hfs_extent_trim(): frees linked list. - hfs_btree_extend(): for extending B*-trees. - Improved debugging output. - - * balloc.c: - Added hfs_bnode_add() (incomplete and uncommented). - - * btree.c: - Moved some work from hfs_btree_extend() to hfs_bnode_add(). - - * bfind.c: - Added hfs_bfind_first() as wrapper for hfs_brec_find_first(). - - * brec.c: - Added hfs_brec_find_first() to search first leaf node. - - * bins_del.c: - Added error returns to hfs_binsert() and binsert(). - - * bins_del.c: - Check to see that we really need ancestors before starting. - Check that hfs_btree_alloc() gave us enough nodes. - binsert() uses info precomputed by hfs_binsert(). - -Mon Mar 25 11:33:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * bnode.c: - Collected together the error returns in hfs_bnode_lock(). - - * Makefile: - Added ChangeLog to $(MISC). - -Wed Mar 20 19:41:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * super.c, hfs_fs.h, file.c, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c: - Removed support for kernels older than about 1.3.70 - Most of that support had been broken recently anyway. - - * super.c: - Fixed so DEBUG_MEM works w/o DEBUG_ALL. - Updated call to hfs_btree_init(). - - * hfs_fs.h: - Updated/added prototypes. - - * hfs_btree.h: - HFS_BFIND_CHAIN removed. - struct hfs_brec gets new 'flags' field with bits: - HFS_BREC_{FIRST,OVERFLOW,UNDERFLOW,UNINITIALIZED} - Removed bitmap size constants. - Changes to struct hfs_btree: - 'file' and 'cache' now structs rather than pointers. - Added 'reserved' field (used during insertion). - Added pointers to size and extent in MDB. - - * file.c: - Made hfs_getblk() public. - Removed (fil->inode == NULL) special cases. - - * extent.c: - {find,update}_ext() are no longer inline. - new_extent() fails when called for the extents tree; - previously it would hanging calling hfs_binsert(). - extend_file(): - renamed to hfs_file_extend() and made public. - fixed to work for B*-trees. - zeros-out blocks as they are allocated. - fixed bugs for (allocation block) != (physical block). - - * btree.c: - hfs_btree_{init,free}() modified for changes to struct: - 'file' and 'cache' moved back into structure - file.inode initialized to reduce special cases - hfs_btree_init() gets pointer to size in MDB instead of size. - Added hfs_btree_extend() (incomplete and uncommented). - - * bnode.c: - hfs_bnode_{alloc,free}() moved to separate file. - Removed 'const' from some function arguments - due to change in struct hfs_btree. - hfs_bnode_lock(): added WRITE/RESRV->READ transition. - - * brec.c: - hfs_brec_get_{root,child}() now take a 'keep_mask' argument - indicating when to keep ancestor nodes, and store - information about why ancestors were kept. - HFS_BFIND_CHAIN eliminated in favor of HFS_BFIND_{INSERT,DELETE} - which are now implemented using 'keep_mask'. - Added hfs_brec_relse_one() that doesn't release ancestors. - - * bins_del.c: - Lots of rewrites to cleanup insertion. - Now tries to extend tree before insertion starts. - binsert() iterative rather than recursive. - No point in keeping track as it is still not "stable". - - * balloc.c: - New file: started with hfs_bnode_{free,alloc}() - Added hfs_bnode_init() to initialize a newly allocated bnode. - hfs_bnode_free(): - Renamed hfs_bnode_bitop(). - Can set or clear a specified bit. - Gets bitmap sizes from nodes directly. - hfs_bnode_alloc(): - Returns actual node, calling hfs_bnode_init(). - Gets bitmap sizes from nodes directly. - - * bfind.c: - Removed obsolete comment from hfs_bsucc() - Removed 'const' from tree arg of hfs_bfind() - due to changes in struct hfs_btree. - - * Makefile: - Added new file: balloc.c - -Sat Mar 9 22:03:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> - - * Start of detailed CVS logging. - -Mar 09, 1996: snapshot-09Mar96 hargrove@sccm.stanford.edu (Paul H. Hargrove) - NOT AN OFFICIAL RELEASE - Fixed up debugging code that was broken by split of btree.c - Added debugging kmalloc/kfree - Fixed memory leak in hfs_bnode_relse() - -Mar 08, 1996: snapshot-08Mar96 hargrove@sccm.stanford.edu (Paul H. Hargrove) - NOT AN OFFICIAL RELEASE - now reset blocksize on device when done. - hfs_binsert done (except for the full tree case). - btree.c split up into manageable pieces (need to sort out hfs_btree.h) - -Feb 26, 1996: snapshot-26Feb96 hargrove@sccm.stanford.edu (Paul H. Hargrove) - NOT AN OFFICIAL RELEASE - Some writability. - Bug with multiple opens of meta data fixed. - Netatalk support no longer considered experimental. - -Virtually everything has changed, so I've lost track here. - -Nov 16, 1995: snapshot-16Nov95 hargrove@sccm.stanford.edu (Paul H. Hargrove) - NOT AN OFFICIAL RELEASE - Still more comments. - btree.c back to 80 columns. will do same to other files soon. - Starting with btree.c have begun to put file contents into some - sort of standard order. - Moved metadata reading to VFS open() routine and now free it in - the VFS release() routine. Much cleaner than the old way. - Unified hfs_iget by shifting scheme-dependent code into a function - pointer in the superblock. This could/should be shifted to - a VFS read_inode() routine if that can be done cleanly. - Probably lots of other changes; I've lost track. - -Nov 05, 1995: version 0.5.3 hargrove@sccm.stanford.edu (Paul H. Hargrove) - NOT AN OFFICIAL RELEASE - 1.2.x compatibility removed - Added lots of comments to btree.c and cleanup some code. The result - is that the source file doubled in size while the object - file dropped in size by 20%. - Added some comments to super.c and dir.c as well. - Cleaned up some stuff in dir.c adding some additional error checking - and moving closer to using a unified hfs_iget by migrating - common code into lookup_parent(). - Changed btree.c to use a separate bnode cache per filesystem. - Renamed a bunch of the bnode functions in btree.c - -Jun 29, 1995: version 0.5.2 hargrove@sccm.stanford.edu (Paul H. Hargrove) - BUG FIX and 1.3.x-compatibility release. - Will compile under 1.2.x or 1.3.x by changing one line in Makefile. - Started adding magic numbers to structures for "safety". - Don't strip internal symbols when linking or loading, as this made - good bug reports rather difficult. - Fixed a bug that could cause the fs to lock-up after trying to open - a non-existent file. - Fixed a bug that allowed files to appear truncated, when in fact it - is still not possible to truncate a file. - Added more/better comments to header files. - Deal with volume and b-tree bitmaps in preparation for writing. - Fixed readdir() to deal properly with the case where the directory - changes while writing to user-space. (which can't yet - actually happen, until directories are writable). - -Jun 23, 1995: version 0.5.1 hargrove@sccm.stanford.edu (Paul H. Hargrove) - BUG FIX RELEASE - Removed two debugging messages that didn't belong. - Fixed a typo that prevented modified inodes from being written to disk. - Added a missing line which prevented rmmod'ing sometimes. - Added a missing line which caused errors when modifying .finderinfo or - .resource under the CAP system. - Added a notify_change() to keep mode bits sensible, and to cause - changes to an inode to affect the data fork and resource fork - of a file together. - -Jun 22, 1995: version 0.5 hargrove@sccm.stanford.edu (Paul H. Hargrove) - Fixed a bug that was giving wrong values for i_blocks - Partly writable (can only 'touch' existing files, so far) - Removed case= mount option. It will be back eventually. - Can now deal with CDROMs (and hard disks?), many thanks to - Holger Schemel for this work. - Latin-1 filename conversion also due to Holger Schemel. - Rewritten btree operations. - -Feb 28, 1995: version 0.4 hargrove@sccm.stanford.edu (Paul H. Hargrove) - Requires Linux >= 1.1.94: depends on changes made to asm/byteorder.h - Now using string comparison code donated by ARDI (see string.c) - Code reorganized to use data structures more like ARDI's. - More code reorganization to abstract the btree operations. - Added the fork= mount option. - Added AppleDouble support. Executor, from ARDI, can now run programs - from HFS filesystems mounted with the HFS module. - -Jan 28, 1995: version 0.3 hargrove@sccm.stanford.edu (Paul H. Hargrove) - Major code reorganization. - Known for certain to work ONLY on floppies. - Started caching extents, so got faster on long file reads. - Now compiles separate from kernel tree. - Supports 5 filename conversion methods. - Supports forks, using the method from CAP. - All external symbols now start with HFS_ or hfs_ - -Jan 12, 1995: version 0.2 hargrove@sccm.stanford.edu (Paul H. Hargrove) - Should now work on all HFS volumes, but still only tested on floppies. - Got smaller and faster with some code reorganization. - Since Linus moved htons() and friends to an asm file, should now be - truly endian-independent, but still only tested on Intel machines. - Requires Linux >= 1.1.77, since Linus moved htons(). - -Jan 05, 1995: version 0.1 hargrove@sccm.stanford.edu (Paul H. Hargrove) - First release. - 1.44Mb floppies only - no resource forks - trivial name mangling only - read only - for Linux >= 1.1.75 diff --git a/fs/hfs/FAQ.txt b/fs/hfs/FAQ.txt deleted file mode 100644 index de76e8d23807..000000000000 --- a/fs/hfs/FAQ.txt +++ /dev/null @@ -1,342 +0,0 @@ - Frequently Asked Questions about the HFS filesystem for - Linux - Paul H. Hargrove, hargrove@sccm.Stanford.EDU - version 1.0.3, 27 Apr 1997 - - This document provides answers to some of the most frequently asked - questions about the HFS filesystem for Linux. It is currently pretty - rough and totally unorganized. Corrections, additions and clarifica- - tions are appreciated. The most current version of this document is - kept on The HFS for Linux Page <http://www-sccm.Stanford.EDU/~har- - grove/HFS/>. - ______________________________________________________________________ - - Table of Contents: - - 1. What is this FAQ about? - - 2. What is HFS? - - 3. How I mount AppleShare volumes? - - 4. What is the current version of the HFS filesystem. - - 5. How stable is the current version? - - 6. Is there a mailing list for discussion of the HFS filesystem? - - 7. What version of Linux do I need to be running? - - 8. Will it run on my (your processor type here)? - - 9. Will it run under (your non-Linux operating system here)? - - 10. Why can I mount some HFS CDROMs but not others? - - 11. What does ``only 1024-char blocks implemented (512)'' mean? - - 12. Why do I get a message about a bad or unknown partition table? - - 13. Can I mount multiple HFS partitions from the same Macintosh - disk? - - 14. In what ways can I write to HFS filesystems? - - 15. Does the HFS filesystem work with 400 kB or 800 kB Macintosh - diskettes? - - 16. How can I format an HFS filesystem? - - 17. How can I fsck an HFS filesystem? - - 18. Why do I get ``error -50'' messages from my Mac when using - netatalk? - - 19. Why does my Macintosh show generic application and document - icons? - - 20. How owns all the copyrights and trademarks? ;-) - - 20.1. This Document - - 20.2. The Software - - 20.3. Trademarks - ______________________________________________________________________ - - 11.. WWhhaatt iiss tthhiiss FFAAQQ aabboouutt?? - - This FAQ is about the HFS filesystem for Linux, which is available in - two forms. The stand-alone version (called hfs_fs) is a Linux kernel - loadable module implementing the Macintosh HFS filesystem. The HFS - filesystem is also included in some distributions of the Linux kernel - source (in the directory linux/fs/hfs). This version can be compiled - as a loadable module or compiled into the kernel. - - Either version allows a machine running Linux to read and write disks - from a Macintosh (almost) as though they were native Linux disks. - - 22.. WWhhaatt iiss HHFFSS?? - - HFS stands for ``Hierarchical File System'' and is the filesystem used - by the Mac Plus and all later Macintosh models. Earlier Macintosh - models used MFS (``Macintosh File System''), which is not supported. - - 33.. HHooww II mmoouunntt AApppplleeSShhaarree vvoolluummeess?? - - The HFS filesystem is for mounting local filesystems only. There is - an experimental afpfs by Ben Hekster heksterb@acm.org available from - http://www.odyssey.co.il/~heksterb/Software/afpfs/. - - 44.. WWhhaatt iiss tthhee ccuurrrreenntt vveerrssiioonn ooff tthhee HHFFSS ffiilleessyysstteemm.. - - As of version 1.0.3 of this FAQ, version 0.95 is the most recent. You - can always find the most recent version on The HFS for Linux Page - <http://www-sccm.Stanford.EDU/~hargrove/HFS/>. Announcements of new - versions are made to the comp.os.linux.announce newsgroup. - - 55.. HHooww ssttaabbllee iiss tthhee ccuurrrreenntt vveerrssiioonn?? - - Version 0.95 is considered to be ``beta'' software, so I recommend - making backups of anything important before you start playing. It is - relatively free of bugs due to lots of testing of the previous - releases. - - After a suitable period without new bugs the I will consider the - software to be ``stable'' and the version number will jump to 1.0. - - 66.. IIss tthheerree aa mmaaiilliinngg lliisstt ffoorr ddiissccuussssiioonn ooff tthhee HHFFSS ffiilleessyysstteemm?? - - There is no mailing list devoted exclusively to the HFS filesystem. - However, announcements of new versions are posted to the ``linux- - atalk'' and ``hfs-interest'' lists. I will see bug reports sent to - those lists but e-mail is more reliable (hargrove@sccm.Stanford.EDU). - - To subscribe to hfs-interest send e-mail with a body of ``subscribe - hfs-interest (your e-mail address)'' to majordomo@ccs.neu.edu. - - To subscribe to linux-atalk send e-mail with a body of ``SUBSCRIBE - LINUX-ATALK (Your full name)'' to listserv@netspace.org. - - 77.. WWhhaatt vveerrssiioonn ooff LLiinnuuxx ddoo II nneeeedd ttoo bbee rruunnnniinngg?? - - To compile and use the stand-alone distribution of the HFS filesystem - you will need Linux kernel version 2.0.1 or newer compiled with - modules enabled (CONFIG_MODULES). To compile you will need the kernel - headers which match the kernel you are running. This is covered in - more detail in the installation instructions in INSTALL.txt. - - If your kernel came with HFS in the kernel source tree then HFS should - work with your Linux version. There may be small problems with a few - of the development kernel releases. For these releases check the HFS - for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/> for - patches. - - 88.. WWiillll iitt rruunn oonn mmyy ((yyoouurr pprroocceessssoorr ttyyppee hheerree))?? - - The code is carefully written to be independent of your processor's - word size and byte-order, so if your machine runs Linux it can run the - HFS filesystem. However some younger ports don't yet have support for - loadable modules. - - Note that HFS is tested most extensively on Intel platforms. So there - could be subtle compilation problems on other platforms. If you - encounter any that are not addressed by the documentation then please - let me know. - - 99.. WWiillll iitt rruunn uunnddeerr ((yyoouurr nnoonn--LLiinnuuxx ooppeerraattiinngg ssyysstteemm hheerree))?? - - No. There is a port in progress to NetBSD. I know of no other active - porting attempts. If you are interested in porting the HFS filesystem - to another Unix-like operating system, I am interested in providing - what guidance I can. - - 1100.. WWhhyy ccaann II mmoouunntt ssoommee HHFFSS CCDDRROOMMss bbuutt nnoott ootthheerrss?? - - In the past there was a known incompatibility with some ``hybrid'' - CDROMs that appear as HFS disks on Macs and as ISO9660 disks on other - systems. I think I have fixed the problem. So, if you encounter this - particular problem or have problems with specific non-hybrid CDROMs - please e-mail me with the title and manufacturer of the CD. - - 1111.. WWhhaatt ddooeess ````oonnllyy 11002244--cchhaarr bblloocckkss iimmpplleemmeenntteedd ((551122))'''' mmeeaann?? - - This message comes from the kernel and indicates that an attempt was - made to read a 512-byte block from a device that doesn't support - 512-byte blocks. The HFS filesystem only works with 512-byte blocks, - and therefore doesn't function with these devices. Eventually it may - be able to use 1024-byte (or even 2048-byte) blocks when necessary. - Ideally the device driver should be enhanced to support 512-byte - blocks so that the various filesystems which need 512-byte blocks - don't each need to work around it. - - 1122.. WWhhyy ddoo II ggeett aa mmeessssaaggee aabboouutt aa bbaadd oorr uunnkknnoowwnn ppaarrttiittiioonn ttaabbllee?? - - If your Linux kernel doesn't understand Macintosh partition tables it - gives this warning when it can't find a partition table it recognizes. - To support partitioned media with such kernels, decoding of Mac - partition tables is done by the HFS filesystem so you should still be - able to mount the disk. However, to do so you will need to mount the - raw device (such as /dev/sdb instead of /dev/sdb4) and use the part - mount option to indicate which partition you want. - - 1133.. CCaann II mmoouunntt mmuullttiippllee HHFFSS ppaarrttiittiioonnss ffrroomm tthhee ssaammee MMaacciinnttoosshh ddiisskk?? - - Only if your kernel understands Macintosh partition tables. It the - kernel doesn't understand the Macintosh partition table, the HFS - filesystem must access the raw device. Therefore, the kernel thinks - the entire drive is in use and prevents additional mounts on it. - - 1144.. IInn wwhhaatt wwaayyss ccaann II wwrriittee ttoo HHFFSS ffiilleessyysstteemmss?? - - The HFS filesystem is as capable as the MS-DOS or VFAT filesystems, - except that certain things can only be done with a file's data fork. - - You ccaann: - - +o Create, delete and rename directories and data forks of files with - the caveat that names are case insensitive (so foo and Foo are the - same file or directory). - - +o Run Linux executables or shared libraries on an HFS disk if they - are stored in the data fork of a file. - - +o Read, write and truncate both forks of files and the Finder's - metadata of files and directories. - - +o Mmap data forks of files (and the resource fork if the filesystem - is mounted with the fork=cap option). - - +o Toggle the 'w' permission bits (as a group) of data forks. - - +o Change the i_mtime of files and directories. - - You ccaannnnoott: - - +o Create, delete or rename resource forks of files or the Finder's - metadata. Note, however, that they are created (with defaults - values), deleted and renamed along with the corresponding data fork - or directory. - - +o Run Linux executables or shared libraries on an HFS disk if they - are stored in the resource fork of a file. - - +o Mmap the Finder's metadata (when fork=cap) or AppleDouble header - files (when fork=double or fork=netatalk). - - +o Change permissions on directories. - - +o Change the uid or gid of files or directories. - - +o Set the set-uid, set-gid or sticky permission bits. - - +o Create multiple links to files. - - +o Create symlinks, device files, sockets or FIFOs. - - 1155.. DDooeess tthhee HHFFSS ffiilleessyysstteemm wwoorrkk wwiitthh 440000kk oorr 880000kk MMaacciinnttoosshh - ddiisskkeetttteess?? - - Yes and no. The software is fully capable of dealing with HFS disks - of any size. However, the 400k and 800k diskettes are written in a - physical format that is incompatible with most non-Macintosh floppy - drives. Note also that almost all 400k Macintosh diskettes are MFS, - not HFS. - - 1166.. HHooww ccaann II ffoorrmmaatt aann HHFFSS ffiilleessyysstteemm?? - - Robert Leslie (rob@mars.org) has written a package for working with - HFS filesystems (like mtools plus a graphical interface). One program - in the package is hformat which can format HFS filesystems. The - latest version can be found on the HFS Utilities home page - <http://www.mars.org/home/rob/proj/hfs/>. - - 1177.. HHooww ccaann II ffsscckk aann HHFFSS ffiilleessyysstteemm?? - - Right now you'll have to use a Macintosh to do this. However, Rob - Leslie is working on an fsck for HFS filesystems. - - 1188.. WWhhyy ddoo II ggeett ````eerrrroorr --5500'''' mmeessssaaggeess ffrroomm mmyy MMaacc wwhheenn uussiinngg - nneettaattaallkk?? - - To be compatible with netatalk's afpd you will need to use netatalk - version 1.4b1 or newer and mount the HFS filesystem with the ``afpd'' - mount option. More information is provided in the ``afpd'' subsection - of the ``Mount Options'' section of the HFS documentation (HFS.txt if - you have the stand-alone HFS distribution or - linux/Documentation/filesystems/hfs.txt if HFS is in your kernel - source tree.) - - 1199.. WWhhyy ddooeess mmyy MMaacciinnttoosshh sshhooww ggeenneerriicc aapppplliiccaattiioonn aanndd ddooccuummeenntt - iiccoonnss?? - - When using the ``afpd'' mount option the Desktop database on the disk - is not made available to Netatalk's afpd. Because of this mounting an - HFS filesystem across the network to a Macintosh may result in the - Finder showing generic application and document icons. Additionally - double clicking on a document will fail to start the correct - application. - - If the disk is writable you can make Netatalk build a new Desktop - database in its own format by holding down the Option key while - selecting the volume in the Chooser. If the disk is not writable then - these problems can be worked around by copying the application to a - local disk on the Macintosh. - - 2200.. HHooww oowwnnss aallll tthhee ccooppyyrriigghhttss aanndd ttrraaddeemmaarrkkss?? ;;--)) - - 2200..11.. TThhiiss DDooccuummeenntt - - This document is Copyright (c) 1996, 1997 by Paul H. Hargrove. - - Permission is granted to make and distribute verbatim copies of this - document provided the copyright notice and this permission notice are - preserved on all copies. - - Permission is granted to copy and distribute modified versions of this - document under the conditions for verbatim copies above, provided a - notice clearly stating that the document is a modified version is also - included in the modified document. - - Permission is granted to copy and distribute translations of this - document into another language, under the conditions specified above - for modified versions. - - Permission is granted to convert this document into another media - under the conditions specified above for modified versions provided - the requirement to acknowledge the source document is fulfilled by - inclusion of an obvious reference to the source document in the new - media. Where there is any doubt as to what defines ``obvious'' the - copyright owner reserves the right to decide. - - 2200..22.. TThhee SSooffttwwaarree - - The HFS filesystem software is Copyright (c) 1994-1997 by Paul H. - Hargrove. - - The software 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, or (at your option) - any later version. - - The software 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 the software in the file ``COPYING''; if not, write to the - Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - USA. - - 2200..33.. TTrraaddeemmaarrkkss - - +o ``Finder'' is a trademark of Apple Computer, Inc. - - +o ``Apple'', ``AppleShare'', and ``Macintosh'' are registered - trademarks of Apple Computer, Inc. - - +o ``MS-DOS'' is a registered trademarks of Microsoft Corporation. - - +o All other trademarks are the property of their respective owners. - diff --git a/fs/hfs/HFS.txt b/fs/hfs/HFS.txt deleted file mode 100644 index 5c1ae5fcce01..000000000000 --- a/fs/hfs/HFS.txt +++ /dev/null @@ -1,1042 +0,0 @@ - Macintosh HFS Filesystem for Linux - Paul H. Hargrove, hargrove@sccm.Stanford.EDU - version 0.95, 28 Apr 1997 - - This document describes version 0.95 of the Macintosh HFS filesystem - for Linux. The most current versions of this document and the - software are kept at The HFS for Linux Page - <http://www-sccm.Stanford.EDU/~hargrove/HFS/>. - ______________________________________________________________________ - - Table of Contents: - - 1. Introduction - - 2. Mounting HFS Filesystems - - 2.1. afpd - - 2.2. case={asis, lower} - - 2.3. conv={auto, binary, text} - - 2.4. creator=cccc - - 2.5. fork={cap, double, netatalk} - - 2.6. gid=n - - 2.7. names={7bit, 8bit, alpha, cap, latin, netatalk, trivial} - - 2.8. part=n - - 2.9. quiet - - 2.10. type=cccc - - 2.11. uid=n - - 2.12. umask=n - - 3. Writing to HFS Filesystems - - 3.1. Writing with fork=cap - - 3.2. Writing with fork=double - - 3.3. Writing with fork=netatalk - - 4. A Guide to Special File Formats - - 4.1. CAP .finderinfo Files - - 4.2. AppleDouble Header Files - - 5. Reporting Bugs - - 5.1. What Goes in a Bug Report - - 5.2. How to Report a Kernel Oops or GPF - - 6. Legal Notices - - 6.1. This Document - - 6.2. The Software - - 6.2.1. The Columbia AppleTalk Package for UNIX - - 6.2.2. Netatalk - - 6.3. Trademarks - ______________________________________________________________________ - - 11.. IInnttrroodduuccttiioonn - - This software implements the Macintosh HFS filesystem under Linux. It - allows you to read and write HFS filesystems on floppy disks, CDROMs, - hard drives, ZIP drives, etc. It is _n_o_t an AppleShare client. - - If you use this software, please send me a note telling of your - success or failure with it. Your feedback lets me know that this - project is not a waste of my time. - - This code is still experimental, so backup anything important before - you start playing. I'd like you to know that I've never lost any - files while using this software, or I would not release it. However, - a ``better safe than sorry'' attitude is probably best. - - If, for instance, the buffer cache were to become corrupted you could - start losing things on other disks. Because of this, if you get a - General Protection Fault, or a kernel Oops, I _s_t_r_o_n_g_l_y recommend that - you reboot before writing any files. - - 22.. MMoouunnttiinngg HHFFSS FFiilleessyysstteemmss - - Once you have the HFS filesystem compiled into the kernel or installed - as a loadable module, you will be able to use hfs as a filesystem type - option to mount. For instance, to mount a Macintosh floppy disk on - the directory /mnt using the default mount options you would execute - ``mount -t hfs /dev/fd0 /mnt''. - - The remainder of this section describes the several mount options - available to control how the HFS filesystem is mapped onto a Linux - filesystem structure. The values for the multiple-choice options - (case, conv, fork and names) can be abbreviated by their first - character. - - 22..11.. aaffppdd - - If included in the options, then the behavior of the filesystem is - changed to make it fully read-write compatible with Netatalk's afpd. - In this mode you should not use normal user-level tools to modify the - filesystem, though reading from it is acceptable. This is because the - return codes from some system calls are changed to fool afpd. These - changes will confuse many user-level tools. In particular ``rm -r'' - will loop forever. - - This option implies fork=netatalk, which in turn implies - names=netatalk. If either of these options are explicitly set to - something else they will take precedence and will confuse afpd. The - quiet option has no effect. The case= option functions normally, but - afpd usually does the same thing for you. The conv= and part= options - also function normally. - - You will probably want to use the uid=, gid= and umask= mount options. - Note that because all the files on an HFS filesystem belong to a - single user and group and have a single umask, the full AppleShare - permission scheme will not work through Netatalk. - - One additional limitation is that the Desktop database on the disk is - stored in afpd's format and is separate from any existing database - maintained by the Finder when the volume is used on a Macintosh. - Because of this mounting an HFS CDROM across the network to a - Macintosh may result in applications and documents showing up with - default application and document icons. Additionally double clicking - on a document will fail to start the correct application. Both of - these problems can be worked around by copying the application to a - local disk on the Macintosh. - - This mode is known to be compatible with afpd from Netatalk versions - 1.4b1 and 1.4b2, and known to be incompatible with the afpd from - version 1.3.3. As of this writing Netatalk version 1.4 has not yet - been released. However, it is expected that this mode will be - compatible with afpd from Netatalk version 1.4 when it is released. - - 22..22.. ccaassee=={{aassiiss,, lloowweerr}} - - default value: asis - - This option determines if Macintosh filenames are presented in their - original case or in all lowercase. Filename lookup is always case - insensitive, so either way foo and Foo refer to the same file but ls - will list Foo with case=asis, and foo with case=lower. (Same as for - the HPFS filesystem.) - - aassiiss - Filenames are reported in the case they were created with. - - lloowweerr - Filenames are reported in lowercase. - - 22..33.. ccoonnvv=={{aauuttoo,, bbiinnaarryy,, tteexxtt}} - - default value: binary - - This option controls CR<->NL conversion of Macintosh _d_a_t_a _f_o_r_k_s. Any - translation takes place only for files accessed with the read() and - write() system calls (either directly or through the stdio functions). - Access through mmap() is unaffected. (Similar to the conv= option for - the MS-DOS filesystem.) - - aauuttoo - If the Finder's type for a file is TEXT or ttro, then CR - characters are converted to NL characters when read, and NL - characters are converted to CR characters when written. - - Be warned that some Macintosh applications create files with - type TEXT even though the contents is clearly binary. - - bbiinnaarryy - No CR<->NL conversion is done. - - tteexxtt - In all data forks, regardless of the Finder's type for the file, - CR characters are converted to NL characters when read, and NL - characters are converted to CR characters when written. - - 22..44.. ccrreeaattoorr==cccccccc - - default value: ``????'' - - Specifies the 4-character string specifying the Finder's Creator for - new files. - - 22..55.. ffoorrkk=={{ccaapp,, ddoouubbllee,, nneettaattaallkk}} - - default value: cap - - This option determines how resource forks and the Finder's metadata - are represented within the structure of the Linux filesystem. - - ccaapp - The scheme used by the Columbia AppleTalk Package's AUFS. - - Associated with each directory are two special directories and a - metadata file. The directory ./bar is represented by: - - ..//bbaarr - The directory itself, containing subdirectories, the data - forks of files, and the following two special directories. - - ..//bbaarr//..rreessoouurrccee - A special directory holding resource forks of the files in - ./bar. - - ..//bbaarr//..ffiinnddeerriinnffoo - A special directory holding metadata files for the files and - subdirectories in ./bar. - - ..//..ffiinnddeerriinnffoo//bbaarr - The metadata file for the directory ./bar. - - The files in a directory are represented as three files: - - ..//ffoooo - The data fork of the file ./foo. - - ..//..rreessoouurrccee//ffoooo - The resource fork of the file ./foo. - - ..//..ffiinnddeerriinnffoo//ffoooo - The metadata file for the file ./foo. - - Additionally, the file .rootinfo in the root directory of the - HFS filesystem is a metadata file for the root directory. - - Brief documentation on the format of file containing the - Finder's metadata is included in the section ``A Guide to - Special File Formats'' in this document. More detailed - information is available in the Columbia AppleTalk Package. - - ddoouubbllee - The ``AppleDouble'' format recommended by Apple. (Apple's other - recommended format, ``AppleSingle'', is not yet implemented.) - - Associated with each directory is an AppleDouble ``header - file''. The directory ./bar is represented by: - - ..//bbaarr - The directory itself, containing subdirectories, the data - forks for files, and the header files for files and - subdirectories. - - ..//%%bbaarr - The header file for the directory ./bar, containing the - Finder's metadata for the directory. - - The files in a directory are represented as two files: - - ..//ffoooo - The data fork of the file ./foo. - - ..//%%ffoooo - The header file for the file ./foo, containing the resource - fork and the Finder's metadata for the file. - - Additionally, the file %RootInfo in the root directory of the - HFS filesystem is a header file for the root directory. This is - not quite the %RootInfo file referred to in the AppleDouble - specification. - - The header files used in this scheme are version 2 AppleDouble - header files. Their format is described briefly in the section - ``A Guide to Special File Formats'' in this document. They are - documented in detail in ``AppleSingle/AppleDouble Formats: - Developer's Note (9/94)'', available from Apple's Developer - Services Page <http://devworld.apple.com>. - - Note that the naming convention for the header file can cause - name conflicts. For instance, using Apple's 7-bit ASCII name - conversion (see the names mount option) the name %Desktop could - be interpreted either as the header file for the file Desktop or - as the file with 0xDE as the hexadecimal representation of its - first character, and "sktop" as the remaining 5 characters. The - problem arises when both files exist, since only one will be - accessible. The behavior of the HFS filesystem in the case of - such a conflict is undefined, and may change in future releases. - (If this causes problems for you, please don't report it as a - bug; I didn't design this ``standard'', Apple did.) - - nneettaattaallkk - The scheme used by the Netatalk afpd. - - Associated with each directory is a special directory and a - metadata file. The directory ./bar is represented by: - - ..//bbaarr - The directory itself, containing subdirectories, the data - forks of files, and the following special directory. - - ..//bbaarr//..AApppplleeDDoouubbllee - A special directory holding AppleDouble header files for - ./bar and the files it contains, but not for the - subdirectories it contains. - - ..//bbaarr//..AApppplleeDDoouubbllee//..PPaarreenntt - The header file for the directory ./bar, containing the - Finder's metadata for the directory. - - The files in a directory are represented as two files: - - ..//ffoooo - The data fork of the file ./foo. - - ..//..AApppplleeDDoouubbllee//ffoooo - The header file for file ./foo, containing the resource fork - and the Finder's metadata. - - The header files used in this scheme are version 1 AppleDouble - header files. They are described briefly in the section ``A - Guide to Special File Formats'' in this document. The format is - documented in detail in the ``Apple II File Type Notes'' under - the type ``$E0.0002/$E0.0003-AppleDouble'', and in Appendix B of - the ``A/UX Toolbox: Macintosh ROM Interface'' manual. - - 22..66.. ggiidd==nn - - default value: gid of the mounting process - - Specifies the group that owns all files and directories on the - filesystem. (Same as for the MS-DOS and HPFS filesystems.) - - 22..77.. nnaammeess=={{77bbiitt,, 88bbiitt,, aallpphhaa,, ccaapp,, llaattiinn,, nneettaattaallkk,, ttrriivviiaall}} - - default value: varies as follows - - +o If the fork option is set to double, then names defaults to alpha. - - +o If the fork option is set to netatalk, then names defaults to - netatalk. - - +o If the fork option is set to cap (or has taken that value by - default), then names defaults to cap. - - This option determines how to convert between valid Macintosh - filenames and valid Linux filenames. The 7bit, 8bit and alpha options - correspond to Apple's recommended conventions named ``7-bit ASCII'', - ``8-bit'' and ``7-bit alphanumeric''. - - 77bbiitt - When converting from Macintosh filenames to Linux filenames the - NULL (0x00), slash (/) and percent (%) characters and the - extended 8-bit characters (hexadecimal codes 0x80-0xff) are - replaced by a percent character (%) followed by the two-digit - hexadecimal code for the character. - - When converting from Linux filenames to Macintosh filenames the - string "%YZ" is replaced by the character with hexadecimal code - 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code - for NULL or colon (:) then the string "%YZ" is unchanged. A - colon (:) is replaced by a pipe character (|). - - 88bbiitt - When converting from Macintosh filenames to Linux filenames the - NULL (0x00), slash (/) and percent (%) characters are replaced - by a percent character (%) followed by the two-digit hexadecimal - code for the character. - - When converting from Linux filenames to Macintosh filenames the - string "%YZ" is replaced by the character with hexadecimal code - 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code - for NULL or colon (:) then the string "%YZ" is unchanged. A - colon (:) is replaced by a pipe character (|). - - aallpphhaa - When converting from Macintosh filenames to Linux filenames only - the alphanumeric characters (a-z, A-Z and 0-9), the underscore - (_) and the last period (.) in the filename are unchanged. The - remaining characters are replaced by a percent character (%) - followed by the two-digit hexadecimal code for the character. - - When converting from Linux filenames to Macintosh filenames the - string "%YZ" is replaced by the character with hexadecimal code - 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code - for NULL or colon (:) then the string "%YZ" is unchanged. A - colon (:) is replaced by a pipe character (|). - - ccaapp - The convention used by the Columbia AppleTalk Package's AUFS. - - When converting from Macintosh filenames to Linux filenames the - characters from space ( ) through tilde (~) (ASCII 32-126) are - unchanged, with the exception of slash (/). The slash (/) and - all characters outside the range 32-126 are replaced by a colon - (:) followed by the two-digit hexadecimal code for the - character. - - When converting from Linux filenames to Macintosh filenames the - string ":YZ" is replaced by the character with hexadecimal code - 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code - for NULL or colon (:) then the colon is replaced by a pipe - character (|). - - llaattiinn - When converting from Macintosh filenames to Linux filenames the - characters from space ( ) through tilde (~) (ASCII 32-126) are - unchanged, with the exception of slash (/) and percent (%). The - extended 8-bit Macintosh characters with equivalents in the - Latin-1 character set are replaced by those equivalents. The - remaining characters are replaced by a percent character (%) - followed by the two-digit hexadecimal code for the character. - - When converting from Linux filenames to Macintosh filenames the - string "%YZ" is replaced by the character with hexadecimal code - 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code - for NULL or colon (:) then the string "%YZ" is unchanged. The - Latin-1 characters with equivalents in the extended 8-bit - Macintosh character set are replaced by those equivalents. A - colon (:) is replaced by a pipe character (|). - - Thanks to Holger Schemel (aeglos@valinor.owl.de) for - contributing this conversion mode. - - nneettaattaallkk - The convention used by the Netatalk afpd. - - When converting from Macintosh filenames to Linux filenames the - characters from space ( ) through tilde (~) (ASCII 32-126) are - unchanged, with the exception of slash (/) and any initial - period (.). The slash (/) and any initial period (.) and all - characters outside the range 32-126 are replaced by a colon (:) - followed by the two-digit hexadecimal code for the character. - - When converting from Linux filenames to Macintosh filenames the - string ":YZ" is replaced by the character with hexadecimal code - 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code - for NULL or colon (:) then the colon is replaced by a pipe - character (|). - - ttrriivviiaall - When converting from Macintosh filenames to Linux filenames a - slash character (/) is replaced by a colon (:). - - When converting from Linux filenames to Macintosh filenames a - colon (:) is replaced by a slash character (/). - - 22..88.. ppaarrtt==nn - - default value: 0 - - Specifies which HFS partition to mount from a Macintosh CDROM or hard - drive. Partitions are numbered from 0 and count only those identified - in the partition table as containing HFS filesystems. This option is - only useful when the Linux platform doesn't fully support Macintosh - partition tables. In particular on MkLinux and Linux-Pmac this option - is useless. - - Note that in versions before 0.8.3 partitions were numbered from 1. - - 22..99.. qquuiieett - - If included in the options, then chown and chmod operations will not - return errors, but will instead fail silently. (Same as for the MS- - DOS and HPFS filesystems.) - - 22..1100.. ttyyppee==cccccccc - - default value: ``????'' - - Specifies the 4-character string specifying the Finder's Type for new - files. - - 22..1111.. uuiidd==nn - - default value: uid of the mounting process - - Specifies the user that owns all files and directories on the - filesystem. (Same as for the MS-DOS and HPFS filesystems.) - - 22..1122.. uummaasskk==nn - - default value: umask of the mounting process - - Specifies (in octal) the umask used for all files and directories. - (Same as for the MS-DOS and HPFS filesystems.) - - 33.. WWrriittiinngg ttoo HHFFSS FFiilleessyysstteemmss - - Each of the values of the fork mount option yields a different - representation of the Macintosh-specific parts of a file within the - structure of the Linux filesystem. There are, therefore, slightly - different steps involved in copying files if you want to preserve the - resource forks and the Finder's metadata. - - It is important to remember not to use normal user-level tools to - modify a filesystem mounted with the afpd mount option. - - Regardless of the value of the fork mount option you can do virtually - everything to the data fork of a file that you can to a file on any - other filesystem. The limitations are essentially the same as those - imposed by the MS-DOS filesystem: - - +o You can't change the uid or gid of files. - - +o You can't set the set-uid, set-gid or sticky permission bits. - - +o You can't clear the execute permission bits. - - Likewise you can do virtually everything to a directory that you can - to a directory on another file system with the following exceptions: - - +o You can't create, delete or rename resource forks of files or the - Finder's metadata. Note, however, that they are created (with - defaults values), deleted and renamed along with the corresponding - data fork or directory. - - +o You can't change permissions on directories. - - +o You can't change the uid or gid of directories. - - +o You can't create multiple links to files. - - +o You can't create symlinks, device files, sockets or FIFOs. - - 33..11.. WWrriittiinngg wwiitthh ffoorrkk==ccaapp - - Unlike the other schemes for representing forked files, the CAP scheme - presents the resource fork as an independent file; the resource fork - of ./foo is ./.resource/foo. Therefore, you can treat it as a normal - file. You can do anything to a resource fork that you can do to a - data fork, except that you cannot enable execute permissions on a - resource fork. Therefore, resource forks are not suitable for holding - Linux executables or shared libraries. - - If you plan to use the resource fork on a Macintosh then you must obey - the format of a valid resource fork. This format is documented in - Chapter 1 of Apple's _I_n_s_i_d_e _M_a_c_i_n_t_o_s_h_: _M_o_r_e _M_a_c_i_n_t_o_s_h _T_o_o_l_b_o_x. The - filesystem knows nothing about this format and so does nothing to - enforce it. - - The current support for reading and writing is sufficient to allow - copying of entire directories with tar, as long as both the source and - destination are mounted with fork=cap. tar may complain about being - unable to change the uid, gid or mode of files. This is normal and is - an unavoidable side effect of the having a single uid, gid and umask - for the entire filesystem. - - It is impossible to create a resource fork or a Finder metadata file. - However, they are created automatically when the data fork is created. - Therefore, if you wish to copy a single file including both forks and - the Finder's metadata then you must create the data fork first. Then - you can copy the resource fork and the Finder's metadata. For - instance to copy the file foo to dir/bar you should do the following: - - 1. cp foo dir/bar - - 2. cp .resource/foo dir/.resource/bar - - 3. cp .finderinfo/foo dir/.finderinfo/bar - - You may get ``Operation not permitted'' errors from cp when it tries - to change the permissions on files. These errors can safely be - ignored. This method will work even if the file dir/bar exists. - - If you wish to move foo to dir/bar and foo and dir are on the same - filesystem then you only need to execute ``mv foo dir/bar'' and the - resource fork and the Finder's metadata will move too. However, if - foo and dir are on different filesystem then this will lose the - resource fork and metadata. Therefore, it is safest to always move - files as follows: - - 1. cp foo dir/bar - - 2. cp .resource/foo dir/.resource/bar - - 3. cp .finderinfo/foo dir/.finderinfo/bar - - 4. rm foo - - You may get ``Operation not permitted'' errors from cp when it tries - to change the permissions on files. These errors can safely be - ignored. This method will work even if the file dir/bar exists. - - Directories have no resource fork but you may wish to create a - directory which has the same location and view on the Finder's screen - as an existing one. This can be done by copying the Finder metadata - file. To give the directory bar the same location, layout, creation - date and modify date as foo you simply execute ``cp .finderinfo/foo - .finderinfo/bar''. - - When copying an entire directory with ``cp -R'' you may also wish to - copy the metadata for the directory: - - 1. cp -R foo bar - - 2. cp .finderinfo/foo .finderinfo/bar - - You may get ``Operation not permitted'' errors from cp when it tries - to change the permissions on files. These errors can safely be - ignored. - - 33..22.. WWrriittiinngg wwiitthh ffoorrkk==ddoouubbllee - - The current support for reading and writing header files is sufficient - to allow copying of entire directories with tar, as long as both the - source and destination are mounted with fork=double. tar may complain - about being unable to change the uid, gid or mode of files. This is - normal and is an unavoidable side effect of the having a single uid, - gid and umask for the entire filesystem. - - It is impossible to create a header file. However, they are created - automatically when the data fork is created. Therefore, if you wish - to copy a single file including both forks and the Finder's metadata - then you must create the data fork first. Then you can copy the - header file. instance to copy the file foo to dir/bar you should do - the following: - - 1. cp foo dir/bar - - 2. cp %foo dir/%bar - - You may get ``Operation not permitted'' errors from cp when it tries - to change the permissions on files. These errors can safely be - ignored. This method will work even if the file dir/bar exists. - - If you wish to move foo to dir/bar and foo and dir are on the same - filesystem then you only need to execute ``mv foo dir/bar'' and the - header file will move too. However, if foo and dir are on different - filesystem then this will lose the header file. Therefore, it is - safest to always move files as follows: - - 1. cp foo dir/bar - - 2. cp %foo dir/%bar - - 3. rm foo - - You may get ``Operation not permitted'' errors from cp when it tries - to change the permissions on files. These errors can safely be - ignored. This method will work even if the file dir/bar exists. - - Directories have no resource fork but you may wish to create a - directory which has the same location and view on the Finder's screen - as an existing one. This can be done by copying the corresponding - header file. To give the directory bar the same location, layout, - creation date and modify date as foo simply execute ``cp %foo %bar''. - - When copying an entire directory with ``cp -R'' you may also wish to - copy the header file for the directory as well: - - 1. cp -R foo bar - - 2. cp %foo %bar - - You may get ``Operation not permitted'' errors from cp when it tries - to change the permissions on files. These errors can safely be - ignored. - - 33..33.. WWrriittiinngg wwiitthh ffoorrkk==nneettaattaallkk - - The current support for reading and writing header files is sufficient - to allow copying of entire directories with tar, as long as both the - source and destination are mounted fork=netatalk. tar may complain - about being unable to change the uid, gid or mode of files. This is - normal and is an unavoidable side effect of the having a single uid, - gid and umask for the entire filesystem. - - It is impossible to create a header file. However, they are created - automatically when the data fork is created. Therefore, if you wish - to copy a single file including both forks and the Finder's metadata - then you must create the data fork first. Then you can copy the - header file. instance to copy the file foo to dir/bar you should do - the following: - - 1. cp foo dir/bar - - 2. cp .AppleDouble/foo dir/.AppleDouble/bar - - You may get ``Operation not permitted'' errors from cp when it tries - to change the permissions on files. These errors can safely be - ignored. This method will work even if the file dir/bar exists. - - If you wish to move foo to dir/bar and foo and dir are on the same - filesystem then you only need to execute ``mv foo dir/bar'' and the - header file will move too. However, if foo and dir are on different - filesystem then this will lose the header file. Therefore, it is - safest to always move files as follows: - - 1. cp foo dir/bar - - 2. cp .AppleDouble/foo dir/.AppleDouble/bar - - 3. rm foo - - You may get ``Operation not permitted'' errors from cp when it tries - to change the permissions on files. These errors can safely be - ignored. This method will work even if the file dir/bar exists. - - Directories have no resource fork but you may wish to create a - directory which has the same location and view on the Finder's screen - as an existing one. This can be done by copying the corresponding - header file. To give the directory bar the same location, layout, - creation date and modify date as foo you simply execute ``cp - foo/.AppleDouble/.Parent bar/.AppleDouble/.Parent''. - - Because the fork=netatalk scheme holds the header file for a directory - within that directory, directories can safely be copied with ``cp -R - foo bar'' with no loss of information. However, you may get - ``Operation not permitted'' errors from cp when it tries to change the - permissions on files. These errors can safely be ignored. - - 44.. AA GGuuiiddee ttoo SSppeecciiaall FFiillee FFoorrmmaattss - - Each of the values of the fork mount option yields different special - files to represent the Macintosh-specific parts of a file within the - structure of the Linux filesystem. You can write to these special - files to change things such as the Creator and Type of a file. - However, to do so safely you must follow certain rules to avoid - corrupting the data. Additionally, there are certain fields in the - special files that you can't change (writes to them will fail - silently). - - 44..11.. CCAAPP ..ffiinnddeerriinnffoo FFiilleess - - The Finder's metadata for the file ./foo in held in the file - ./.finderinfo/foo. The file has a fixed format defined in hfs_fs.h as - follows: - - ______________________________________________________________________ - struct hfs_cap_info { - __u8 fi_fndr[32]; /* Finder's info */ - __u16 fi_attr; /* AFP attributes */ - __u8 fi_magic1; /* Magic number: */ - #define HFS_CAP_MAGIC1 0xFF - __u8 fi_version; /* Version of this structure: */ - #define HFS_CAP_VERSION 0x10 - __u8 fi_magic; /* Another magic number: */ - #define HFS_CAP_MAGIC 0xDA - __u8 fi_bitmap; /* Bitmap of which names are valid: */ - #define HFS_CAP_SHORTNAME 0x01 - #define HFS_CAP_LONGNAME 0x02 - __u8 fi_shortfilename[12+1]; /* "short name" (unused) */ - __u8 fi_macfilename[32+1]; /* Original (Macintosh) name */ - __u8 fi_comln; /* Length of comment (always 0) */ - __u8 fi_comnt[200]; /* Finder comment (unused) */ - /* optional: used by aufs only if compiled with USE_MAC_DATES */ - __u8 fi_datemagic; /* Magic number for dates extension: */ - #define HFS_CAP_DMAGIC 0xDA - __u8 fi_datevalid; /* Bitmap of which dates are valid: */ - #define HFS_CAP_MDATE 0x01 - #define HFS_CAP_CDATE 0x02 - __u8 fi_ctime[4]; /* Creation date (in AFP format) */ - __u8 fi_mtime[4]; /* Modify date (in AFP format) */ - __u8 fi_utime[4]; /* Un*x time of last mtime change */ - }; - ______________________________________________________________________ - - The type __u8 is an unsigned character, and __u16 is an unsigned - 16-bit integer. - - Currently only the fields fi_fndr, fi_attr, fi_ctime and fi_mtime can - be changed. Writes to the other fields are silently ignored. - However, you shouldn't write random bytes to the other fields, since - they may be writable in the future. - - The fi_fndr field is the ``Finder info'' and ``Extended Finder info'' - for a file or directory. These structures are described in various - books on Macintosh programming. The portion of the most interest is - probably the first 8 bytes which, for a file, give the 4-byte Type - followed by the 4-byte Creator. - - The fi_attr field is the AFP attributes of the file or directory. - While you can write any value to this field, only the ``write- - inhibit'' bit is significant. Setting or clearing this bit will clear - or set the write bits in the file's permissions. When you read from - this field anything you may have written is lost. If the file has - write permissions enabled then you will read zero from this field. - With write permission disabled you will read back 0x01 0xA0, which - corresponds to setting the ``write-inhibit'', ``rename-inhibit'' and - ``delete-inhibit'' bits. - - The fi_ctime and fi_mtime are the Macintosh created and modified time - for the file or directory, and are 32-bit signed integers in network - byteorder giving seconds from 00:00 GMT Jan. 1, 2000. - - 44..22.. AApppplleeDDoouubbllee HHeeaaddeerr FFiilleess - - Both the fork=double and fork=netatalk schemes for representing forked - files use AppleDouble header files to contain the resource fork and - the Finder's metadata together in a single file. - - The AppleDouble format specifies a fixed-format header which describes - which fields are contained in the remainder of the file, where they - are located in the file and how long they are. A full description of - the version 1 format used when fork=netatalk is available from ??????. - The version 2 format used when fork=double is documented in ??????. - The discussion that follows assumes you have read and understood these - documents, which may be difficult until I've replaced the ``??????''s - above with something more informative :-). - - Due to the variable structure of an AppleDouble header file you must - not use buffered I/O when reading or writing them; you should only use - the read() and write() system calls. It is also important that you - make some effort to coordinate processes that are reading and writing - the same header file, since a reader will receive the wrong data if - the location of a given entry has changed since it read the descriptor - for the entry. If a process tries to read the descriptor table while - it is changing then it is possible to read totally meaningless data. - - When a header file is opened it is initially presented with a default - header layout. You may write to the header to change the layout, but - when all file descriptors for the file or directory have been closed - the change in format is lost and subsequent opens will yield the - default layout. Changes to supported entries are made directly to the - filesystem and are thus preserved when the file is closed and - reopened. - - The HFS filesystem currently uses a fixed-size table to hold the - descriptors. Therefore you are limited to HFS_HDR_MAX (currently 10) - descriptors. In the unlikely event that you try to write a header - with more descriptors, a warning will be issued by the kernel, and - extra descriptors will be ignored. This should be considered a bug - and will hopefully change sooner rather than later. - - The results of specifying overlapping entries is undefined and should - not be relied upon to remain unchanged from one version of the HFS - filesystem to the next. There is no valid reason to define - overlapping entries, so just don't do it! - - Changes to the magic number and version fields are preserved until all - file descriptors are closed, however the only significance given to - them internally is that the 16 bytes following the version changes - meaning according to the version. For version 1 header files these 16 - bytes contain the string ``Macintosh'' followed by 7 spaces. For any - other value of the version field these 16 bytes are all zeros. In - either case writes to these 16 bytes are silently ignored. - - Since the magic number and version are given no other significance - internally, you are free to do many things that violate the official - formats. For instance you can create an entry for the data fork in a - header file with an AppleDouble magic number or create ``File Info'' - (id=7) entries in version 2 header files and ``File Dates Info'' - (id=8) entries in version 1 header files. However, future versions of - the filesystem may enforce the format more strictly. - - Entry id 1 (``Data Fork'') is read-only. You should use the data file - to modify the data fork. The data fork is, of course, not supported - for directories. - - Entry ids 2, 7, 8, 9 and 10 (``Resource Fork'', ``File Info'', ``File - Dates Info'', ``Finder Info'' and ``Macintosh File Info'') are fully - supported, meaning that their contents may be read and written and - that data written is preserved when the file is closed and reopened. - The resource fork is, of course, not supported for directories. - - Entry id 7 specifies some of the same data given by ids 8 and 10. If - you create a header file with an entry for id 7 and for ids 8 or 10, - then the behavior with respect to their interaction is undefined. A - header that contains an entry for id 7 and for ids 8 or 10 is not - valid as either a version 1 or a version 2 header file, so there is no - reason to do this and future versions may prevent it. - - Entry id 3 (``Real Name'') is read-only, since it will change - automatically when a file is renamed. Writes to the corresponding - entry are silently ignored. - - All other entry ids are ignored. You may create descriptors for them; - in fact the default header layout when fork=netatalk includes a - descriptor for id 4 (``Comment''). However writes to the entries - corresponding to the ignored ids fail silently and reads from the - entries always return zeros. However, you shouldn't write random - bytes to unsupported entries, since they may be supported in the - future. - - All of the supported entry types except the data and resource forks - have a fixed length. If you give them a smaller length in the - descriptor then you are unable to access part of the corresponding - entry. If you give them a larger length in the descriptor, then the - corresponding entry is padded with zeros and writes to the extra space - are silently ignored. - - Writes to the length field of descriptors for the data and resource - forks will cause the corresponding fork to grow (with zero padding) or - shrink to the indicated length. - - If you have an entry for the data fork then the descriptor's length - field does not change automatically to reflect any modification of the - data fork directly (the data does change however). If the data fork - is longer than the descriptor indicates, then a portion of it is - inaccessible. If the data fork is shorter than the descriptor - indicates then reads will be padded with zeros. - - Writes beyond the end of the resource fork that extend into empty - space between entries or beyond the end of the file will extend the - fork, automatically changing the length field of the corresponding - descriptor. Writes to any other space between entries are silently - ignored and read of such spaces always return zeros. - - Calling truncate() on a header file can change the length of the - resource fork and such a change will automatically be reflected in the - length field of the corresponding descriptor. If truncate() shortens - the file so that the entry for the resource fork would extend beyond - the new end of the file then the fork is shortened to fit in the space - that remains, or to zero bytes if the entry is now entirely beyond the - end of the file. If the last entry in a header file is the resource - fork then a call to truncate() that extends the header file will - extend the fork with zeros. Note that this happens even if there was - previously space between the end of the fork and the end of the file. - - 55.. RReeppoorrttiinngg BBuuggss - - If you'd like any problems you encounter fixed, you'll need to provide - a detailed bug report. However, you should check the FAQ (available - from the HFS for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/>) - first to be certain that your problem is not a known limitation of the - filesystem. If your bug doesn't appear in the FAQ then you should e-mail - me at hargrove@sccm.Stanford.EDU. - - 55..11.. WWhhaatt GGooeess iinn aa BBuugg RReeppoorrtt - - When writing your bug report, include any facts you think might be - relevant; I'd much rather have a bunch of extra facts than need to - e-mail you to get the information. At a minimum the following - information should be included: - - +o The version of the HFS filesystem you are using (see - linux/fs/hfs/version.h). - - +o The kernel version you are using. - - +o Any unofficial kernel patches or loadable modules you are using. - - +o If you are loading the HFS filesystem as a module, then version of - the module utilities used to load hfs.o. - - +o The type of media you are working with (floppy, CDROM, ZIP Drive, - etc.). - - +o The steps required to reproduce the bug, including mount options - used. (If you can't reproduce the bug tell me everything you did - the one time it did occur, but be warned that non-reproducible bugs - can only rarely be fixed.) - - 55..22.. HHooww ttoo RReeppoorrtt aa KKeerrnneell OOooppss oorr GGPPFF - - If you encounter a bug that causes a kernel Oops or a General - Protection Fault then you'll need to collect some additional - information for the bug report. If you are loading the HFS filesystem - as a module, then is important that you do this before rebooting, - since the module is unlikely to be loaded at the same address after - the reboot. - - You should include all the information that the kernel prints to the - console or to the system logs. However, the EIP and Stack Trace are - addresses in _y_o_u_r kernel and mean nothing to me without more - information. Using your System.map file (or either ksymoops or klogd) - determine which functions the EIP and Stack Trace are in. If you do - this by hand using your System.map file then the correct symbol is the - one of type t or T with the largest address less than or equal to the - one you are resolving. - - If you are loading the HFS filesystem as a module and the Oops or GPF - was in the HFS code then the EIP and the top levels of the Stack Trace - will be in a loadable module, rather than in the kernel proper. So, - their symbols will not be in the file System.map. Therefore, you will - need to use /proc/ksyms, or a loadmap produced by passing the -m - option to insmod, to locate those symbols. - - 66.. LLeeggaall NNoottiicceess - - 66..11.. TThhiiss DDooccuummeenntt - - This document is Copyright (c) 1996, 1997 by Paul H. Hargrove. - - Permission is granted to make and distribute verbatim copies of this - document provided the copyright notice and this permission notice are - preserved on all copies. - - Permission is granted to copy and distribute modified versions of this - document under the conditions for verbatim copies above, provided a - notice clearly stating that the document is a modified version is also - included in the modified document. - - Permission is granted to copy and distribute translations of this - document into another language, under the conditions specified above - for modified versions. - - Permission is granted to convert this document into another media - under the conditions specified above for modified versions provided - the requirement to acknowledge the source document is fulfilled by - inclusion of an obvious reference to the source document in the new - media. Where there is any doubt as to what defines ``obvious'' the - copyright owner reserves the right to decide. - - 66..22.. TThhee SSooffttwwaarree - - The HFS filesystem for Linux is Copyright (c) 1994-1997 by Paul H. - Hargrove. - - This software 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, or (at your option) - any later version. - - This software 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 software in the file ``COPYING''; if not, write to the - Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - USA. - - 66..22..11.. TThhee CCoolluummbbiiaa AApppplleeTTaallkk PPaacckkaaggee ffoorr UUNNIIXX - - The source code distribution of the Columbia AppleTalk Package for - UNIX, version 6.0, (CAP) was used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location - and format of files used by CAP's Aufs. No code from CAP appears in - the HFS filesystem. The HFS filesystem is not a work ``derived'' from - CAP in the sense of intellectual property law. - - 66..22..22.. NNeettaattaallkk - - The source code distributions of Netatalk, versions 1.3.3b2 and 1.4b2, - were used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location and format of files used - by Netatalk's afpd. No code from Netatalk appears in the HFS - filesystem. The HFS filesystem is not a work ``derived'' from - Netatalk in the sense of intellectual property law. - - 66..33.. TTrraaddeemmaarrkkss - - +o ``Finder'' is a trademarks of Apple Computer, Inc. - - +o ``Apple'', ``AppleShare'', ``AppleTalk'' and ``Macintosh'' are - registered trademarks of Apple Computer, Inc. - - +o ``Microsoft'' and ``MS-DOS'' are registered trademarks of Microsoft - Corporation. - - +o All other trademarks are the property of their respective owners. - diff --git a/fs/hfs/INSTALL.txt b/fs/hfs/INSTALL.txt deleted file mode 100644 index b9c8efed4535..000000000000 --- a/fs/hfs/INSTALL.txt +++ /dev/null @@ -1,124 +0,0 @@ - Installation instructions for the HFS Filesystem for Linux - Paul H. Hargrove, hargrove@sccm.Stanford.EDU - version 0.95 28 Apr 1997 - - This document explains how to compile and install version 0.95 of - hfs_fs, the HFS filesystem for Linux. - - 11.. SSyysstteemm RReeqquuiirreemmeennttss - - You will need the following to compile and use this release of hfs_fs: - - +o Kernel version 2.0.1 or newer compiled with modules enabled - (CONFIG_MODULES). - - +o The kernel sources (or at least the header files) available online. - - +o The module utilities package current for your kernel version and an - understanding of how to use it. - - 22.. IInnssttaallllaattiioonn - - This release of the HFS filesystem is not part of the official kernel - distribution. Therefore, it is compiled as a module and then loaded - into the kernel using the module utilities. Therefore, your kernel - must be compiled with CONFIG_MODULES enabled. - - 22..11.. CCoommppiilliinngg tthhee llooaaddaabbllee mmoodduullee - - To compile hfs.o you should only need to execute ``make'' in the - hfs_fs source directory. - - If gcc complains about not finding a large number of header files with - names beginning with ``linux/'' then you probably don't have the - kernel header files installed correctly. Either /usr/include/linux, - /usr/include/asm and /usr/include/scsi should be symbolic links to - include/linux, include/asm and include/scsi in the kernel source tree - for the kernel you wish to use hfs_fs with, or else they should be - directories containing the header files for the kernel you wish to use - hfs_fs with. - - If gcc complains about not finding linux/version.h, then you will need - to run ``make dep'' in the kernel source directory to build it. Under - MkLinux, run ``make include/linux/version.h'' instead. - - If gcc complains about not finding the files linux/config.h or - linux/autoconf.h, then you will need to run ``make config'' and ``make - dep'' in the kernel source directory to build these two files. - - If you are compiling on a DEC Alpha and receive messages saying - assignment from incompatible pointer type when compiling files dir_*.c - and file_*.c, then you need to change a single line in the file - linux/hfs_fs.h. Remove the text ``&& !defined(__alpha__)'' from the - end of line 217. - - 22..22.. IInnssttaalllliinngg tthhee mmoodduullee iinn tthhee mmoodduulleess ddiirreeccttoorryy ((ooppttiioonnaall)) - - If you plan to use kerneld to automatically load the module or if you - wish to use modprobe or insmod without supplying a complete path to - hfs.o, then you will need to copy hfs.o into a directory where the - module utilities expect to find it. - - The proper directory may depend slightly on your configuration. - However, /lib/modules/default/fs/ is a common one for filesystem - modules. Once hfs.o is in the proper directory you should run depmod - -a to update the dependency list used by kerneld and modprobe. - - 22..33.. LLooaaddiinngg tthhee mmoodduullee iinnttoo tthhee rruunnnniinngg kkeerrnneell - - There are three ways to accomplish this: - - 1. If you are running kerneld and have installed hfs.o in the modules - directory then you don't need to issue any commands; the module - will be loaded when you attempt to mount an HFS filesystem. - - 2. If you are _n_o_t running kerneld then you can load hfs.o manually by - running modprobe hfs.o. If you have not installed hfs.o in one of - the standard module directories, then you will need provide a full - path to the file hfs.o. - - 3. If you have been experiencing kernel crashes with hfs_fs, then you - should file a bug report including the names of the functions which - the EIP and Stack Trace point into. To help with this you can ask - for relocation map for the module when you load it. To do this - load the module with ``insmod -m hfs.o >loadmap''. Again, you may - need a full path to the file hfs.o if you have not placed it in one - of the standard module directories. - - 22..44.. UUssiinngg tthhee mmoodduullee wwiitthh vveerrssiioonneedd ssyymmbboollss - - All the interface between the module and the kernel take place through - very stable (since the mid-1.3.x kernels) parts of the kernel. If you - enabled versioned symbols (CONFIG_MODVERSIONS) when you compiled your - kernel you should often be able to compile this module once and then - use it with many kernels newer than the one you compiled it for. - - In any case, it is unlikely that this module will need changes with - each new kernel patch; simple recompilation should usually suffice. - - 33.. LLeeggaall NNoottiicceess - - 33..11.. TThhiiss DDooccuummeenntt - - This document is Copyright (c) 1996, 1997 by Paul H. Hargrove. - - Permission is granted to make and distribute verbatim copies of this - document provided the copyright notice and this permission notice are - preserved on all copies. - - Permission is granted to copy and distribute modified versions of this - document under the conditions for verbatim copies above, provided a - notice clearly stating that the document is a modified version is also - included in the modified document. - - Permission is granted to copy and distribute translations of this - document into another language, under the conditions specified above - for modified versions. - - Permission is granted to convert this document into another media - under the conditions specified above for modified versions provided - the requirement to acknowledge the source document is fulfilled by - inclusion of an obvious reference to the source document in the new - media. Where there is any doubt as to what defines ``obvious'' the - copyright owner reserves the right to decide. - diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile index c7fbf07d6166..fa13214b1848 100644 --- a/fs/hfs/Makefile +++ b/fs/hfs/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_HFS_FS) += hfs.o -hfs-objs := balloc.o bdelete.o bfind.o bins_del.o binsert.o bitmap.o bitops.o \ - bnode.o brec.o btree.o catalog.o dir.o dir_cap.o dir_dbl.o \ - dir_nat.o extent.o file.o file_cap.o file_hdr.o inode.o mdb.o \ - part_tbl.o string.o super.o sysdep.o trans.o version.o +hfs-objs := bitmap.o bfind.o bnode.o brec.o btree.o \ + catalog.o dir.o extent.o inode.o mdb.o \ + part_tbl.o string.o super.o sysdep.o trans.o + diff --git a/fs/hfs/TODO b/fs/hfs/TODO deleted file mode 100644 index fbbc6e16c379..000000000000 --- a/fs/hfs/TODO +++ /dev/null @@ -1,52 +0,0 @@ -The hfs_fs "to do" list. ------------------------- -Items are broken down into groups and the groups are listed in order -from most important to least important. The items within each group -are not placed in any particular order. The order in which items are -listed probably doesn't correlate well with the order they will be -addressed. - -Genuine bugs: -1. Header files have compiled-in limit (currently 10) on descriptors. - -Missing features: -1. 1k block support is needed for some devices. -2. An ioctl()-based interface is needed to provide a consistent way - to do things under all of the representations of forked files. - -Possible additional "fork" mount options: -1. AppleSingle. -2. The scheme MacOS uses on FAT disks (PC Exchange). -3. "Flat" (no resource forks or metadata). - -Performance issues: -1. Use drAllocPtr to speed block allocations. -2. Keep a real cache of bnodes, rather than just a hash table of - the ones that are currently in use. -3. Keep a real cache of extent records, rather than just a linked - list of the ones that are currently in use and the one most - recently used. This is particularly needed to get acceptable - performance with multiple readers on a file. Perhaps simply - keep them in memory once they've been read until the file is - closed. - -Implementation details: -1. Allocation scheme could/should be closer to that used by Apple. -2. B*-tree insertion could/should be closer to that used by Apple. -3. Magic-number checks on data structures are rarely done. -4. Error recovery is needed for failed binsert(), bdelete() and rename(). -5. Deadlock detection is needed to make insert_empty_bnode() and - bdelete() less likely to hang on a corrupted B-tree. -6. Metadata for covered directories shouldn't appear in the filesystem. - Under CAP and AppleDouble it currently does. However, the obvious - solution is a real performance killer and is not worth implementing. - -Fantasy features: -1. Access Desktop file/database for comment and icon. -2. Implement mmap() for AppleDouble header files and CAP info files. -3. Implement AppleShare client support. - -Suggestions/comments/questions are welcome. -Code addressing any of the issues listed above is especially welcome. -Paul H. Hargrove -hargrove@sccm.Stanford.EDU diff --git a/fs/hfs/balloc.c b/fs/hfs/balloc.c deleted file mode 100644 index 58d281784734..000000000000 --- a/fs/hfs/balloc.c +++ /dev/null @@ -1,439 +0,0 @@ -/* - * linux/fs/hfs/balloc.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * hfs_bnode_alloc() and hfs_bnode_bitop() are based on GPLed code - * Copyright (C) 1995 Michael Dreher - * - * This file contains the code to create and destroy nodes - * in the B-tree structure. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - * - * The code in this file initializes some structures which contain - * pointers by calling memset(&foo, 0, sizeof(foo)). - * This produces the desired behavior only due to the non-ANSI - * assumption that the machine representation of NULL is all zeros. - */ - -#include "hfs_btree.h" - -/*================ File-local functions ================*/ - -/* - * get_new_node() - * - * Get a buffer for a new node with out reading it from disk. - */ -static hfs_buffer get_new_node(struct hfs_btree *tree, hfs_u32 node) -{ - int tmp; - hfs_buffer retval = HFS_BAD_BUFFER; - - tmp = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0); - if (tmp) { - retval = hfs_buffer_get(tree->sys_mdb, tmp, 0); - } - return retval; -} - -/* - * hfs_bnode_init() - * - * Description: - * Initialize a newly allocated bnode. - * Input Variable(s): - * struct hfs_btree *tree: Pointer to a B-tree - * hfs_u32 node: the node number to allocate - * Output Variable(s): - * NONE - * Returns: - * struct hfs_bnode_ref for the new node - * Preconditions: - * 'tree' points to a "valid" (struct hfs_btree) - * 'node' exists and has been allocated in the bitmap of bnodes. - * Postconditions: - * On success: - * The node is not read from disk, nor added to the bnode cache. - * The 'sticky' and locking-related fields are all zero/NULL. - * The bnode's nd{[FB]Link, Type, NHeight} fields are uninitialized. - * The bnode's ndNRecs field and offsets table indicate an empty bnode. - * On failure: - * The node is deallocated. - */ -static struct hfs_bnode_ref hfs_bnode_init(struct hfs_btree * tree, - hfs_u32 node) -{ -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - extern int bnode_count; -#endif - struct hfs_bnode_ref retval; - - retval.lock_type = HFS_LOCK_NONE; - if (!HFS_NEW(retval.bn)) { - hfs_warn("hfs_bnode_init: out of memory.\n"); - goto bail2; - } - - /* Partially initialize the in-core structure */ - memset(retval.bn, 0, sizeof(*retval.bn)); - retval.bn->magic = HFS_BNODE_MAGIC; - retval.bn->tree = tree; - retval.bn->node = node; - hfs_init_waitqueue(&retval.bn->wqueue); - hfs_init_waitqueue(&retval.bn->rqueue); - hfs_bnode_lock(&retval, HFS_LOCK_WRITE); - - retval.bn->buf = get_new_node(tree, node); - if (!hfs_buffer_ok(retval.bn->buf)) { - goto bail1; - } - -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - ++bnode_count; -#endif - - /* Partially initialize the on-disk structure */ - memset(hfs_buffer_data(retval.bn->buf), 0, HFS_SECTOR_SIZE); - hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval.bn, 1)); - - return retval; - -bail1: - HFS_DELETE(retval.bn); -bail2: - /* clear the bit in the bitmap */ - hfs_bnode_bitop(tree, node, 0); - return retval; -} - -/* - * init_mapnode() - * - * Description: - * Initializes a given node as a mapnode in the given tree. - * Input Variable(s): - * struct hfs_bnode *bn: the node to add the mapnode after. - * hfs_u32: the node to use as a mapnode. - * Output Variable(s): - * NONE - * Returns: - * struct hfs_bnode *: the new mapnode or NULL - * Preconditions: - * 'tree' is a valid (struct hfs_btree). - * 'node' is the number of the first node in 'tree' that is not - * represented by a bit in the existing mapnodes. - * Postconditions: - * On failure 'tree' is unchanged and NULL is returned. - * On success the node given by 'node' has been added to the linked - * list of mapnodes attached to 'tree', and has been initialized as - * a valid mapnode with its first bit set to indicate itself as - * allocated. - */ -static struct hfs_bnode *init_mapnode(struct hfs_bnode *bn, hfs_u32 node) -{ -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - extern int bnode_count; -#endif - struct hfs_bnode *retval; - - if (!HFS_NEW(retval)) { - hfs_warn("hfs_bnode_add: out of memory.\n"); - return NULL; - } - - memset(retval, 0, sizeof(*retval)); - retval->magic = HFS_BNODE_MAGIC; - retval->tree = bn->tree; - retval->node = node; - retval->sticky = HFS_STICKY; - retval->buf = get_new_node(bn->tree, node); - if (!hfs_buffer_ok(retval->buf)) { - HFS_DELETE(retval); - return NULL; - } - -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - ++bnode_count; -#endif - - /* Initialize the bnode data structure */ - memset(hfs_buffer_data(retval->buf), 0, HFS_SECTOR_SIZE); - retval->ndFLink = 0; - retval->ndBLink = bn->node; - retval->ndType = ndMapNode; - retval->ndNHeight = 0; - retval->ndNRecs = 1; - hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval, 1)); - hfs_put_hs(0x1fa, RECTBL(retval, 2)); - *((hfs_u8 *)bnode_key(retval, 1)) = 0x80; /* set first bit of bitmap */ - retval->prev = bn; - hfs_bnode_commit(retval); - - bn->ndFLink = node; - bn->next = retval; - hfs_bnode_commit(bn); - - return retval; -} - -/*================ Global functions ================*/ - -/* - * hfs_bnode_bitop() - * - * Description: - * Allocate/free the requested node of a B-tree of the hfs filesystem - * by setting/clearing the corresponding bit in the B-tree bitmap. - * The size of the B-tree will not be changed. - * Input Variable(s): - * struct hfs_btree *tree: Pointer to a B-tree - * hfs_u32 bitnr: The node number to free - * int set: 0 to clear the bit, non-zero to set it. - * Output Variable(s): - * None - * Returns: - * 0: no error - * -1: The node was already allocated/free, nothing has been done. - * -2: The node is out of range of the B-tree. - * -4: not enough map nodes to hold all the bits - * Preconditions: - * 'tree' points to a "valid" (struct hfs_btree) - * 'bitnr' is a node number within the range of the btree, which is - * currently free/allocated. - * Postconditions: - * The bit number 'bitnr' of the node bitmap is set/cleared and the - * number of free nodes in the btree is decremented/incremented by one. - */ -int hfs_bnode_bitop(struct hfs_btree *tree, hfs_u32 bitnr, int set) -{ - struct hfs_bnode *bn; /* the current bnode */ - hfs_u16 start; /* the start (in bits) of the bitmap in node */ - hfs_u16 len; /* the len (in bits) of the bitmap in node */ - hfs_u32 *u32; /* address of the u32 containing the bit */ - - if (bitnr >= tree->bthNNodes) { - hfs_warn("hfs_bnode_bitop: node number out of range.\n"); - return -2; - } - - bn = &tree->head; - for (;;) { - start = bnode_offset(bn, bn->ndNRecs) << 3; - len = (bnode_offset(bn, bn->ndNRecs + 1) << 3) - start; - - if (bitnr < len) { - break; - } - - /* continue on to next map node if available */ - if (!(bn = bn->next)) { - hfs_warn("hfs_bnode_bitop: too few map nodes.\n"); - return -4; - } - bitnr -= len; - } - - /* Change the correct bit */ - bitnr += start; - u32 = (hfs_u32 *)hfs_buffer_data(bn->buf) + (bitnr >> 5); - bitnr %= 32; - if ((set && hfs_set_bit(bitnr, u32)) || - (!set && !hfs_clear_bit(bitnr, u32))) { - hfs_warn("hfs_bnode_bitop: bitmap corruption.\n"); - return -1; - } - hfs_buffer_dirty(bn->buf); - - /* adjust the free count */ - tree->bthFree += (set ? -1 : 1); - tree->dirt = 1; - - return 0; -} - -/* - * hfs_bnode_alloc() - * - * Description: - * Find a cleared bit in the B-tree node bitmap of the hfs filesystem, - * set it and return the corresponding bnode, with its contents zeroed. - * When there is no free bnode in the tree, an error is returned, no - * new nodes will be added by this function! - * Input Variable(s): - * struct hfs_btree *tree: Pointer to a B-tree - * Output Variable(s): - * NONE - * Returns: - * struct hfs_bnode_ref for the new bnode - * Preconditions: - * 'tree' points to a "valid" (struct hfs_btree) - * There is at least one free bnode. - * Postconditions: - * On success: - * The corresponding bit in the btree bitmap is set. - * The number of free nodes in the btree is decremented by one. - * The node is not read from disk, nor added to the bnode cache. - * The 'sticky' field is uninitialized. - */ -struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *tree) -{ - struct hfs_bnode *bn; /* the current bnode */ - hfs_u32 bitnr = 0; /* which bit are we examining */ - hfs_u16 first; /* the first clear bit in this bnode */ - hfs_u16 start; /* the start (in bits) of the bitmap in node */ - hfs_u16 end; /* the end (in bits) of the bitmap in node */ - hfs_u32 *data; /* address of the data in this bnode */ - - bn = &tree->head; - for (;;) { - start = bnode_offset(bn, bn->ndNRecs) << 3; - end = bnode_offset(bn, bn->ndNRecs + 1) << 3; - data = (hfs_u32 *)hfs_buffer_data(bn->buf); - - /* search the current node */ - first = hfs_find_zero_bit(data, end, start); - if (first < end) { - break; - } - - /* continue search in next map node */ - bn = bn->next; - - if (!bn) { - hfs_warn("hfs_bnode_alloc: too few map nodes.\n"); - goto bail; - } - bitnr += (end - start); - } - - if ((bitnr += (first - start)) >= tree->bthNNodes) { - hfs_warn("hfs_bnode_alloc: no free nodes found, " - "count wrong?\n"); - goto bail; - } - - if (hfs_set_bit(first % 32, data + (first>>5))) { - hfs_warn("hfs_bnode_alloc: bitmap corruption.\n"); - goto bail; - } - hfs_buffer_dirty(bn->buf); - - /* decrement the free count */ - --tree->bthFree; - tree->dirt = 1; - - return hfs_bnode_init(tree, bitnr); - -bail: - return (struct hfs_bnode_ref){NULL, HFS_LOCK_NONE}; -} - -/* - * hfs_btree_extend() - * - * Description: - * Adds nodes to a B*-tree if possible. - * Input Variable(s): - * struct hfs_btree *tree: the btree to add nodes to. - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'tree' is a valid (struct hfs_btree *). - * Postconditions: - * If possible the number of nodes indicated by the tree's clumpsize - * have been added to the tree, updating all in-core and on-disk - * allocation information. - * If insufficient disk-space was available then fewer nodes may have - * been added than would be expected based on the clumpsize. - * In the case of the extents B*-tree this function will add fewer - * nodes than expected if adding more would result in an extent - * record for the extents tree being added to the extents tree. - * The situation could be dealt with, but doing so confuses Macs. - */ -void hfs_btree_extend(struct hfs_btree *tree) -{ - struct hfs_bnode_ref head; - struct hfs_bnode *bn, *tmp; - struct hfs_cat_entry *entry = &tree->entry; - struct hfs_mdb *mdb = entry->mdb; - hfs_u32 old_nodes, new_nodes, total_nodes, new_mapnodes, seen; - - old_nodes = entry->u.file.data_fork.psize; - - entry->u.file.data_fork.lsize += 1; /* rounded up to clumpsize */ - hfs_extent_adj(&entry->u.file.data_fork); - - total_nodes = entry->u.file.data_fork.psize; - entry->u.file.data_fork.lsize = total_nodes << HFS_SECTOR_SIZE_BITS; - new_nodes = total_nodes - old_nodes; - if (!new_nodes) { - return; - } - - head = hfs_bnode_find(tree, 0, HFS_LOCK_WRITE); - if (!(bn = head.bn)) { - hfs_warn("hfs_btree_extend: header node not found.\n"); - return; - } - - seen = 0; - new_mapnodes = 0; - for (;;) { - seen += bnode_rsize(bn, bn->ndNRecs) << 3; - - if (seen >= total_nodes) { - break; - } - - if (!bn->next) { - tmp = init_mapnode(bn, seen); - if (!tmp) { - hfs_warn("hfs_btree_extend: " - "can't build mapnode.\n"); - hfs_bnode_relse(&head); - return; - } - ++new_mapnodes; - } - bn = bn->next; - } - hfs_bnode_relse(&head); - - tree->bthNNodes = total_nodes; - tree->bthFree += (new_nodes - new_mapnodes); - tree->dirt = 1; - - /* write the backup MDB, not returning until it is written */ - hfs_mdb_commit(mdb, 1); - - return; -} - -/* - * hfs_bnode_free() - * - * Remove a node from the cache and mark it free in the bitmap. - */ -int hfs_bnode_free(struct hfs_bnode_ref *bnr) -{ - hfs_u32 node = bnr->bn->node; - struct hfs_btree *tree = bnr->bn->tree; - - if (bnr->bn->count != 1) { - hfs_warn("hfs_bnode_free: count != 1.\n"); - return -EIO; - } - - hfs_bnode_relse(bnr); - hfs_bnode_bitop(tree, node, 0); - return 0; -} diff --git a/fs/hfs/bdelete.c b/fs/hfs/bdelete.c deleted file mode 100644 index aec139476d6f..000000000000 --- a/fs/hfs/bdelete.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * linux/fs/hfs/bdelete.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the code to delete records in a B-tree. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#include "hfs_btree.h" - -/*================ Variable-like macros ================*/ - -#define FULL (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor)) -#define NO_SPACE (HFS_SECTOR_SIZE+1) - -/*================ File-local functions ================*/ - -/* - * bdelete_nonempty() - * - * Description: - * Deletes a record from a given bnode without regard to it becoming empty. - * Input Variable(s): - * struct hfs_brec* brec: pointer to the brec for the deletion - * struct hfs_belem* belem: which node in 'brec' to delete from - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'brec' points to a valid (struct hfs_brec). - * 'belem' points to a valid (struct hfs_belem) in 'brec'. - * Postconditions: - * The record has been inserted in the position indicated by 'brec'. - */ -static void bdelete_nonempty(struct hfs_brec *brec, struct hfs_belem *belem) -{ - int i, rec, nrecs, tomove; - hfs_u16 size; - hfs_u8 *start; - struct hfs_bnode *bnode = belem->bnr.bn; - - rec = belem->record; - nrecs = bnode->ndNRecs; - size = bnode_rsize(bnode, rec); - tomove = bnode_offset(bnode, nrecs+1) - bnode_offset(bnode, rec+1); - - /* adjust the record table */ - for (i = rec+1; i <= nrecs; ++i) { - hfs_put_hs(bnode_offset(bnode,i+1) - size, RECTBL(bnode,i)); - } - - /* move it down */ - start = bnode_key(bnode, rec); - memmove(start, start + size, tomove); - - /* update record count */ - --bnode->ndNRecs; -} - -/* - * del_root() - * - * Description: - * Delete the current root bnode. - * Input Variable(s): - * struct hfs_bnode_ref *root: reference to the root bnode - * Output Variable(s): - * NONE - * Returns: - * int: 0 on success, error code on failure - * Preconditions: - * 'root' refers to the root bnode with HFS_LOCK_WRITE access. - * None of 'root's children are held with HFS_LOCK_WRITE access. - * Postconditions: - * The current 'root' node is removed from the tree and the depth - * of the tree is reduced by one. - * If 'root' is an index node with exactly one child, then that - * child becomes the new root of the tree. - * If 'root' is an empty leaf node the tree becomes empty. - * Upon return access to 'root' is relinquished. - */ -static int del_root(struct hfs_bnode_ref *root) -{ - struct hfs_btree *tree = root->bn->tree; - struct hfs_bnode_ref child; - hfs_u32 node; - - if (root->bn->ndNRecs > 1) { - return 0; - } else if (root->bn->ndNRecs == 0) { - /* tree is empty */ - tree->bthRoot = 0; - tree->root = NULL; - tree->bthRoot = 0; - tree->bthFNode = 0; - tree->bthLNode = 0; - --tree->bthDepth; - tree->dirt = 1; - if (tree->bthDepth) { - hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n", - tree->bthDepth); - goto bail; - } - return hfs_bnode_free(root); - } else if (root->bn->ndType == ndIndxNode) { - /* tree is non-empty */ - node = hfs_get_hl(bkey_record(bnode_datastart(root->bn))); - - child = hfs_bnode_find(tree, node, HFS_LOCK_READ); - if (!child.bn) { - hfs_warn("hfs_bdelete: can't read child node.\n"); - goto bail; - } - - child.bn->sticky = HFS_STICKY; - if (child.bn->next) { - child.bn->next->prev = child.bn->prev; - } - if (child.bn->prev) { - child.bn->prev->next = child.bn->next; - } - if (bhash(tree, child.bn->node) == child.bn) { - bhash(tree, child.bn->node) = child.bn->next; - } - child.bn->next = NULL; - child.bn->prev = NULL; - - tree->bthRoot = child.bn->node; - tree->root = child.bn; - - /* re-assign bthFNode and bthLNode if the new root is - a leaf node. */ - if (child.bn->ndType == ndLeafNode) { - tree->bthFNode = node; - tree->bthLNode = node; - } - hfs_bnode_relse(&child); - - tree->bthRoot = node; - --tree->bthDepth; - tree->dirt = 1; - if (!tree->bthDepth) { - hfs_warn("hfs_bdelete: non-empty tree with " - "bthDepth == 0\n"); - goto bail; - } - return hfs_bnode_free(root); /* marks tree dirty */ - } - hfs_bnode_relse(root); - return 0; - -bail: - hfs_bnode_relse(root); - return -EIO; -} - - -/* - * delete_empty_bnode() - * - * Description: - * Removes an empty non-root bnode from between 'left' and 'right' - * Input Variable(s): - * hfs_u32 left_node: node number of 'left' or zero if 'left' is invalid - * struct hfs_bnode_ref *left: reference to the left neighbor of the - * bnode to remove, or invalid if no such neighbor exists. - * struct hfs_bnode_ref *center: reference to the bnode to remove - * hfs_u32 right_node: node number of 'right' or zero if 'right' is invalid - * struct hfs_bnode_ref *right: reference to the right neighbor of the - * bnode to remove, or invalid if no such neighbor exists. - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'left_node' is as described above. - * 'left' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE - * access and referring to the left neighbor of 'center' if such a - * neighbor exists, or invalid if no such neighbor exists. - * 'center' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE - * access and referring to the bnode to delete. - * 'right_node' is as described above. - * 'right' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE - * access and referring to the right neighbor of 'center' if such a - * neighbor exists, or invalid if no such neighbor exists. - * Postconditions: - * If 'left' is valid its 'ndFLink' field becomes 'right_node'. - * If 'right' is valid its 'ndBLink' field becomes 'left_node'. - * If 'center' was the first leaf node then the tree's 'bthFNode' - * field becomes 'right_node' - * If 'center' was the last leaf node then the tree's 'bthLNode' - * field becomes 'left_node' - * 'center' is NOT freed and access to the nodes is NOT relinquished. - */ -static void delete_empty_bnode(hfs_u32 left_node, struct hfs_bnode_ref *left, - struct hfs_bnode_ref *center, - hfs_u32 right_node, struct hfs_bnode_ref *right) -{ - struct hfs_bnode *bnode = center->bn; - - if (left_node) { - left->bn->ndFLink = right_node; - } else if (bnode->ndType == ndLeafNode) { - bnode->tree->bthFNode = right_node; - bnode->tree->dirt = 1; - } - - if (right_node) { - right->bn->ndBLink = left_node; - } else if (bnode->ndType == ndLeafNode) { - bnode->tree->bthLNode = left_node; - bnode->tree->dirt = 1; - } -} - -/* - * balance() - * - * Description: - * Attempt to equalize space usage in neighboring bnodes. - * Input Variable(s): - * struct hfs_bnode *left: the left bnode. - * struct hfs_bnode *right: the right bnode. - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'left' and 'right' point to valid (struct hfs_bnode)s obtained - * with HFS_LOCK_WRITE access, and are neighbors. - * Postconditions: - * Records are shifted either left or right to make the space usage - * nearly equal. When exact equality is not possible the break - * point is chosen to reduce data movement. - * The key corresponding to 'right' in its parent is NOT updated. - */ -static void balance(struct hfs_bnode *left, struct hfs_bnode *right) -{ - int index, left_free, right_free, half; - - left_free = bnode_freespace(left); - right_free = bnode_freespace(right); - half = (left_free + right_free)/2; - - if (left_free < right_free) { - /* shift right to balance */ - index = left->ndNRecs + 1; - while (right_free >= half) { - --index; - right_free -= bnode_rsize(left,index)+sizeof(hfs_u16); - } - if (index < left->ndNRecs) { -#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE) - hfs_warn("shifting %d of %d recs right to balance: ", - left->ndNRecs - index, left->ndNRecs); -#endif - hfs_bnode_shift_right(left, right, index+1); -#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE) - hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs); -#endif - } - } else { - /* shift left to balance */ - index = 0; - while (left_free >= half) { - ++index; - left_free -= bnode_rsize(right,index)+sizeof(hfs_u16); - } - if (index > 1) { -#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE) - hfs_warn("shifting %d of %d recs left to balance: ", - index-1, right->ndNRecs); -#endif - hfs_bnode_shift_left(left, right, index-1); -#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE) - hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs); -#endif - } - } -} - -/* - * bdelete() - * - * Delete the given record from a B-tree. - */ -static int bdelete(struct hfs_brec *brec) -{ - struct hfs_btree *tree = brec->tree; - struct hfs_belem *belem = brec->bottom; - struct hfs_belem *parent = (belem-1); - struct hfs_bnode *bnode; - hfs_u32 left_node, right_node; - struct hfs_bnode_ref left, right; - int left_space, right_space, min_space; - int fix_right_key; - int fix_key; - - while ((belem > brec->top) && - (belem->flags & (HFS_BPATH_UNDERFLOW | HFS_BPATH_FIRST))) { - bnode = belem->bnr.bn; - fix_key = belem->flags & HFS_BPATH_FIRST; - fix_right_key = 0; - - bdelete_nonempty(brec, belem); - - if (bnode->node == tree->root->node) { - del_root(&belem->bnr); - --brec->bottom; - goto done; - } - - /* check for btree corruption which could lead to deadlock */ - left_node = bnode->ndBLink; - right_node = bnode->ndFLink; - if ((left_node && hfs_bnode_in_brec(left_node, brec)) || - (right_node && hfs_bnode_in_brec(right_node, brec)) || - (left_node == right_node)) { - hfs_warn("hfs_bdelete: corrupt btree\n"); - hfs_brec_relse(brec, NULL); - return -EIO; - } - - /* grab the left neighbor if it exists */ - if (left_node) { - hfs_bnode_lock(&belem->bnr, HFS_LOCK_RESRV); - left = hfs_bnode_find(tree,left_node,HFS_LOCK_WRITE); - if (!left.bn) { - hfs_warn("hfs_bdelete: unable to read left " - "neighbor.\n"); - hfs_brec_relse(brec, NULL); - return -EIO; - } - hfs_bnode_lock(&belem->bnr, HFS_LOCK_WRITE); - if (parent->record != 1) { - left_space = bnode_freespace(left.bn); - } else { - left_space = NO_SPACE; - } - } else { - left.bn = NULL; - left_space = NO_SPACE; - } - - /* grab the right neighbor if it exists */ - if (right_node) { - right = hfs_bnode_find(tree,right_node,HFS_LOCK_WRITE); - if (!right.bn) { - hfs_warn("hfs_bdelete: unable to read right " - "neighbor.\n"); - hfs_bnode_relse(&left); - hfs_brec_relse(brec, NULL); - return -EIO; - } - if (parent->record < parent->bnr.bn->ndNRecs) { - right_space = bnode_freespace(right.bn); - } else { - right_space = NO_SPACE; - } - } else { - right.bn = NULL; - right_space = NO_SPACE; - } - - if (left_space < right_space) { - min_space = left_space; - } else { - min_space = right_space; - } - - if (min_space == NO_SPACE) { - hfs_warn("hfs_bdelete: no siblings?\n"); - hfs_brec_relse(brec, NULL); - return -EIO; - } - - if (bnode->ndNRecs == 0) { - delete_empty_bnode(left_node, &left, &belem->bnr, - right_node, &right); - } else if (min_space + bnode_freespace(bnode) >= FULL) { - if ((right_space == NO_SPACE) || - ((right_space == min_space) && - (left_space != NO_SPACE))) { - hfs_bnode_shift_left(left.bn, bnode, - bnode->ndNRecs); - } else { - hfs_bnode_shift_right(bnode, right.bn, 1); - fix_right_key = 1; - } - delete_empty_bnode(left_node, &left, &belem->bnr, - right_node, &right); - } else if (min_space == right_space) { - balance(bnode, right.bn); - fix_right_key = 1; - } else { - balance(left.bn, bnode); - fix_key = 1; - } - - if (fix_right_key) { - hfs_bnode_update_key(brec, belem, right.bn, 1); - } - - hfs_bnode_relse(&left); - hfs_bnode_relse(&right); - - if (bnode->ndNRecs) { - if (fix_key) { - hfs_bnode_update_key(brec, belem, bnode, 0); - } - goto done; - } - - hfs_bnode_free(&belem->bnr); - --brec->bottom; - belem = parent; - --parent; - } - - if (belem < brec->top) { - hfs_warn("hfs_bdelete: Missing parent.\n"); - hfs_brec_relse(brec, NULL); - return -EIO; - } - - bdelete_nonempty(brec, belem); - -done: - hfs_brec_relse(brec, NULL); - return 0; -} - -/*================ Global functions ================*/ - -/* - * hfs_bdelete() - * - * Delete the requested record from a B-tree. - */ -int hfs_bdelete(struct hfs_btree *tree, const struct hfs_bkey *key) -{ - struct hfs_belem *belem; - struct hfs_bnode *bnode; - struct hfs_brec brec; - int retval; - - if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key) { - hfs_warn("hfs_bdelete: invalid arguments.\n"); - return -EINVAL; - } - - retval = hfs_bfind(&brec, tree, key, HFS_BFIND_DELETE); - if (!retval) { - belem = brec.bottom; - bnode = belem->bnr.bn; - - belem->flags = 0; - if ((bnode->ndNRecs * sizeof(hfs_u16) + bnode_end(bnode) - - bnode_rsize(bnode, belem->record)) < FULL/2) { - belem->flags |= HFS_BPATH_UNDERFLOW; - } - if (belem->record == 1) { - belem->flags |= HFS_BPATH_FIRST; - } - - if (!belem->flags) { - hfs_brec_lock(&brec, brec.bottom); - } else { - hfs_brec_lock(&brec, NULL); - } - - retval = bdelete(&brec); - if (!retval) { - --brec.tree->bthNRecs; - brec.tree->dirt = 1; - } - hfs_brec_relse(&brec, NULL); - } - return retval; -} diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c index 8e84133d20bd..af78ad64fa3a 100644 --- a/fs/hfs/bfind.c +++ b/fs/hfs/bfind.c @@ -1,322 +1,209 @@ /* - * linux/fs/hfs/bfind.c + * linux/fs/hfs/bfind.c * - * Copyright (C) 1995, 1996 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> * - * This file contains the code to access records in a btree. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. + * Search routines for btrees */ -#include "hfs_btree.h" +#include <linux/slab.h> +#include "btree.h" -/*================ Global functions ================*/ - -/* - * hfs_brec_relse() - * - * Description: - * This function releases some of the nodes associated with a brec. - * Input Variable(s): - * struct hfs_brec *brec: pointer to the brec to release some nodes from. - * struct hfs_belem *elem: the last node to release or NULL for all - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'brec' points to a "valid" (struct hfs_brec) - * Postconditions: - * All nodes between the indicated node and the beginning of the path - * are released. - */ -void hfs_brec_relse(struct hfs_brec *brec, struct hfs_belem *elem) +int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) { - if (!elem) { - elem = brec->bottom; - } - - while (brec->top <= elem) { - hfs_bnode_relse(&brec->top->bnr); - ++brec->top; - } + void *ptr; + + fd->tree = tree; + fd->bnode = NULL; + ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + fd->search_key = ptr; + fd->key = ptr + tree->max_key_len + 2; + dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", tree->cnid, __builtin_return_address(0)); + down(&tree->tree_lock); + return 0; } -/* - * hfs_bfind() - * - * Description: - * This function has sole responsibility for locating existing - * records in a B-tree. Given a B-tree and a key it locates the - * "greatest" record "less than or equal to" the given key. The - * exact behavior is determined by the bits of the flags variable as - * follows: - * ('flags' & HFS_LOCK_MASK): - * The lock_type argument to be used when calling hfs_bnode_find(). - * HFS_BFIND_EXACT: only accept an exact match, otherwise take the - * "largest" record less than 'target' as a "match" - * HFS_BFIND_LOCK: request HFS_LOCK_WRITE access to the node containing - * the "matching" record when it is located - * HFS_BPATH_FIRST: keep access to internal nodes when accessing their - * first child. - * HFS_BPATH_OVERFLOW: keep access to internal nodes when the accessed - * child is too full to insert another pointer record. - * HFS_BPATH_UNDERFLOW: keep access to internal nodes when the accessed - * child is would be less than half full upon removing a pointer record. - * Input Variable(s): - * struct hfs_brec *brec: pointer to the (struct hfs_brec) to hold - * the search results. - * struct hfs_bkey *target: pointer to the (struct hfs_bkey) - * to search for - * int flags: bitwise OR of flags which determine the function's behavior - * Output Variable(s): - * 'brec' contains the results of the search on success or is invalid - * on failure. - * Returns: - * int: 0 or 1 on success or an error code on failure: - * -EINVAL: one of the input variables was NULL. - * -ENOENT: tree is valid but empty or no "matching" record was located. - * If the HFS_BFIND_EXACT bit of 'flags' is not set then the case of no - * matching record will give a 'brec' with a 'record' field of zero - * rather than returning this error. - * -EIO: an I/O operation or an assertion about the structure of a - * valid B-tree failed indicating corruption of either the B-tree - * structure on the disk or one of the in-core structures representing - * the B-tree. - * (This could also be returned if a kmalloc() call failed in a - * subordinate routine that is intended to get the data from the - * disk or the buffer cache.) - * Preconditions: - * 'brec' is NULL or points to a (struct hfs_brec) with a 'tree' field - * which points to a valid (struct hfs_btree). - * 'target' is NULL or points to a "valid" (struct hfs_bkey) - * Postconditions: - * If 'brec', 'brec->tree' or 'target' is NULL then -EINVAL is returned. - * If 'brec', 'brec->tree' and 'target' are non-NULL but the tree - * is empty then -ENOENT is returned. - * If 'brec', 'brec->tree' and 'target' are non-NULL but the call to - * hfs_brec_init() fails then '*brec' is NULL and -EIO is returned. - * If 'brec', 'brec->tree' and 'target' are non-NULL and the tree is - * non-empty then the tree is searched as follows: - * If any call to hfs_brec_next() fails or returns a node that is - * neither an index node nor a leaf node then -EIO is returned to - * indicate that the B-tree or buffer-cache are corrupted. - * If every record in the tree is "greater than" the given key - * and the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned. - * If every record in the tree is "greater than" the given key - * and the HFS_BFIND_EXACT bit of 'flags' is clear then 'brec' refers - * to the first leaf node in the tree and has a 'record' field of - * zero, and 1 is returned. - * If a "matching" record is located with key "equal to" 'target' - * then the return value is 0 and 'brec' indicates the record. - * If a "matching" record is located with key "greater than" 'target' - * then the behavior is determined as follows: - * If the HFS_BFIND_EXACT bit of 'flags' is not set then 1 is returned - * and 'brec' refers to the "matching" record. - * If the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned. - * If the return value is non-negative and the HFS_BFIND_LOCK bit of - * 'flags' is set then hfs_brec_lock() is called on the bottom element - * of 'brec' before returning. - */ -int hfs_bfind(struct hfs_brec *brec, struct hfs_btree *tree, - const struct hfs_bkey *target, int flags) +void hfs_find_exit(struct hfs_find_data *fd) { - struct hfs_belem *curr; - struct hfs_bkey *key; - struct hfs_bnode *bn; - int result, ntype; - - /* check for invalid arguments */ - if (!brec || (tree->magic != HFS_BTREE_MAGIC) || !target) { - return -EINVAL; - } - - /* check for empty tree */ - if (!tree->root || !tree->bthNRecs) { - return -ENOENT; - } - - /* start search at root of tree */ - if (!(curr = hfs_brec_init(brec, tree, flags))) { - return -EIO; - } + hfs_bnode_put(fd->bnode); + kfree(fd->search_key); + dprint(DBG_BNODE_REFS, "find_exit: %d (%p)\n", fd->tree->cnid, __builtin_return_address(0)); + up(&fd->tree->tree_lock); + fd->tree = NULL; +} - /* traverse the tree */ +/* Find the record in bnode that best matches key (not greater than...)*/ +int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd) +{ + int cmpval; + u16 off, len, keylen; + int rec; + int b, e; + int res; + + b = 0; + e = bnode->num_recs - 1; + res = -ENOENT; do { - bn = curr->bnr.bn; - - if (!curr->record) { - hfs_warn("hfs_bfind: empty bnode\n"); - hfs_brec_relse(brec, NULL); - return -EIO; - } - - /* reverse linear search yielding largest key "less - than or equal to" 'target'. - It is questionable whether a binary search would be - significantly faster */ - do { - key = belem_key(curr); - if (!key->KeyLen) { - hfs_warn("hfs_bfind: empty key\n"); - hfs_brec_relse(brec, NULL); - return -EIO; - } - result = (tree->compare)(target, key); - } while ((result<0) && (--curr->record)); - - ntype = bn->ndType; - - /* see if all keys > target */ - if (!curr->record) { - if (bn->ndBLink) { - /* at a node other than the left-most at a - given level it means the parent had an - incorrect key for this child */ - hfs_brec_relse(brec, NULL); - hfs_warn("hfs_bfind: corrupted b-tree %d.\n", - (int)ntohl(tree->entry.cnid)); - return -EIO; - } - if (flags & HFS_BFIND_EXACT) { - /* we're not going to find it */ - hfs_brec_relse(brec, NULL); - return -ENOENT; - } - if (ntype == ndIndxNode) { - /* since we are at the left-most node at - the current level and looking for the - predecessor of 'target' keep going down */ - curr->record = 1; - } else { - /* we're at first leaf so fall through */ - } - } - - /* get next node if necessary */ - if ((ntype == ndIndxNode) && !(curr = hfs_brec_next(brec))) { - return -EIO; + rec = (e + b) / 2; + len = hfs_brec_lenoff(bnode, rec, &off); + keylen = hfs_brec_keylen(bnode, rec); + hfs_bnode_read(bnode, fd->key, off, keylen); + cmpval = bnode->tree->keycmp(fd->key, fd->search_key); + if (!cmpval) { + e = rec; + res = 0; + goto done; } - } while (ntype == ndIndxNode); - - if (key->KeyLen > tree->bthKeyLen) { - hfs_warn("hfs_bfind: oversized key\n"); - hfs_brec_relse(brec, NULL); - return -EIO; - } - - if (ntype != ndLeafNode) { - hfs_warn("hfs_bfind: invalid node type %02x in node %d of " - "btree %d\n", bn->ndType, bn->node, - (int)ntohl(tree->entry.cnid)); - hfs_brec_relse(brec, NULL); - return -EIO; + if (cmpval < 0) + b = rec + 1; + else + e = rec - 1; + } while (b <= e); + //printk("%d: %d,%d,%d\n", bnode->this, b, e, rec); + if (rec != e && e >= 0) { + len = hfs_brec_lenoff(bnode, e, &off); + keylen = hfs_brec_keylen(bnode, e); + hfs_bnode_read(bnode, fd->key, off, keylen); } +done: + fd->record = e; + fd->keyoffset = off; + fd->keylength = keylen; + fd->entryoffset = off + keylen; + fd->entrylength = len - keylen; + return res; +} - if ((flags & HFS_BFIND_EXACT) && result) { - hfs_brec_relse(brec, NULL); +/* Traverse a B*Tree from the root to a leaf finding best fit to key */ +/* Return allocated copy of node found, set recnum to best record */ +int hfs_brec_find(struct hfs_find_data *fd) +{ + struct hfs_btree *tree; + struct hfs_bnode *bnode; + u32 data, nidx, parent; + int height, res; + + tree = fd->tree; + if (fd->bnode) + hfs_bnode_put(fd->bnode); + fd->bnode = NULL; + nidx = tree->root; + if (!nidx) return -ENOENT; - } - - if (!(flags & HFS_BPATH_MASK)) { - hfs_brec_relse(brec, brec->bottom-1); - } + height = tree->depth; + res = 0; + parent = 0; + for (;;) { + bnode = hfs_bnode_find(tree, nidx); + if (IS_ERR(bnode)) { + res = PTR_ERR(bnode); + bnode = NULL; + break; + } + if (bnode->height != height) + goto invalid; + if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF)) + goto invalid; + bnode->parent = parent; + + res = __hfs_brec_find(bnode, fd); + if (!height) + break; + if (fd->record < 0) + goto release; - if (flags & HFS_BFIND_LOCK) { - hfs_brec_lock(brec, brec->bottom); + parent = nidx; + hfs_bnode_read(bnode, &data, fd->entryoffset, 4); + nidx = be32_to_cpu(data); + hfs_bnode_put(bnode); } - - brec->key = brec_key(brec); - brec->data = bkey_record(brec->key); - - return result ? 1 : 0; + fd->bnode = bnode; + return res; + +invalid: + printk("HFS: inconsistency in B*Tree (%d,%d,%d,%u,%u)\n", + height, bnode->height, bnode->type, nidx, parent); + res = -EIO; +release: + hfs_bnode_put(bnode); + return res; } -/* - * hfs_bsucc() - * - * Description: - * This function overwrites '*brec' with its successor in the B-tree, - * obtaining the same type of access. - * Input Variable(s): - * struct hfs_brec *brec: address of the (struct hfs_brec) to overwrite - * with its successor - * Output Variable(s): - * struct hfs_brec *brec: address of the successor of the original - * '*brec' or to invalid data - * Returns: - * int: 0 on success, or one of -EINVAL, -EIO, or -EINVAL on failure - * Preconditions: - * 'brec' pointers to a "valid" (struct hfs_brec) - * Postconditions: - * If the given '*brec' is not "valid" -EINVAL is returned and - * '*brec' is unchanged. - * If the given 'brec' is "valid" but has no successor then -ENOENT - * is returned and '*brec' is invalid. - * If a call to hfs_bnode_find() is necessary to find the successor, - * but fails then -EIO is returned and '*brec' is invalid. - * If none of the three previous conditions prevents finding the - * successor of '*brec', then 0 is returned, and '*brec' is overwritten - * with the (struct hfs_brec) for its successor. - * In the cases when '*brec' is invalid, the old records is freed. - */ -int hfs_bsucc(struct hfs_brec *brec, int count) +int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) { - struct hfs_belem *belem; - struct hfs_bnode *bn; + int res; - if (!brec || !(belem = brec->bottom) || (belem != brec->top) || - !(bn = belem->bnr.bn) || (bn->magic != HFS_BNODE_MAGIC) || - !bn->tree || (bn->tree->magic != HFS_BTREE_MAGIC) || - !hfs_buffer_ok(bn->buf)) { - hfs_warn("hfs_bsucc: invalid/corrupt arguments.\n"); + res = hfs_brec_find(fd); + if (res) + return res; + if (fd->entrylength > rec_len) return -EINVAL; - } - - while (count) { - int left = bn->ndNRecs - belem->record; - - if (left < count) { - struct hfs_bnode_ref old; - hfs_u32 node; + hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength); + return 0; +} - /* Advance to next node */ - if (!(node = bn->ndFLink)) { - hfs_brec_relse(brec, belem); - return -ENOENT; +int hfs_brec_goto(struct hfs_find_data *fd, int cnt) +{ + struct hfs_btree *tree; + struct hfs_bnode *bnode; + int idx, res = 0; + u16 off, len, keylen; + + bnode = fd->bnode; + tree = bnode->tree; + + if (cnt < 0) { + cnt = -cnt; + while (cnt > fd->record) { + cnt -= fd->record + 1; + fd->record = bnode->num_recs - 1; + idx = bnode->prev; + if (!idx) { + res = -ENOENT; + goto out; + } + hfs_bnode_put(bnode); + bnode = hfs_bnode_find(tree, idx); + if (IS_ERR(bnode)) { + res = PTR_ERR(bnode); + bnode = NULL; + goto out; } - if (node == bn->node) { - hfs_warn("hfs_bsucc: corrupt btree\n"); - hfs_brec_relse(brec, belem); - return -EIO; + } + fd->record -= cnt; + } else { + while (cnt >= bnode->num_recs - fd->record) { + cnt -= bnode->num_recs - fd->record; + fd->record = 0; + idx = bnode->next; + if (!idx) { + res = -ENOENT; + goto out; } - old = belem->bnr; - belem->bnr = hfs_bnode_find(brec->tree, node, - belem->bnr.lock_type); - hfs_bnode_relse(&old); - if (!(bn = belem->bnr.bn)) { - return -EIO; + hfs_bnode_put(bnode); + bnode = hfs_bnode_find(tree, idx); + if (IS_ERR(bnode)) { + res = PTR_ERR(bnode); + bnode = NULL; + goto out; } - belem->record = 1; - count -= (left + 1); - } else { - belem->record += count; - break; } + fd->record += cnt; } - brec->key = belem_key(belem); - brec->data = bkey_record(brec->key); - if (brec->key->KeyLen > brec->tree->bthKeyLen) { - hfs_warn("hfs_bsucc: oversized key\n"); - hfs_brec_relse(brec, NULL); - return -EIO; - } - - return 0; + len = hfs_brec_lenoff(bnode, fd->record, &off); + keylen = hfs_brec_keylen(bnode, fd->record); + fd->keyoffset = off; + fd->keylength = keylen; + fd->entryoffset = off + keylen; + fd->entrylength = len - keylen; + hfs_bnode_read(bnode, fd->key, off, keylen); +out: + fd->bnode = bnode; + return res; } diff --git a/fs/hfs/bins_del.c b/fs/hfs/bins_del.c deleted file mode 100644 index a03b959e3f12..000000000000 --- a/fs/hfs/bins_del.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * linux/fs/hfs/bins_del.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the code common to inserting and deleting records - * in a B-tree. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#include "hfs_btree.h" - -/*================ File-local functions ================*/ - -/* - * hfs_bnode_update_key() - * - * Description: - * Updates the key for a bnode in its parent. - * The key change is propagated up the tree as necessary. - * Input Variable(s): - * struct hfs_brec *brec: the search path to update keys in - * struct hfs_belem *belem: the search path element with the changed key - * struct hfs_bnode *bnode: the bnode with the changed key - * int offset: the "distance" from 'belem->bn' to 'bnode': - * 0 if the change is in 'belem->bn', - * 1 if the change is in its right sibling, etc. - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'brec' points to a valid (struct hfs_brec) - * 'belem' points to a valid (struct hfs_belem) in 'brec'. - * 'bnode' points to a valid (struct hfs_bnode) which is non-empty - * and is 'belem->bn' or one of its siblings. - * 'offset' is as described above. - * Postconditions: - * The key change is propagated up the tree as necessary. - */ -void hfs_bnode_update_key(struct hfs_brec *brec, struct hfs_belem *belem, - struct hfs_bnode *bnode, int offset) -{ - int record = (--belem)->record + offset; - void *key = bnode_datastart(bnode) + 1; - int keysize = brec->tree->bthKeyLen; - struct hfs_belem *limit; - - memcpy(1+bnode_key(belem->bnr.bn, record), key, keysize); - - /* don't trash the header */ - if (brec->top > &brec->elem[1]) { - limit = brec->top; - } else { - limit = &brec->elem[1]; - } - - while ((belem > limit) && (record == 1)) { - record = (--belem)->record; - memcpy(1+belem_key(belem), key, keysize); - } -} - -/* - * hfs_bnode_shift_right() - * - * Description: - * Shifts some records from a node to its right neighbor. - * Input Variable(s): - * struct hfs_bnode* left: the node to shift records from - * struct hfs_bnode* right: the node to shift records to - * hfs_u16 first: the number of the first record in 'left' to move to 'right' - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'left' and 'right' point to valid (struct hfs_bnode)s. - * 'left' contains at least 'first' records. - * 'right' has enough free space to hold the records to be moved from 'left' - * Postconditions: - * The record numbered 'first' and all records after it in 'left' are - * placed at the beginning of 'right'. - * The key corresponding to 'right' in its parent is NOT updated. - */ -void hfs_bnode_shift_right(struct hfs_bnode *left, struct hfs_bnode *right, - int first) -{ - int i, adjust, nrecs; - unsigned size; - hfs_u16 *to, *from; - - if ((first <= 0) || (first > left->ndNRecs)) { - hfs_warn("bad argument to shift_right: first=%d, nrecs=%d\n", - first, left->ndNRecs); - return; - } - - /* initialize variables */ - nrecs = left->ndNRecs + 1 - first; - size = bnode_end(left) - bnode_offset(left, first); - - /* move (possibly empty) contents of right node forward */ - memmove(bnode_datastart(right) + size, - bnode_datastart(right), - bnode_end(right) - sizeof(struct NodeDescriptor)); - - /* copy in new records */ - memcpy(bnode_datastart(right), bnode_key(left,first), size); - - /* fix up offsets in right node */ - i = right->ndNRecs + 1; - from = RECTBL(right, i); - to = from - nrecs; - while (i--) { - hfs_put_hs(hfs_get_hs(from++) + size, to++); - } - adjust = sizeof(struct NodeDescriptor) - bnode_offset(left, first); - i = nrecs-1; - from = RECTBL(left, first+i); - while (i--) { - hfs_put_hs(hfs_get_hs(from++) + adjust, to++); - } - - /* fix record counts */ - left->ndNRecs -= nrecs; - right->ndNRecs += nrecs; -} - -/* - * hfs_bnode_shift_left() - * - * Description: - * Shifts some records from a node to its left neighbor. - * Input Variable(s): - * struct hfs_bnode* left: the node to shift records to - * struct hfs_bnode* right: the node to shift records from - * hfs_u16 last: the number of the last record in 'right' to move to 'left' - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'left' and 'right' point to valid (struct hfs_bnode)s. - * 'right' contains at least 'last' records. - * 'left' has enough free space to hold the records to be moved from 'right' - * Postconditions: - * The record numbered 'last' and all records before it in 'right' are - * placed at the end of 'left'. - * The key corresponding to 'right' in its parent is NOT updated. - */ -void hfs_bnode_shift_left(struct hfs_bnode *left, struct hfs_bnode *right, - int last) -{ - int i, adjust, nrecs; - unsigned size; - hfs_u16 *to, *from; - - if ((last <= 0) || (last > right->ndNRecs)) { - hfs_warn("bad argument to shift_left: last=%d, nrecs=%d\n", - last, right->ndNRecs); - return; - } - - /* initialize variables */ - size = bnode_offset(right, last + 1) - sizeof(struct NodeDescriptor); - - /* copy records to left node */ - memcpy(bnode_dataend(left), bnode_datastart(right), size); - - /* move (possibly empty) remainder of right node backward */ - memmove(bnode_datastart(right), bnode_datastart(right) + size, - bnode_end(right) - bnode_offset(right, last + 1)); - - /* fix up offsets */ - nrecs = left->ndNRecs; - i = last; - from = RECTBL(right, 2); - to = RECTBL(left, nrecs + 2); - adjust = bnode_offset(left, nrecs + 1) - sizeof(struct NodeDescriptor); - while (i--) { - hfs_put_hs(hfs_get_hs(from--) + adjust, to--); - } - i = right->ndNRecs + 1 - last; - ++from; - to = RECTBL(right, 1); - while (i--) { - hfs_put_hs(hfs_get_hs(from--) - size, to--); - } - - /* fix record counts */ - left->ndNRecs += last; - right->ndNRecs -= last; -} - -/* - * hfs_bnode_in_brec() - * - * Description: - * Determines whethet a given bnode is part of a given brec. - * This is used to avoid deadlock in the case of a corrupted b-tree. - * Input Variable(s): - * hfs_u32 node: the number of the node to check for. - * struct hfs_brec* brec: the brec to check in. - * Output Variable(s): - * NONE - * Returns: - * int: 1 it found, 0 if not - * Preconditions: - * 'brec' points to a valid struct hfs_brec. - * Postconditions: - * 'brec' is unchanged. - */ -int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec) -{ - const struct hfs_belem *belem = brec->bottom; - - while (belem && (belem >= brec->top)) { - if (belem->bnr.bn && (belem->bnr.bn->node == node)) { - return 1; - } - --belem; - } - return 0; -} diff --git a/fs/hfs/binsert.c b/fs/hfs/binsert.c deleted file mode 100644 index 7fe11c3b88d3..000000000000 --- a/fs/hfs/binsert.c +++ /dev/null @@ -1,536 +0,0 @@ -/* - * linux/fs/hfs/binsert.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the code to insert records in a B-tree. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#include "hfs_btree.h" - -/*================ File-local functions ================*/ -/* - * binsert_nonfull() - * - * Description: - * Inserts a record in a given bnode known to have sufficient space. - * Input Variable(s): - * struct hfs_brec* brec: pointer to the brec for the insertion - * struct hfs_belem* belem: the element in the search path to insert in - * struct hfs_bkey* key: pointer to the key for the record to insert - * void* data: pointer to the record to insert - * hfs_u16 keysize: size of the key to insert - * hfs_u16 datasize: size of the record to insert - * Output Variable(s): - * NONE - * Returns: - * NONE - * Preconditions: - * 'brec' points to a valid (struct hfs_brec). - * 'belem' points to a valid (struct hfs_belem) in 'brec', the node - * of which has enough free space to insert 'key' and 'data'. - * 'key' is a pointer to a valid (struct hfs_bkey) of length 'keysize' - * which, in sorted order, belongs at the location indicated by 'brec'. - * 'data' is non-NULL an points to appropriate data of length 'datasize' - * Postconditions: - * The record has been inserted in the position indicated by 'brec'. - */ -static void binsert_nonfull(struct hfs_brec *brec, struct hfs_belem *belem, - const struct hfs_bkey *key, const void *data, - hfs_u8 keysize, hfs_u16 datasize) -{ - int i, rec, nrecs, size, tomove; - hfs_u8 *start; - struct hfs_bnode *bnode = belem->bnr.bn; - - rec = ++(belem->record); - size = ROUND(keysize+1) + datasize; - nrecs = bnode->ndNRecs + 1; - tomove = bnode_offset(bnode, nrecs) - bnode_offset(bnode, rec); - - /* adjust the record table */ - for (i = nrecs; i >= rec; --i) { - hfs_put_hs(bnode_offset(bnode,i) + size, RECTBL(bnode,i+1)); - } - - /* make room */ - start = bnode_key(bnode, rec); - memmove(start + size, start, tomove); - - /* copy in the key and the data*/ - *start = keysize; - keysize = ROUND(keysize+1); - memcpy(start + 1, (hfs_u8 *)key + 1, keysize-1); - memcpy(start + keysize, data, datasize); - - /* update record count */ - ++bnode->ndNRecs; -} - -/* - * add_root() - * - * Description: - * Adds a new root to a B*-tree, increasing its height. - * Input Variable(s): - * struct hfs_btree *tree: the tree to add a new root to - * struct hfs_bnode *left: the new root's first child or NULL - * struct hfs_bnode *right: the new root's second child or NULL - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'tree' points to a valid (struct hfs_btree). - * 'left' and 'right' point to valid (struct hfs_bnode)s, which - * resulted from splitting the old root node, or are both NULL - * if there was no root node before. - * Postconditions: - * Upon success a new root node is added to 'tree' with either - * two children ('left' and 'right') or none. - */ -static void add_root(struct hfs_btree *tree, - struct hfs_bnode *left, - struct hfs_bnode *right) -{ - struct hfs_bnode_ref bnr; - struct hfs_bnode *root; - struct hfs_bkey *key; - int keylen = tree->bthKeyLen; - - if (left && !right) { - hfs_warn("add_root: LEFT but no RIGHT\n"); - return; - } - - bnr = hfs_bnode_alloc(tree); - if (!(root = bnr.bn)) { - return; - } - - root->sticky = HFS_STICKY; - tree->root = root; - tree->bthRoot = root->node; - ++tree->bthDepth; - - root->ndNHeight = tree->bthDepth; - root->ndFLink = 0; - root->ndBLink = 0; - - if (!left) { - /* tree was empty */ - root->ndType = ndLeafNode; - root->ndNRecs = 0; - - tree->bthFNode = root->node; - tree->bthLNode = root->node; - } else { - root->ndType = ndIndxNode; - root->ndNRecs = 2; - - hfs_put_hs(sizeof(struct NodeDescriptor) + ROUND(1+keylen) + - sizeof(hfs_u32), RECTBL(root, 2)); - key = bnode_key(root, 1); - key->KeyLen = keylen; - memcpy(key->value, - ((struct hfs_bkey *)bnode_key(left, 1))->value, keylen); - hfs_put_hl(left->node, bkey_record(key)); - - hfs_put_hs(sizeof(struct NodeDescriptor) + 2*ROUND(1+keylen) + - 2*sizeof(hfs_u32), RECTBL(root, 3)); - key = bnode_key(root, 2); - key->KeyLen = keylen; - memcpy(key->value, - ((struct hfs_bkey *)bnode_key(right, 1))->value, keylen); - hfs_put_hl(right->node, bkey_record(key)); - - /* the former root (left) is now just a normal node */ - left->sticky = HFS_NOT_STICKY; - if ((left->next = bhash(tree, left->node))) { - left->next->prev = left; - } - bhash(tree, left->node) = left; - } - hfs_bnode_relse(&bnr); - tree->dirt = 1; -} - -/* - * insert_empty_bnode() - * - * Description: - * Adds an empty node to the right of 'left'. - * Input Variable(s): - * struct hfs_btree *tree: the tree to add a node to - * struct hfs_bnode *left: the node to add a node after - * Output Variable(s): - * NONE - * Returns: - * struct hfs_bnode_ref *: reference to the new bnode. - * Preconditions: - * 'tree' points to a valid (struct hfs_btree) with at least 1 free node. - * 'left' points to a valid (struct hfs_bnode) belonging to 'tree'. - * Postconditions: - * If NULL is returned then 'tree' and 'left' are unchanged. - * Otherwise a node with 0 records is inserted in the tree to the right - * of the node 'left'. The 'ndFLink' of 'left' and the 'ndBLink' of - * the former right-neighbor of 'left' (if one existed) point to the - * new node. If 'left' had no right neighbor and is a leaf node the - * the 'bthLNode' of 'tree' points to the new node. The free-count and - * bitmap for 'tree' are kept current by hfs_bnode_alloc() which supplies - * the required node. - */ -static struct hfs_bnode_ref insert_empty_bnode(struct hfs_btree *tree, - struct hfs_bnode *left) -{ - struct hfs_bnode_ref retval; - struct hfs_bnode_ref right; - - retval = hfs_bnode_alloc(tree); - if (!retval.bn) { - hfs_warn("hfs_binsert: out of bnodes?.\n"); - goto done; - } - retval.bn->sticky = HFS_NOT_STICKY; - if ((retval.bn->next = bhash(tree, retval.bn->node))) { - retval.bn->next->prev = retval.bn; - } - bhash(tree, retval.bn->node) = retval.bn; - - if (left->ndFLink) { - right = hfs_bnode_find(tree, left->ndFLink, HFS_LOCK_WRITE); - if (!right.bn) { - hfs_warn("hfs_binsert: corrupt btree.\n"); - hfs_bnode_bitop(tree, retval.bn->node, 0); - hfs_bnode_relse(&retval); - goto done; - } - right.bn->ndBLink = retval.bn->node; - hfs_bnode_relse(&right); - } else if (left->ndType == ndLeafNode) { - tree->bthLNode = retval.bn->node; - tree->dirt = 1; - } - - retval.bn->ndFLink = left->ndFLink; - retval.bn->ndBLink = left->node; - retval.bn->ndType = left->ndType; - retval.bn->ndNHeight = left->ndNHeight; - retval.bn->ndNRecs = 0; - - left->ndFLink = retval.bn->node; - - done: - return retval; -} - -/* - * split() - * - * Description: - * Splits an over full node during insertion. - * Picks the split point that results in the most-nearly equal - * space usage in the new and old nodes. - * Input Variable(s): - * struct hfs_belem *elem: the over full node. - * int size: the number of bytes to be used by the new record and its key. - * Output Variable(s): - * struct hfs_belem *elem: changed to indicate where the new record - * should be inserted. - * Returns: - * struct hfs_bnode_ref: reference to the new bnode. - * Preconditions: - * 'elem' points to a valid path element corresponding to the over full node. - * 'size' is positive. - * Postconditions: - * The records in the node corresponding to 'elem' are redistributed across - * the old and new nodes so that after inserting the new record, the space - * usage in these two nodes is as equal as possible. - * 'elem' is updated so that a call to binsert_nonfull() will insert the - * new record in the correct location. - */ -static inline struct hfs_bnode_ref split(struct hfs_belem *elem, int size) -{ - struct hfs_bnode *bnode = elem->bnr.bn; - int nrecs, cutoff, index, tmp, used, in_right; - struct hfs_bnode_ref right; - - right = insert_empty_bnode(bnode->tree, bnode); - if (right.bn) { - nrecs = bnode->ndNRecs; - cutoff = (size + bnode_end(bnode) - - sizeof(struct NodeDescriptor) + - (nrecs+1)*sizeof(hfs_u16))/2; - used = 0; - in_right = 1; - /* note that this only works because records sizes are even */ - for (index=1; index <= elem->record; ++index) { - tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2; - used += tmp; - if (used > cutoff) { - goto found; - } - used += tmp; - } - tmp = (size + sizeof(hfs_u16))/2; - used += tmp; - if (used > cutoff) { - goto found; - } - in_right = 0; - used += tmp; - for (; index <= nrecs; ++index) { - tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2; - used += tmp; - if (used > cutoff) { - goto found; - } - used += tmp; - } - /* couldn't find the split point! */ - hfs_bnode_relse(&right); - } - return right; - -found: - if (in_right) { - elem->bnr = right; - elem->record -= index-1; - } - hfs_bnode_shift_right(bnode, right.bn, index); - - return right; -} - -/* - * binsert() - * - * Description: - * Inserts a record in a tree known to have enough room, even if the - * insertion requires the splitting of nodes. - * Input Variable(s): - * struct hfs_brec *brec: partial path to the node to insert in - * const struct hfs_bkey *key: key for the new record - * const void *data: data for the new record - * hfs_u8 keysize: size of the key - * hfs_u16 datasize: size of the data - * int reserve: number of nodes reserved in case of splits - * Output Variable(s): - * *brec = NULL - * Returns: - * int: 0 on success, error code on failure - * Preconditions: - * 'brec' points to a valid (struct hfs_brec) corresponding to a - * record in a leaf node, after which a record is to be inserted, - * or to "record 0" of the leaf node if the record is to be inserted - * before all existing records in the node. The (struct hfs_brec) - * includes all ancestors of the leaf node that are needed to - * complete the insertion including the parents of any nodes that - * will be split. - * 'key' points to a valid (struct hfs_bkey) which is appropriate - * to this tree, and which belongs at the insertion point. - * 'data' points data appropriate for the indicated node. - * 'keysize' gives the size in bytes of the key. - * 'datasize' gives the size in bytes of the data. - * 'reserve' gives the number of nodes that have been reserved in the - * tree to allow for splitting of nodes. - * Postconditions: - * All 'reserve'd nodes have been either used or released. - * *brec = NULL - * On success the key and data have been inserted at the indicated - * location in the tree, all appropriate fields of the in-core data - * structures have been changed and updated versions of the on-disk - * data structures have been scheduled for write-back to disk. - * On failure the B*-tree is probably invalid both on disk and in-core. - * - * XXX: Some attempt at repair might be made in the event of failure, - * or the fs should be remounted read-only so things don't get worse. - */ -static int binsert(struct hfs_brec *brec, const struct hfs_bkey *key, - const void *data, hfs_u8 keysize, hfs_u16 datasize, - int reserve) -{ - struct hfs_bnode_ref left, right, other; - struct hfs_btree *tree = brec->tree; - struct hfs_belem *belem = brec->bottom; - int tmpsize = 1 + tree->bthKeyLen; - struct hfs_bkey *tmpkey = hfs_malloc(tmpsize); - hfs_u32 node; - - while ((belem >= brec->top) && (belem->flags & HFS_BPATH_OVERFLOW)) { - left = belem->bnr; - if (left.bn->ndFLink && - hfs_bnode_in_brec(left.bn->ndFLink, brec)) { - hfs_warn("hfs_binsert: corrupt btree\n"); - tree->reserved -= reserve; - hfs_free(tmpkey, tmpsize); - return -EIO; - } - - right = split(belem, ROUND(keysize+1) + ROUND(datasize)); - --reserve; - --tree->reserved; - if (!right.bn) { - hfs_warn("hfs_binsert: unable to split node!\n"); - tree->reserved -= reserve; - hfs_free(tmpkey, tmpsize); - return -ENOSPC; - } - binsert_nonfull(brec, belem, key, data, keysize, datasize); - - if (belem->bnr.bn == left.bn) { - other = right; - if (belem->record == 1) { - hfs_bnode_update_key(brec, belem, left.bn, 0); - } - } else { - other = left; - } - - if (left.bn->node == tree->root->node) { - add_root(tree, left.bn, right.bn); - hfs_bnode_relse(&other); - goto done; - } - - data = &node; - datasize = sizeof(node); - node = htonl(right.bn->node); - key = tmpkey; - keysize = tree->bthKeyLen; - memcpy(tmpkey, bnode_key(right.bn, 1), keysize+1); - hfs_bnode_relse(&other); - - --belem; - } - - if (belem < brec->top) { - hfs_warn("hfs_binsert: Missing parent.\n"); - tree->reserved -= reserve; - hfs_free(tmpkey, tmpsize); - return -EIO; - } - - binsert_nonfull(brec, belem, key, data, keysize, datasize); - -done: - tree->reserved -= reserve; - hfs_free(tmpkey, tmpsize); - return 0; -} - -/*================ Global functions ================*/ - -/* - * hfs_binsert() - * - * Description: - * This function inserts a new record into a b-tree. - * Input Variable(s): - * struct hfs_btree *tree: pointer to the (struct hfs_btree) to insert in - * struct hfs_bkey *key: pointer to the (struct hfs_bkey) to insert - * void *data: pointer to the data to associate with 'key' in the b-tree - * unsigned int datasize: the size of the data - * Output Variable(s): - * NONE - * Returns: - * int: 0 on success, error code on failure - * Preconditions: - * 'tree' points to a valid (struct hfs_btree) - * 'key' points to a valid (struct hfs_bkey) - * 'data' points to valid memory of length 'datasize' - * Postconditions: - * If zero is returned then the record has been inserted in the - * indicated location updating all in-core data structures and - * scheduling all on-disk data structures for write-back. - */ -int hfs_binsert(struct hfs_btree *tree, const struct hfs_bkey *key, - const void *data, hfs_u16 datasize) -{ - struct hfs_brec brec; - struct hfs_belem *belem; - int err, reserve, retval; - hfs_u8 keysize; - - if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key || !data) { - hfs_warn("hfs_binsert: invalid arguments.\n"); - return -EINVAL; - } - - if (key->KeyLen > tree->bthKeyLen) { - hfs_warn("hfs_binsert: oversized key\n"); - return -EINVAL; - } - -restart: - if (!tree->bthNRecs) { - /* create the root bnode */ - add_root(tree, NULL, NULL); - if (!hfs_brec_init(&brec, tree, HFS_BFIND_INSERT)) { - hfs_warn("hfs_binsert: failed to create root.\n"); - return -ENOSPC; - } - } else { - err = hfs_bfind(&brec, tree, key, HFS_BFIND_INSERT); - if (err < 0) { - hfs_warn("hfs_binsert: hfs_brec_find failed.\n"); - return err; - } else if (err == 0) { - hfs_brec_relse(&brec, NULL); - return -EEXIST; - } - } - - keysize = key->KeyLen; - datasize = ROUND(datasize); - belem = brec.bottom; - belem->flags = 0; - if (bnode_freespace(belem->bnr.bn) < - (sizeof(hfs_u16) + ROUND(keysize+1) + datasize)) { - belem->flags |= HFS_BPATH_OVERFLOW; - } - if (belem->record == 0) { - belem->flags |= HFS_BPATH_FIRST; - } - - if (!belem->flags) { - hfs_brec_lock(&brec, brec.bottom); - reserve = 0; - } else { - reserve = brec.bottom - brec.top; - if (brec.top == 0) { - ++reserve; - } - /* make certain we have enough nodes to proceed */ - if ((tree->bthFree - tree->reserved) < reserve) { - hfs_brec_relse(&brec, NULL); - down(&tree->sem); - if ((tree->bthFree - tree->reserved) < reserve) { - hfs_btree_extend(tree); - } - up(&tree->sem); - if ((tree->bthFree - tree->reserved) < reserve) { - return -ENOSPC; - } else { - goto restart; - } - } - tree->reserved += reserve; - hfs_brec_lock(&brec, NULL); - } - - retval = binsert(&brec, key, data, keysize, datasize, reserve); - hfs_brec_relse(&brec, NULL); - if (!retval) { - ++tree->bthNRecs; - tree->dirt = 1; - } - return retval; -} diff --git a/fs/hfs/bitmap.c b/fs/hfs/bitmap.c index c9bb850d3d9a..117fe1e1983a 100644 --- a/fs/hfs/bitmap.c +++ b/fs/hfs/bitmap.c @@ -1,84 +1,111 @@ /* - * linux/fs/hfs/bitmap.c + * linux/fs/hfs/bitmap.c * * Copyright (C) 1996-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * Based on GPLed code Copyright (C) 1995 Michael Dreher * * This file contains the code to modify the volume bitmap: * search/set/clear bits. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. */ -#include "hfs.h" - -/*================ Global functions ================*/ +#include "hfs_fs.h" /* - * hfs_vbm_count_free() + * hfs_find_zero_bit() * * Description: - * Count the number of consecutive cleared bits in the bitmap blocks of - * the hfs MDB starting at bit number 'start'. 'mdb' had better - * be locked or the indicated number of blocks may be no longer free, - * when this functions returns! - * Input Variable(s): - * struct hfs_mdb *mdb: Pointer to the hfs MDB - * hfs_u16 start: bit number to start at - * Output Variable(s): - * NONE - * Returns: - * The number of consecutive cleared bits starting at bit 'start' - * Preconditions: - * 'mdb' points to a "valid" (struct hfs_mdb). - * Postconditions: - * NONE + * Given a block of memory, its length in bits, and a starting bit number, + * determine the number of the first zero bits (in left-to-right ordering) + * in that range. + * + * Returns >= 'size' if no zero bits are found in the range. + * + * Accesses memory in 32-bit aligned chunks of 32-bits and thus + * may read beyond the 'size'th bit. */ -hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *mdb, hfs_u16 start) +static u32 hfs_find_set_zero_bits(u32 *bitmap, u32 size, u32 offset, u32 *max) { - hfs_u16 block_nr; /* index of the current bitmap block */ - hfs_u16 bit_nr; /* index of the current bit in block */ - hfs_u16 count; /* number of bits found so far */ - hfs_u16 len; /* number of bits found in this block */ - hfs_u16 max_block; /* index of last bitmap block */ - hfs_u16 max_bits; /* index of last bit in block */ - - /* is this a valid HFS MDB? */ - if (!mdb) { - return 0; + u32 *curr, *end; + u32 val, mask, start, len; + int i; + + len = *max; + if (!len) + return size; + + curr = bitmap + (offset / 32); + end = bitmap + ((size + 31) / 32); + + /* scan the first partial u32 for zero bits */ + val = *curr; + if (~val) { + val = be32_to_cpu(val); + i = offset % 32; + mask = (1U << 31) >> i; + for (; i < 32; mask >>= 1, i++) { + if (!(val & mask)) + goto found; + } } - block_nr = start / HFS_BM_BPB; - bit_nr = start % HFS_BM_BPB; - max_block = (mdb->fs_ablocks + HFS_BM_BPB - 1) / HFS_BM_BPB - 1; - - count = 0; - while (block_nr <= max_block) { - if (block_nr != max_block) { - max_bits = HFS_BM_BPB; - } else { - max_bits = mdb->fs_ablocks % HFS_BM_BPB; + /* scan complete u32s for the first zero bit */ + while (++curr < end) { + val = *curr; + if (~val) { + val = be32_to_cpu(val); + mask = 1 << 31; + for (i = 0; i < 32; mask >>= 1, i++) { + if (!(val & mask)) + goto found; + } } - - len=hfs_count_zero_bits(hfs_buffer_data(mdb->bitmap[block_nr]), - max_bits, bit_nr); - count += len; - - /* see if we fell short of the end of this block */ - if ((len + bit_nr) < max_bits) { + } + return size; + +found: + start = (curr - bitmap) * 32 + i; + if (start >= size) + return start; + /* do any partial u32 at the start */ + len = min(size - start, len); + while (1) { + val |= mask; + if (++i >= 32) + break; + mask >>= 1; + if (!--len || val & mask) + goto done; + } + if (!--len) + goto done; + *curr++ = cpu_to_be32(val); + /* do full u32s */ + while (1) { + val = be32_to_cpu(*curr); + if (len < 32) + break; + if (val) { + len = 32; break; } - - ++block_nr; - bit_nr = 0; + *curr++ = 0xffffffffU; + len -= 32; + } + /* do any partial u32 at end */ + mask = 1U << 31; + for (i = 0; i < len; i++) { + if (val & mask) + break; + val |= mask; + mask >>= 1; } - return count; +done: + *curr = cpu_to_be32(val); + *max = (curr - bitmap) * 32 + i - start; + return start; } /* @@ -92,10 +119,10 @@ hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *mdb, hfs_u16 start) * the bit number stored in 's_alloc_ptr' of the MDB. * Input Variable(s): * struct hfs_mdb *mdb: Pointer to the hfs MDB - * hfs_u16 *num_bits: Pointer to the number of cleared bits + * u16 *num_bits: Pointer to the number of cleared bits * to search for * Output Variable(s): - * hfs_u16 *num_bits: The number of consecutive clear bits of the + * u16 *num_bits: The number of consecutive clear bits of the * returned range. If the bitmap is fragmented, this will be less than * requested and it will be zero, when the disk is full. * Returns: @@ -103,207 +130,51 @@ hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *mdb, hfs_u16 start) * found. When 'num_bits' is zero, this is invalid! * Preconditions: * 'mdb' points to a "valid" (struct hfs_mdb). - * 'num_bits' points to a variable of type (hfs_u16), which contains + * 'num_bits' points to a variable of type (u16), which contains * the number of cleared bits to find. * Postconditions: * 'num_bits' is set to the length of the found sequence. */ -hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *mdb, hfs_u16 *num_bits) +u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits) { - hfs_u16 block_nr; /* index of the current bitmap block */ + void *bitmap; + u32 pos; - /* position and length of current portion of a run */ - hfs_u16 cur_pos, cur_len; - - /* position and length of current complete run */ - hfs_u16 pos=0, len=0; - - /* position and length of longest complete run */ - hfs_u16 longest_pos=0, longest_len=0; - - void *bitmap; /* contents of the current bitmap block */ - hfs_u16 max_block; /* upper limit of outer loop */ - hfs_u16 max_bits; /* upper limit of inner loop */ - - /* is this a valid HFS MDB? */ - if (!mdb) { - *num_bits = 0; - hfs_warn("hfs_vbm_search_free: not a valid MDB\n"); - return 0; - } - /* make sure we have actual work to perform */ - if (!(*num_bits)) { + if (!*num_bits) return 0; - } - max_block = (mdb->fs_ablocks+HFS_BM_BPB-1) / HFS_BM_BPB - 1; - - /* search all bitmap blocks */ - for (block_nr = 0; block_nr <= max_block; block_nr++) { - bitmap = hfs_buffer_data(mdb->bitmap[block_nr]); + down(&HFS_SB(sb)->bitmap_lock); + bitmap = HFS_SB(sb)->bitmap; - if (block_nr != max_block) { - max_bits = HFS_BM_BPB; - } else { - max_bits = mdb->fs_ablocks % HFS_BM_BPB; + pos = hfs_find_set_zero_bits(bitmap, HFS_SB(sb)->fs_ablocks, goal, num_bits); + if (pos >= HFS_SB(sb)->fs_ablocks) { + if (goal) + pos = hfs_find_set_zero_bits(bitmap, goal, 0, num_bits); + if (pos >= HFS_SB(sb)->fs_ablocks) { + *num_bits = pos = 0; + goto out; } - - cur_pos = 0; - do { - cur_len = hfs_count_zero_bits(bitmap, max_bits, - cur_pos); - len += cur_len; - if (len > longest_len) { - longest_pos = pos; - longest_len = len; - if (len >= *num_bits) { - goto search_end; - } - } - if ((cur_pos + cur_len) == max_bits) { - break; /* zeros may continue into next block */ - } - - /* find start of next run of zeros */ - cur_pos = hfs_find_zero_bit(bitmap, max_bits, - cur_pos + cur_len); - pos = cur_pos + HFS_BM_BPB*block_nr; - len = 0; - } while (cur_pos < max_bits); } -search_end: - *num_bits = longest_len; - return longest_pos; + dprint(DBG_BITMAP, "alloc_bits: %u,%u\n", pos, *num_bits); + HFS_SB(sb)->free_ablocks -= *num_bits; + hfs_bitmap_dirty(sb); +out: + up(&HFS_SB(sb)->bitmap_lock); + return pos; } /* - * hfs_set_vbm_bits() - * - * Description: - * Set the requested bits in the volume bitmap of the hfs filesystem - * Input Variable(s): - * struct hfs_mdb *mdb: Pointer to the hfs MDB - * hfs_u16 start: The offset of the first bit - * hfs_u16 count: The number of bits - * Output Variable(s): - * None - * Returns: - * 0: no error - * -1: One of the bits was already set. This is a strange - * error and when it happens, the filesystem must be repaired! - * -2: One or more of the bits are out of range of the bitmap. - * -3: The 's_magic' field of the MDB does not match - * Preconditions: - * 'mdb' points to a "valid" (struct hfs_mdb). - * Postconditions: - * Starting with bit number 'start', 'count' bits in the volume bitmap - * are set. The affected bitmap blocks are marked "dirty", the free - * block count of the MDB is updated and the MDB is marked dirty. - */ -int hfs_set_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count) -{ - hfs_u16 block_nr; /* index of the current bitmap block */ - hfs_u16 u32_nr; /* index of the current hfs_u32 in block */ - hfs_u16 bit_nr; /* index of the current bit in hfs_u32 */ - hfs_u16 left = count; /* number of bits left to be set */ - hfs_u32 *bitmap; /* the current bitmap block's contents */ - - /* is this a valid HFS MDB? */ - if (!mdb) { - return -3; - } - - /* is there any actual work to be done? */ - if (!count) { - return 0; - } - - /* are all of the bits in range? */ - if ((start + count) > mdb->fs_ablocks) { - return -2; - } - - block_nr = start / HFS_BM_BPB; - u32_nr = (start % HFS_BM_BPB) / 32; - bit_nr = start % 32; - - /* bitmap is always on a 32-bit boundary */ - bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]); - - /* do any partial hfs_u32 at the start */ - if (bit_nr != 0) { - while ((bit_nr < 32) && left) { - if (hfs_set_bit(bit_nr, bitmap + u32_nr)) { - hfs_buffer_dirty(mdb->bitmap[block_nr]); - return -1; - } - ++bit_nr; - --left; - } - bit_nr=0; - - /* advance u32_nr and check for end of this block */ - if (++u32_nr > 127) { - u32_nr = 0; - hfs_buffer_dirty(mdb->bitmap[block_nr]); - ++block_nr; - /* bitmap is always on a 32-bit boundary */ - bitmap = (hfs_u32 *) - hfs_buffer_data(mdb->bitmap[block_nr]); - } - } - - /* do full hfs_u32s */ - while (left > 31) { - if (bitmap[u32_nr] != ((hfs_u32)0)) { - hfs_buffer_dirty(mdb->bitmap[block_nr]); - return -1; - } - bitmap[u32_nr] = ~((hfs_u32)0); - left -= 32; - - /* advance u32_nr and check for end of this block */ - if (++u32_nr > 127) { - u32_nr = 0; - hfs_buffer_dirty(mdb->bitmap[block_nr]); - ++block_nr; - /* bitmap is always on a 32-bit boundary */ - bitmap = (hfs_u32 *) - hfs_buffer_data(mdb->bitmap[block_nr]); - } - } - - - /* do any partial hfs_u32 at end */ - while (left) { - if (hfs_set_bit(bit_nr, bitmap + u32_nr)) { - hfs_buffer_dirty(mdb->bitmap[block_nr]); - return -1; - } - ++bit_nr; - --left; - } - - hfs_buffer_dirty(mdb->bitmap[block_nr]); - mdb->free_ablocks -= count; - - /* successful completion */ - hfs_mdb_dirty(mdb->sys_mdb); - return 0; -} - -/* * hfs_clear_vbm_bits() * * Description: * Clear the requested bits in the volume bitmap of the hfs filesystem * Input Variable(s): * struct hfs_mdb *mdb: Pointer to the hfs MDB - * hfs_u16 start: The offset of the first bit - * hfs_u16 count: The number of bits + * u16 start: The offset of the first bit + * u16 count: The number of bits * Output Variable(s): * None * Returns: @@ -311,7 +182,6 @@ int hfs_set_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count) * -1: One of the bits was already clear. This is a strange * error and when it happens, the filesystem must be repaired! * -2: One or more of the bits are out of range of the bitmap. - * -3: The 's_magic' field of the MDB does not match * Preconditions: * 'mdb' points to a "valid" (struct hfs_mdb). * Postconditions: @@ -319,94 +189,54 @@ int hfs_set_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count) * are cleared. The affected bitmap blocks are marked "dirty", the free * block count of the MDB is updated and the MDB is marked dirty. */ -int hfs_clear_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count) +int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count) { - hfs_u16 block_nr; /* index of the current bitmap block */ - hfs_u16 u32_nr; /* index of the current hfs_u32 in block */ - hfs_u16 bit_nr; /* index of the current bit in hfs_u32 */ - hfs_u16 left = count; /* number of bits left to be set */ - hfs_u32 *bitmap; /* the current bitmap block's contents */ - - /* is this a valid HFS MDB? */ - if (!mdb) { - return -3; - } + u32 *curr; + u32 mask; + int i, len; /* is there any actual work to be done? */ - if (!count) { + if (!count) return 0; - } + dprint(DBG_BITMAP, "clear_bits: %u,%u\n", start, count); /* are all of the bits in range? */ - if ((start + count) > mdb->fs_ablocks) { + if ((start + count) > HFS_SB(sb)->fs_ablocks) return -2; - } - - block_nr = start / HFS_BM_BPB; - u32_nr = (start % HFS_BM_BPB) / 32; - bit_nr = start % 32; + down(&HFS_SB(sb)->bitmap_lock); /* bitmap is always on a 32-bit boundary */ - bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]); - - /* do any partial hfs_u32 at the start */ - if (bit_nr != 0) { - while ((bit_nr < 32) && left) { - if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) { - hfs_buffer_dirty(mdb->bitmap[block_nr]); - return -1; - } - ++bit_nr; - --left; - } - bit_nr=0; - - /* advance u32_nr and check for end of this block */ - if (++u32_nr > 127) { - u32_nr = 0; - hfs_buffer_dirty(mdb->bitmap[block_nr]); - ++block_nr; - /* bitmap is always on a 32-bit boundary */ - bitmap = (hfs_u32 *) - hfs_buffer_data(mdb->bitmap[block_nr]); + curr = HFS_SB(sb)->bitmap + (start / 32); + len = count; + + /* do any partial u32 at the start */ + i = start % 32; + if (i) { + int j = 32 - i; + mask = 0xffffffffU << j; + if (j > count) { + mask |= 0xffffffffU >> (i + count); + *curr &= cpu_to_be32(mask); + goto out; } + *curr++ &= cpu_to_be32(mask); + count -= j; } - /* do full hfs_u32s */ - while (left > 31) { - if (bitmap[u32_nr] != ~((hfs_u32)0)) { - hfs_buffer_dirty(mdb->bitmap[block_nr]); - return -1; - } - bitmap[u32_nr] = ((hfs_u32)0); - left -= 32; - - /* advance u32_nr and check for end of this block */ - if (++u32_nr > 127) { - u32_nr = 0; - hfs_buffer_dirty(mdb->bitmap[block_nr]); - ++block_nr; - /* bitmap is always on a 32-bit boundary */ - bitmap = (hfs_u32 *) - hfs_buffer_data(mdb->bitmap[block_nr]); - } + /* do full u32s */ + while (count >= 32) { + *curr++ = 0; + count -= 32; } - - - /* do any partial hfs_u32 at end */ - while (left) { - if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) { - hfs_buffer_dirty(mdb->bitmap[block_nr]); - return -1; - } - ++bit_nr; - --left; + /* do any partial u32 at end */ + if (count) { + mask = 0xffffffffU >> count; + *curr &= cpu_to_be32(mask); } +out: + HFS_SB(sb)->free_ablocks += len; + up(&HFS_SB(sb)->bitmap_lock); + hfs_bitmap_dirty(sb); - hfs_buffer_dirty(mdb->bitmap[block_nr]); - mdb->free_ablocks += count; - - /* successful completion */ - hfs_mdb_dirty(mdb->sys_mdb); return 0; } diff --git a/fs/hfs/bitops.c b/fs/hfs/bitops.c deleted file mode 100644 index e2cb38877e76..000000000000 --- a/fs/hfs/bitops.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * linux/fs/hfs/bitops.c - * - * Copyright (C) 1996 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains functions to handle bitmaps in "left-to-right" - * bit-order such that the MSB of a 32-bit big-endian word is bit 0. - * (This corresponds to bit 7 of a 32-bit little-endian word.) - * - * I have tested and confirmed that the results are identical on the - * Intel x86, PowerPC and DEC Alpha processors. - * - * "XXX" in a comment is a note to myself to consider changing something. - */ - -#include "hfs.h" - -/*================ Global functions ================*/ - -/* - * hfs_find_zero_bit() - * - * Description: - * Given a block of memory, its length in bits, and a starting bit number, - * determine the number of the first zero bits (in left-to-right ordering) - * in that range. - * - * Returns >= 'size' if no zero bits are found in the range. - * - * Accesses memory in 32-bit aligned chunks of 32-bits and thus - * may read beyond the 'size'th bit. - */ -hfs_u32 hfs_find_zero_bit(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset) -{ - const hfs_u32 *end = start + ((size + 31) >> 5); - const hfs_u32 *curr = start + (offset >> 5); - int bit = offset % 32; - - if (offset < size) { - /* scan the first partial hfs_u32 for zero bits */ - if (bit != 0) { - do { - if (!hfs_test_bit(bit, curr)) { - goto done; - } - ++bit; - } while (bit < 32); - bit = 0; - ++curr; - } - - /* scan complete hfs_u32s for the first zero bit */ - while (curr < end) { - if (*curr == ~((hfs_u32)0)) { - ++curr; - } else { - while (hfs_test_bit(bit, curr)) { - ++bit; - } - break; - } - } - -done: - bit |= (curr - start) << 5; - return bit; - } else { - return size; - } -} - -/* - * hfs_count_zero_bits() - * - * Description: - * Given a block of memory, its length in bits, and a starting bit number, - * determine the number of consecutive zero bits (in left-to-right ordering) - * in that range. - * - * Accesses memory in 32-bit aligned chunks of 32-bits and thus - * may read beyond the 'size'th bit. - */ -hfs_u32 hfs_count_zero_bits(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset) -{ - const hfs_u32 *end = start + ((size + 31) >> 5); - const hfs_u32 *curr = start + (offset >> 5); - int bit = offset % 32; - - if (offset < size) { - /* scan the first partial hfs_u32 for one bits */ - if (bit != 0) { - do { - if (hfs_test_bit(bit, curr)) { - goto done; - } - ++bit; - } while (bit < 32); - bit = 0; - ++curr; - } - - /* scan complete hfs_u32s for the first one bit */ - while (curr < end) { - if (*curr == ((hfs_u32)0)) { - ++curr; - } else { - while (!hfs_test_bit(bit, curr)) { - ++bit; - } - break; - } - } - -done: - bit |= (curr - start) << 5; - if (bit > size) { - bit = size; - } - return bit - offset; - } else { - return 0; - } -} diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index d7f0566e3cc0..d0cf0a6f431c 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -1,544 +1,486 @@ /* - * linux/fs/hfs/bnode.c + * linux/fs/hfs/bnode.c * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> * - * This file contains the code to access nodes in the B-tree structure. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - * - * The code in this file initializes some structures which contain - * pointers by calling memset(&foo, 0, sizeof(foo)). - * This produces the desired behavior only due to the non-ANSI - * assumption that the machine representation of NULL is all zeros. + * Handle basic btree node operations */ -#include "hfs_btree.h" +#include <linux/pagemap.h> +#include <linux/swap.h> -/*================ File-local variables ================*/ - -/* debugging statistics */ -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) -int bnode_count = 0; -#endif +#include "btree.h" -/*================ Global functions ================*/ +#define REF_PAGES 0 -/* - * hfs_bnode_delete() - * - * Description: - * This function is called to remove a bnode from the cache and - * release its resources. - * Input Variable(s): - * struct hfs_bnode *bn: Pointer to the (struct hfs_bnode) to be - * removed from the cache. - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'bn' points to a "valid" (struct hfs_bnode). - * Postconditions: - * The node 'bn' is removed from the cache, its memory freed and its - * buffer (if any) released. - */ -void hfs_bnode_delete(struct hfs_bnode *bn) +void hfs_bnode_read(struct hfs_bnode *node, void *buf, + int off, int len) { -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - --bnode_count; -#endif - /* join neighbors */ - if (bn->next) { - bn->next->prev = bn->prev; - } - if (bn->prev) { - bn->prev->next = bn->next; - } - /* fix cache slot if necessary */ - if (bhash(bn->tree, bn->node) == bn) { - bhash(bn->tree, bn->node) = bn->next; - } - /* release resources */ - hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */ - HFS_DELETE(bn); + struct page *page; + + off += node->page_offset; + page = node->page[0]; + + memcpy(buf, kmap(page) + off, len); + kunmap(page); +} + +u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) +{ + u16 data; + // optimize later... + hfs_bnode_read(node, &data, off, 2); + return be16_to_cpu(data); } +u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) +{ + u8 data; + // optimize later... + hfs_bnode_read(node, &data, off, 1); + return data; +} -/* - * hfs_bnode_read() - * - * Description: - * This function creates a (struct hfs_bnode) and, if appropriate, - * inserts it in the cache. - * Input Variable(s): - * struct hfs_bnode *bnode: pointer to the new bnode. - * struct hfs_btree *tree: pointer to the (struct hfs_btree) - * containing the desired node - * hfs_u32 node: the number of the desired node. - * int sticky: the value to assign to the 'sticky' field. - * Output Variable(s): - * NONE - * Returns: - * (struct hfs_bnode *) pointing to the newly created bnode or NULL. - * Preconditions: - * 'bnode' points to a "valid" (struct hfs_bnode). - * 'tree' points to a "valid" (struct hfs_btree). - * 'node' is an existing node number in the B-tree. - * Postconditions: - * The following are true of 'bnode' upon return: - * The 'magic' field is set to indicate a valid (struct hfs_bnode). - * The 'sticky', 'tree' and 'node' fields are initialized to the - * values of the of the corresponding arguments. - * If the 'sticky' argument is zero then the fields 'prev' and - * 'next' are initialized by inserting the (struct hfs_bnode) in the - * linked list of the appropriate cache slot; otherwise they are - * initialized to NULL. - * The data is read from disk (or buffer cache) and the 'buf' field - * points to the buffer for that data. - * If no other processes tried to access this node while this - * process was waiting on disk I/O (if necessary) then the - * remaining fields are zero ('count', 'resrv', 'lock') or NULL - * ('wqueue', 'rqueue') corresponding to no accesses. - * If there were access attempts during I/O then they were blocked - * until the I/O was complete, and the fields 'count', 'resrv', - * 'lock', 'wqueue' and 'rqueue' reflect the results of unblocking - * those processes when the I/O was completed. - */ -void hfs_bnode_read(struct hfs_bnode *bnode, struct hfs_btree *tree, - hfs_u32 node, int sticky) +void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) { - struct NodeDescriptor *nd; - int block, lcv; - hfs_u16 curr, prev, limit; - - /* Initialize the structure */ - memset(bnode, 0, sizeof(*bnode)); - bnode->magic = HFS_BNODE_MAGIC; - bnode->tree = tree; - bnode->node = node; - bnode->sticky = sticky; - hfs_init_waitqueue(&bnode->rqueue); - hfs_init_waitqueue(&bnode->wqueue); - - if (sticky == HFS_NOT_STICKY) { - /* Insert it in the cache if appropriate */ - if ((bnode->next = bhash(tree, node))) { - bnode->next->prev = bnode; - } - bhash(tree, node) = bnode; - } + struct hfs_btree *tree; + int key_len; - /* Make the bnode look like it is being - modified so other processes will wait for - the I/O to complete */ - bnode->count = bnode->resrv = bnode->lock = 1; - - /* Read in the node, possibly causing a schedule() - call. If the I/O fails then emit a warning. Each - process that was waiting on the bnode (including - the current one) will notice the failure and - hfs_bnode_relse() the node. The last hfs_bnode_relse() - will call hfs_bnode_delete() and discard the bnode. */ - - block = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0); - if (!block) { - hfs_warn("hfs_bnode_read: bad node number 0x%08x\n", node); - } else if (hfs_buffer_ok(bnode->buf = - hfs_buffer_get(tree->sys_mdb, block, 1))) { - /* read in the NodeDescriptor */ - nd = (struct NodeDescriptor *)hfs_buffer_data(bnode->buf); - bnode->ndFLink = hfs_get_hl(nd->ndFLink); - bnode->ndBLink = hfs_get_hl(nd->ndBLink); - bnode->ndType = nd->ndType; - bnode->ndNHeight = nd->ndNHeight; - bnode->ndNRecs = hfs_get_hs(nd->ndNRecs); - - /* verify the integrity of the node */ - prev = sizeof(struct NodeDescriptor); - limit = HFS_SECTOR_SIZE - sizeof(hfs_u16)*(bnode->ndNRecs + 1); - for (lcv=1; lcv <= (bnode->ndNRecs + 1); ++lcv) { - curr = hfs_get_hs(RECTBL(bnode, lcv)); - if ((curr < prev) || (curr > limit)) { - hfs_warn("hfs_bnode_read: corrupt node " - "number 0x%08x\n", node); - hfs_buffer_put(bnode->buf); - bnode->buf = NULL; - break; - } - prev = curr; - } - } + tree = node->tree; + if (node->type == HFS_NODE_LEAF || + tree->attributes & HFS_TREE_VARIDXKEYS) + key_len = hfs_bnode_read_u8(node, off) + 1; + else + key_len = tree->max_key_len + 1; - /* Undo our fakery with the lock state and - hfs_wake_up() anyone who we managed to trick */ - --bnode->count; - bnode->resrv = bnode->lock = 0; - hfs_wake_up(&bnode->rqueue); + hfs_bnode_read(node, key, off, key_len); } -/* - * hfs_bnode_lock() - * - * Description: - * This function does the locking of a bnode. - * Input Variable(s): - * struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to lock - * int lock_type: the type of lock desired - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'bn' points to a "valid" (struct hfs_bnode). - * 'lock_type' is a valid hfs_lock_t - * Postconditions: - * The 'count' field of 'bn' is incremented by one. If 'lock_type' - * is HFS_LOCK_RESRV the 'resrv' field is also incremented. - */ -void hfs_bnode_lock(struct hfs_bnode_ref *bnr, int lock_type) +void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) { - struct hfs_bnode *bn = bnr->bn; + struct page *page; + + off += node->page_offset; + page = node->page[0]; - if ((lock_type == bnr->lock_type) || !bn) { + memcpy(kmap(page) + off, buf, len); + kunmap(page); + set_page_dirty(page); +} + +void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data) +{ + data = cpu_to_be16(data); + // optimize later... + hfs_bnode_write(node, &data, off, 2); +} + +void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data) +{ + // optimize later... + hfs_bnode_write(node, &data, off, 1); +} + +void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) +{ + struct page *page; + + off += node->page_offset; + page = node->page[0]; + + memset(kmap(page) + off, 0, len); + kunmap(page); + set_page_dirty(page); +} + +void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, + struct hfs_bnode *src_node, int src, int len) +{ + struct hfs_btree *tree; + struct page *src_page, *dst_page; + + dprint(DBG_BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len); + if (!len) return; - } + tree = src_node->tree; + src += src_node->page_offset; + dst += dst_node->page_offset; + src_page = src_node->page[0]; + dst_page = dst_node->page[0]; + + memcpy(kmap(dst_page) + dst, kmap(src_page) + src, len); + kunmap(src_page); + kunmap(dst_page); + set_page_dirty(dst_page); +} - if (bnr->lock_type == HFS_LOCK_WRITE) { - hfs_bnode_commit(bnr->bn); - } +void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) +{ + struct page *page; + void *ptr; - switch (lock_type) { - default: - goto bail; - break; + dprint(DBG_BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len); + if (!len) + return; + src += node->page_offset; + dst += node->page_offset; + page = node->page[0]; + ptr = kmap(page); + memmove(ptr + dst, ptr + src, len); + kunmap(page); + set_page_dirty(page); +} - case HFS_LOCK_READ: - /* We may not obtain read access if any process is - currently modifying or waiting to modify this node. - If we can't obtain access we wait on the rqueue - wait queue to be woken up by the modifying process - when it relinquishes its lock. */ - switch (bnr->lock_type) { - default: - goto bail; - break; - - case HFS_LOCK_NONE: - while (bn->lock || waitqueue_active(&bn->wqueue)) { - hfs_sleep_on(&bn->rqueue); - } - ++bn->count; - break; - } - break; - - case HFS_LOCK_RESRV: - /* We may not obtain a reservation (read access with - an option to write later), if any process currently - holds a reservation on this node. That includes - any process which is currently modifying this node. - If we can't obtain access, then we wait on the - rqueue wait queue to e woken up by the - reservation-holder when it calls hfs_bnode_relse. */ - switch (bnr->lock_type) { - default: - goto bail; - break; - - case HFS_LOCK_NONE: - while (bn->resrv) { - hfs_sleep_on(&bn->rqueue); - } - bn->resrv = 1; - ++bn->count; - break; - - case HFS_LOCK_WRITE: - bn->lock = 0; - hfs_wake_up(&bn->rqueue); - break; - } - break; - - case HFS_LOCK_WRITE: - switch (bnr->lock_type) { - default: - goto bail; - break; - - case HFS_LOCK_NONE: - while (bn->resrv) { - hfs_sleep_on(&bn->rqueue); - } - bn->resrv = 1; - ++bn->count; - case HFS_LOCK_RESRV: - while (bn->count > 1) { - hfs_sleep_on(&bn->wqueue); - } - bn->lock = 1; - break; +void hfs_bnode_dump(struct hfs_bnode *node) +{ + struct hfs_bnode_desc desc; + u32 cnid; + int i, off, key_off; + + dprint(DBG_BNODE_MOD, "bnode: %d\n", node->this); + hfs_bnode_read(node, &desc, 0, sizeof(desc)); + dprint(DBG_BNODE_MOD, "%d, %d, %d, %d, %d\n", + be32_to_cpu(desc.next), be32_to_cpu(desc.prev), + desc.type, desc.height, be16_to_cpu(desc.num_recs)); + + off = node->tree->node_size - 2; + for (i = be16_to_cpu(desc.num_recs); i >= 0; off -= 2, i--) { + key_off = hfs_bnode_read_u16(node, off); + dprint(DBG_BNODE_MOD, " %d", key_off); + if (i && node->type == HFS_NODE_INDEX) { + int tmp; + + if (node->tree->attributes & HFS_TREE_VARIDXKEYS) + tmp = (hfs_bnode_read_u8(node, key_off) | 1) + 1; + else + tmp = node->tree->max_key_len + 1; + dprint(DBG_BNODE_MOD, " (%d,%d", tmp, hfs_bnode_read_u8(node, key_off)); + hfs_bnode_read(node, &cnid, key_off + tmp, 4); + dprint(DBG_BNODE_MOD, ",%d)", be32_to_cpu(cnid)); + } else if (i && node->type == HFS_NODE_LEAF) { + int tmp; + + tmp = hfs_bnode_read_u8(node, key_off); + dprint(DBG_BNODE_MOD, " (%d)", tmp); } - break; + } + dprint(DBG_BNODE_MOD, "\n"); +} - case HFS_LOCK_NONE: - switch (bnr->lock_type) { - default: - goto bail; - break; - - case HFS_LOCK_READ: - /* This process was reading this node. If - there is now exactly one other process using - the node then hfs_wake_up() a (potentially - nonexistent) waiting process. Note that I - refer to "a" process since the reservation - system ensures that only one process can - get itself on the wait queue. */ - if (bn->count == 2) { - hfs_wake_up(&bn->wqueue); - } - break; - - case HFS_LOCK_WRITE: - /* This process was modifying this node. - Unlock the node and fall-through to the - HFS_LOCK_RESRV case, since a 'reservation' - is a prerequisite for HFS_LOCK_WRITE. */ - bn->lock = 0; - case HFS_LOCK_RESRV: - /* This process had placed a 'reservation' on - this node, indicating an intention to - possibly modify the node. We can get to - this spot directly (if the 'reservation' - not converted to a HFS_LOCK_WRITE), or by - falling through from the above case if the - reservation was converted. - Since HFS_LOCK_RESRV and HFS_LOCK_WRITE - both block processes that want access - (HFS_LOCK_RESRV blocks other processes that - want reservations but allow HFS_LOCK_READ - accesses, while HFS_LOCK_WRITE must have - exclusive access and thus blocks both - types) we hfs_wake_up() any processes that - might be waiting for access. If multiple - processes are waiting for a reservation - then the magic of process scheduling will - settle the dispute. */ - bn->resrv = 0; - hfs_wake_up(&bn->rqueue); - break; - } - --bn->count; - break; +void hfs_bnode_unlink(struct hfs_bnode *node) +{ + struct hfs_btree *tree; + struct hfs_bnode *tmp; + u32 cnid; + + tree = node->tree; + if (node->prev) { + tmp = hfs_bnode_find(tree, node->prev); + if (IS_ERR(tmp)) + return; + tmp->next = node->next; + cnid = cpu_to_be32(tmp->next); + hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, next), 4); + hfs_bnode_put(tmp); + } else if (node->type == HFS_NODE_LEAF) + tree->leaf_head = node->next; + + if (node->next) { + tmp = hfs_bnode_find(tree, node->next); + if (IS_ERR(tmp)) + return; + tmp->prev = node->prev; + cnid = cpu_to_be32(tmp->prev); + hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, prev), 4); + hfs_bnode_put(tmp); + } else if (node->type == HFS_NODE_LEAF) + tree->leaf_tail = node->prev; + + // move down? + if (!node->prev && !node->next) { + printk("hfs_btree_del_level\n"); } - bnr->lock_type = lock_type; - return; + if (!node->parent) { + tree->root = 0; + tree->depth = 0; + } + set_bit(HFS_BNODE_DELETED, &node->flags); +} -bail: - hfs_warn("hfs_bnode_lock: invalid lock change: %d->%d.\n", - bnr->lock_type, lock_type); - return; +static inline int hfs_bnode_hash(u32 num) +{ + num = (num >> 16) + num; + num += num >> 8; + return num & (NODE_HASH_SIZE - 1); } -/* - * hfs_bnode_relse() - * - * Description: - * This function is called when a process is done using a bnode. If - * the proper conditions are met then we call hfs_bnode_delete() to remove - * it from the cache. If it is not deleted then we update its state - * to reflect one less process using it. - * Input Variable(s): - * struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to release. - * int lock_type: The type of lock held by the process releasing this node. - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'bn' is NULL or points to a "valid" (struct hfs_bnode). - * Postconditions: - * If 'bn' meets the appropriate conditions (see below) then it is - * kept in the cache and all fields are set to consistent values - * which reflect one less process using the node than upon entry. - * If 'bn' does not meet the conditions then it is deleted (see - * hfs_bnode_delete() for postconditions). - * In either case, if 'lock_type' is HFS_LOCK_WRITE - * then the corresponding buffer is dirtied. - */ -void hfs_bnode_relse(struct hfs_bnode_ref *bnr) +struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid) { - struct hfs_bnode *bn; + struct hfs_bnode *node; - if (!bnr || !(bn = bnr->bn)) { - return; + if (cnid >= tree->node_count) { + printk("HFS: request for non-existent node %d in B*Tree\n", cnid); + return NULL; } - /* We update the lock state of the node if it is still in use - or if it is "sticky" (such as the B-tree head and root). - Otherwise we just delete it. */ - if ((bn->count > 1) || (waitqueue_active(&bn->rqueue)) || (bn->sticky != HFS_NOT_STICKY)) { - hfs_bnode_lock(bnr, HFS_LOCK_NONE); - } else { - /* dirty buffer if we (might) have modified it */ - if (bnr->lock_type == HFS_LOCK_WRITE) { - hfs_bnode_commit(bn); + for (node = tree->node_hash[hfs_bnode_hash(cnid)]; + node; node = node->next_hash) { + if (node->this == cnid) { + return node; } - hfs_bnode_delete(bn); - bnr->lock_type = HFS_LOCK_NONE; } - bnr->bn = NULL; + return NULL; } -/* - * hfs_bnode_find() - * - * Description: - * This function is called to obtain a bnode. The cache is - * searched for the node. If it not found there it is added to - * the cache by hfs_bnode_read(). There are two special cases node=0 - * (the header node) and node='tree'->bthRoot (the root node), in - * which the nodes are obtained from fields of 'tree' without - * consulting or modifying the cache. - * Input Variable(s): - * struct hfs_tree *tree: pointer to the (struct hfs_btree) from - * which to get a node. - * int node: the node number to get from 'tree'. - * int lock_type: The kind of access (HFS_LOCK_READ, or - * HFS_LOCK_RESRV) to obtain to the node - * Output Variable(s): - * NONE - * Returns: - * (struct hfs_bnode_ref) Reference to the requested node. - * Preconditions: - * 'tree' points to a "valid" (struct hfs_btree). - * Postconditions: - * If 'node' refers to a valid node in 'tree' and 'lock_type' has - * one of the values listed above and no I/O errors occur then the - * value returned refers to a valid (struct hfs_bnode) corresponding - * to the requested node with the requested access type. The node - * is also added to the cache if not previously present and not the - * root or header. - * If the conditions given above are not met, the bnode in the - * returned reference is NULL. - */ -struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *tree, - hfs_u32 node, int lock_type) +static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) { - struct hfs_bnode *bn; - struct hfs_bnode *empty = NULL; - struct hfs_bnode_ref bnr; - - bnr.lock_type = HFS_LOCK_NONE; - bnr.bn = NULL; - -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - hfs_warn("hfs_bnode_find: %c %d:%d\n", - lock_type==HFS_LOCK_READ?'R': - (lock_type==HFS_LOCK_RESRV?'V':'W'), - (int)ntohl(tree->entry.cnid), node); -#endif - - /* check special cases */ - if (!node) { - bn = &tree->head; - goto return_it; - } else if (node == tree->bthRoot) { - bn = tree->root; - goto return_it; - } - -restart: - /* look for the node in the cache. */ - bn = bhash(tree, node); - while (bn && (bn->magic == HFS_BNODE_MAGIC)) { - if (bn->node == node) { - goto found_it; - } - bn = bn->next; + struct super_block *sb; + struct hfs_bnode *node, *node2; + struct address_space *mapping; + struct page *page; + int size, block, i, hash; + loff_t off; + + if (cnid >= tree->node_count) { + printk("HFS: request for non-existent node %d in B*Tree\n", cnid); + return NULL; } - if (!empty) { -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - ++bnode_count; -#endif - if (HFS_NEW(empty)) { - goto restart; - } - return bnr; + sb = tree->inode->i_sb; + size = sizeof(struct hfs_bnode) + tree->pages_per_bnode * + sizeof(struct page *); + node = kmalloc(size, GFP_KERNEL); + if (!node) + return NULL; + memset(node, 0, size); + node->tree = tree; + node->this = cnid; + set_bit(HFS_BNODE_NEW, &node->flags); + atomic_set(&node->refcnt, 1); + dprint(DBG_BNODE_REFS, "new_node(%d:%d): 1\n", + node->tree->cnid, node->this); + init_waitqueue_head(&node->lock_wq); + spin_lock(&tree->hash_lock); + node2 = hfs_bnode_findhash(tree, cnid); + if (!node2) { + hash = hfs_bnode_hash(cnid); + node->next_hash = tree->node_hash[hash]; + tree->node_hash[hash] = node; + tree->node_hash_cnt++; + } else { + spin_unlock(&tree->hash_lock); + kfree(node); + wait_event(node2->lock_wq, !test_bit(HFS_BNODE_NEW, &node2->flags)); + return node2; } - bn = empty; - hfs_bnode_read(bn, tree, node, HFS_NOT_STICKY); - goto return_it; - -found_it: - /* check validity */ - if (bn->magic != HFS_BNODE_MAGIC) { - /* If we find a corrupt bnode then we return - NULL. However, we don't try to remove it - from the cache or release its resources - since we have no idea what kind of trouble - we could get into that way. */ - hfs_warn("hfs_bnode_find: bnode cache is corrupt.\n"); - return bnr; - } - if (empty) { -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - --bnode_count; + spin_unlock(&tree->hash_lock); + + mapping = tree->inode->i_mapping; + off = (loff_t)cnid * tree->node_size; + block = off >> PAGE_CACHE_SHIFT; + node->page_offset = off & ~PAGE_CACHE_MASK; + for (i = 0; i < tree->pages_per_bnode; i++) { + page = read_cache_page(mapping, block++, (filler_t *)mapping->a_ops->readpage, NULL); + if (IS_ERR(page)) + goto fail; +#if !REF_PAGES + page_cache_release(page); #endif - HFS_DELETE(empty); + node->page[i] = page; + } + + return node; +fail: + set_bit(HFS_BNODE_ERROR, &node->flags); + return node; +} + +void hfs_bnode_unhash(struct hfs_bnode *node) +{ + struct hfs_bnode **p; + + dprint(DBG_BNODE_REFS, "remove_node(%d:%d): %d\n", + node->tree->cnid, node->this, atomic_read(&node->refcnt)); + for (p = &node->tree->node_hash[hfs_bnode_hash(node->this)]; + *p && *p != node; p = &(*p)->next_hash) + ; + if (!*p) + BUG(); + *p = node->next_hash; + node->tree->node_hash_cnt--; +} + +/* Load a particular node out of a tree */ +struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num) +{ + struct hfs_bnode *node; + struct hfs_bnode_desc *desc; + int i, rec_off, off, next_off; + int entry_size, key_size; + + spin_lock(&tree->hash_lock); + node = hfs_bnode_findhash(tree, num); + if (node) { + hfs_bnode_get(node); + spin_unlock(&tree->hash_lock); + wait_event(node->lock_wq, !test_bit(HFS_BNODE_NEW, &node->flags)); + return node; } - -return_it: - /* Wait our turn */ - bnr.bn = bn; - hfs_bnode_lock(&bnr, lock_type); - - /* Check for failure to read the node from disk */ - if (!hfs_buffer_ok(bn->buf)) { - hfs_bnode_relse(&bnr); + spin_unlock(&tree->hash_lock); + node = __hfs_bnode_create(tree, num); + if (!node) + return ERR_PTR(-ENOMEM); + if (!test_bit(HFS_BNODE_NEW, &node->flags)) + return node; + + desc = (struct hfs_bnode_desc *)(kmap(node->page[0]) + node->page_offset); + node->prev = be32_to_cpu(desc->prev); + node->next = be32_to_cpu(desc->next); + node->num_recs = be16_to_cpu(desc->num_recs); + node->type = desc->type; + node->height = desc->height; + kunmap(node->page[0]); + + switch (node->type) { + case HFS_NODE_HEADER: + case HFS_NODE_MAP: + if (node->height != 0) + goto node_error; + break; + case HFS_NODE_LEAF: + if (node->height != 1) + goto node_error; + break; + case HFS_NODE_INDEX: + if (node->height <= 1 || node->height > tree->depth) + goto node_error; + break; + default: + goto node_error; } -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - if (!bnr.bn) { - hfs_warn("hfs_bnode_find: failed\n"); - } else { - hfs_warn("hfs_bnode_find: use %d(%d) lvl %d [%d]\n", bn->count, - bn->buf->b_count, bn->ndNHeight, bnode_count); - hfs_warn("hfs_bnode_find: blnk %u flnk %u recs %u\n", - bn->ndBLink, bn->ndFLink, bn->ndNRecs); + rec_off = tree->node_size - 2; + off = hfs_bnode_read_u16(node, rec_off); + if (off != sizeof(struct hfs_bnode_desc)) + goto node_error; + for (i = 1; i <= node->num_recs; off = next_off, i++) { + rec_off -= 2; + next_off = hfs_bnode_read_u16(node, rec_off); + if (next_off <= off || + next_off > tree->node_size || + next_off & 1) + goto node_error; + entry_size = next_off - off; + if (node->type != HFS_NODE_INDEX && + node->type != HFS_NODE_LEAF) + continue; + key_size = hfs_bnode_read_u8(node, off) + 1; + if (key_size >= entry_size /*|| key_size & 1*/) + goto node_error; } -#endif + clear_bit(HFS_BNODE_NEW, &node->flags); + wake_up(&node->lock_wq); + return node; + +node_error: + set_bit(HFS_BNODE_ERROR, &node->flags); + clear_bit(HFS_BNODE_NEW, &node->flags); + wake_up(&node->lock_wq); + hfs_bnode_put(node); + return ERR_PTR(-EIO); +} - return bnr; +void hfs_bnode_free(struct hfs_bnode *node) +{ + //int i; + + //for (i = 0; i < node->tree->pages_per_bnode; i++) + // if (node->page[i]) + // page_cache_release(node->page[i]); + kfree(node); } -/* - * hfs_bnode_commit() - * - * Called to write a possibly dirty bnode back to disk. - */ -void hfs_bnode_commit(struct hfs_bnode *bn) +struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num) { - if (hfs_buffer_ok(bn->buf)) { - struct NodeDescriptor *nd; - nd = (struct NodeDescriptor *)hfs_buffer_data(bn->buf); - - hfs_put_hl(bn->ndFLink, nd->ndFLink); - hfs_put_hl(bn->ndBLink, nd->ndBLink); - nd->ndType = bn->ndType; - nd->ndNHeight = bn->ndNHeight; - hfs_put_hs(bn->ndNRecs, nd->ndNRecs); - hfs_buffer_dirty(bn->buf); - - /* increment write count */ - hfs_mdb_dirty(bn->tree->sys_mdb); + struct hfs_bnode *node; + struct page **pagep; + int i; + + spin_lock(&tree->hash_lock); + node = hfs_bnode_findhash(tree, num); + spin_unlock(&tree->hash_lock); + if (node) + BUG(); + node = __hfs_bnode_create(tree, num); + if (!node) + return ERR_PTR(-ENOMEM); + + pagep = node->page; + memset(kmap(*pagep) + node->page_offset, 0, + min((int)PAGE_CACHE_SIZE, (int)tree->node_size)); + set_page_dirty(*pagep); + kunmap(*pagep); + for (i = 1; i < tree->pages_per_bnode; i++) { + memset(kmap(*++pagep), 0, PAGE_CACHE_SIZE); + set_page_dirty(*pagep); + kunmap(*pagep); + } + clear_bit(HFS_BNODE_NEW, &node->flags); + wake_up(&node->lock_wq); + + return node; +} + +void hfs_bnode_get(struct hfs_bnode *node) +{ + if (node) { + atomic_inc(&node->refcnt); +#if REF_PAGES + { + int i; + for (i = 0; i < node->tree->pages_per_bnode; i++) + get_page(node->page[i]); + } +#endif + dprint(DBG_BNODE_REFS, "get_node(%d:%d): %d\n", + node->tree->cnid, node->this, atomic_read(&node->refcnt)); + } +} + +/* Dispose of resources used by a node */ +void hfs_bnode_put(struct hfs_bnode *node) +{ + if (node) { + struct hfs_btree *tree = node->tree; + int i; + + dprint(DBG_BNODE_REFS, "put_node(%d:%d): %d\n", + node->tree->cnid, node->this, atomic_read(&node->refcnt)); + if (!atomic_read(&node->refcnt)) + BUG(); + if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock)) { +#if REF_PAGES + for (i = 0; i < tree->pages_per_bnode; i++) + put_page(node->page[i]); +#endif + return; + } + for (i = 0; i < tree->pages_per_bnode; i++) { + mark_page_accessed(node->page[i]); +#if REF_PAGES + put_page(node->page[i]); +#endif + } + + if (test_bit(HFS_BNODE_DELETED, &node->flags)) { + hfs_bnode_unhash(node); + spin_unlock(&tree->hash_lock); + hfs_bmap_free(node); + hfs_bnode_free(node); + return; + } + spin_unlock(&tree->hash_lock); } } diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c index 4db76fc4f62c..5e54671dcff9 100644 --- a/fs/hfs/brec.c +++ b/fs/hfs/brec.c @@ -1,239 +1,492 @@ /* - * linux/fs/hfs/brec.c + * linux/fs/hfs/brec.c * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> * - * This file contains the code to access records in a btree. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. + * Handle individual btree records */ -#include "hfs_btree.h" - -/*================ File-local functions ================*/ +#include "btree.h" -/* - * first() - * - * returns HFS_BPATH_FIRST if elem->record == 1, 0 otherwise - */ -static inline int first(const struct hfs_belem *elem) +/* Get the length and offset of the given record in the given node */ +u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off) { - return (elem->record == 1) ? HFS_BPATH_FIRST : 0; + u16 retval[2]; + u16 dataoff; + + dataoff = node->tree->node_size - (rec + 2) * 2; + hfs_bnode_read(node, retval, dataoff, 4); + *off = be16_to_cpu(retval[1]); + return be16_to_cpu(retval[0]) - *off; } -/* - * overflow() - * - * return HFS_BPATH_OVERFLOW if the node has no room for an - * additional pointer record, 0 otherwise. - */ -static inline int overflow(const struct hfs_btree *tree, - const struct hfs_bnode *bnode) +/* Get the length of the key from a keyed record */ +u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) { - /* there is some algebra involved in getting this form */ - return ((HFS_SECTOR_SIZE - sizeof(hfs_u32)) < - (bnode_end(bnode) + (2+bnode->ndNRecs)*sizeof(hfs_u16) + - ROUND(tree->bthKeyLen+1))) ? HFS_BPATH_OVERFLOW : 0; + u16 retval, recoff; + + if (node->type != HFS_NODE_INDEX && node->type != HFS_NODE_LEAF) + return 0; + + if ((node->type == HFS_NODE_INDEX) && + !(node->tree->attributes & HFS_TREE_VARIDXKEYS)) { + if (node->tree->attributes & HFS_TREE_BIGKEYS) + retval = node->tree->max_key_len + 2; + else + retval = node->tree->max_key_len + 1; + } else { + recoff = hfs_bnode_read_u16(node, node->tree->node_size - (rec + 1) * 2); + if (!recoff) + return 0; + if (node->tree->attributes & HFS_TREE_BIGKEYS) + retval = hfs_bnode_read_u16(node, recoff) + 2; + else + retval = (hfs_bnode_read_u8(node, recoff) | 1) + 1; + } + return retval; } -/* - * underflow() - * - * return HFS_BPATH_UNDERFLOW if the node will be less that 1/2 full - * upon removal of a pointer record, 0 otherwise. - */ -static inline int underflow(const struct hfs_btree *tree, - const struct hfs_bnode *bnode) +int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) { - return ((bnode->ndNRecs * sizeof(hfs_u16) + - bnode_offset(bnode, bnode->ndNRecs)) < - (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))/2) ? - HFS_BPATH_UNDERFLOW : 0; + struct hfs_btree *tree; + struct hfs_bnode *node, *new_node; + int size, key_len, rec; + int data_off, end_off; + int idx_rec_off, data_rec_off, end_rec_off; + u32 cnid; + + tree = fd->tree; + if (!fd->bnode) { + if (!tree->root) + hfs_btree_inc_height(tree); + fd->bnode = hfs_bnode_find(tree, tree->leaf_head); + if (IS_ERR(fd->bnode)) + return PTR_ERR(fd->bnode); + fd->record = -1; + } + new_node = NULL; + key_len = (fd->search_key->key_len | 1) + 1; +again: + /* new record idx and complete record size */ + rec = fd->record + 1; + size = key_len + entry_len; + + node = fd->bnode; + hfs_bnode_dump(node); + /* get last offset */ + end_rec_off = tree->node_size - (node->num_recs + 1) * 2; + end_off = hfs_bnode_read_u16(node, end_rec_off); + end_rec_off -= 2; + dprint(DBG_BNODE_MOD, "insert_rec: %d, %d, %d, %d\n", rec, size, end_off, end_rec_off); + if (size > end_rec_off - end_off) { + if (new_node) + panic("not enough room!\n"); + new_node = hfs_bnode_split(fd); + if (IS_ERR(new_node)) + return PTR_ERR(new_node); + goto again; + } + if (node->type == HFS_NODE_LEAF) { + tree->leaf_count++; + mark_inode_dirty(tree->inode); + } + node->num_recs++; + /* write new last offset */ + hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs); + hfs_bnode_write_u16(node, end_rec_off, end_off + size); + data_off = end_off; + data_rec_off = end_rec_off + 2; + idx_rec_off = tree->node_size - (rec + 1) * 2; + if (idx_rec_off == data_rec_off) + goto skip; + /* move all following entries */ + do { + data_off = hfs_bnode_read_u16(node, data_rec_off + 2); + hfs_bnode_write_u16(node, data_rec_off, data_off + size); + data_rec_off += 2; + } while (data_rec_off < idx_rec_off); + + /* move data away */ + hfs_bnode_move(node, data_off + size, data_off, + end_off - data_off); + +skip: + hfs_bnode_write(node, fd->search_key, data_off, key_len); + hfs_bnode_write(node, entry, data_off + key_len, entry_len); + hfs_bnode_dump(node); + + if (new_node) { + /* update parent key if we inserted a key + * at the start of the first node + */ + if (!rec && new_node != node) + hfs_brec_update_parent(fd); + + hfs_bnode_put(fd->bnode); + if (!new_node->parent) { + hfs_btree_inc_height(tree); + new_node->parent = tree->root; + } + fd->bnode = hfs_bnode_find(tree, new_node->parent); + + /* create index data entry */ + cnid = cpu_to_be32(new_node->this); + entry = &cnid; + entry_len = sizeof(cnid); + + /* get index key */ + hfs_bnode_read_key(new_node, fd->search_key, 14); + __hfs_brec_find(fd->bnode, fd); + + hfs_bnode_put(new_node); + new_node = NULL; + + if (tree->attributes & HFS_TREE_VARIDXKEYS) + key_len = fd->search_key->key_len + 1; + else { + fd->search_key->key_len = tree->max_key_len; + key_len = tree->max_key_len + 1; + } + goto again; + } + + if (!rec) + hfs_brec_update_parent(fd); + + return 0; } -/*================ Global functions ================*/ +int hfs_brec_remove(struct hfs_find_data *fd) +{ + struct hfs_btree *tree; + struct hfs_bnode *node, *parent; + int end_off, rec_off, data_off, size; -/* - * hfs_brec_next() - * - * Description: - * Obtain access to a child of an internal node in a B-tree. - * Input Variable(s): - * struct hfs_brec *brec: pointer to the (struct hfs_brec) to - * add an element to. - * Output Variable(s): - * NONE - * Returns: - * struct hfs_belem *: pointer to the new path element or NULL - * Preconditions: - * 'brec' points to a "valid" (struct hfs_brec), the last element of - * which corresponds to a record in a bnode of type ndIndxNode and the - * 'record' field indicates the index record for the desired child. - * Postconditions: - * If the call to hfs_bnode_find() fails then 'brec' is released - * and a NULL is returned. - * Otherwise: - * Any ancestors in 'brec' that are not needed (as determined by the - * 'keep_flags' field of 'brec) are released from 'brec'. - * A new element is added to 'brec' corresponding to the desired - * child. - * The child is obtained with the same 'lock_type' field as its - * parent. - * The 'record' field is initialized to the last record. - * A pointer to the new path element is returned. - */ -struct hfs_belem *hfs_brec_next(struct hfs_brec *brec) + tree = fd->tree; + node = fd->bnode; +again: + rec_off = tree->node_size - (fd->record + 2) * 2; + end_off = tree->node_size - (node->num_recs + 1) * 2; + + if (node->type == HFS_NODE_LEAF) { + tree->leaf_count--; + mark_inode_dirty(tree->inode); + } + hfs_bnode_dump(node); + dprint(DBG_BNODE_MOD, "remove_rec: %d, %d\n", fd->record, fd->keylength + fd->entrylength); + if (!--node->num_recs) { + hfs_bnode_unlink(node); + if (!node->parent) + return 0; + parent = hfs_bnode_find(tree, node->parent); + if (IS_ERR(parent)) + return PTR_ERR(parent); + hfs_bnode_put(node); + node = fd->bnode = parent; + + __hfs_brec_find(node, fd); + goto again; + } + hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs); + + if (rec_off == end_off) + goto skip; + size = fd->keylength + fd->entrylength; + + do { + data_off = hfs_bnode_read_u16(node, rec_off); + hfs_bnode_write_u16(node, rec_off + 2, data_off - size); + rec_off -= 2; + } while (rec_off >= end_off); + + /* fill hole */ + hfs_bnode_move(node, fd->keyoffset, fd->keyoffset + size, + data_off - fd->keyoffset - size); +skip: + hfs_bnode_dump(node); + if (!fd->record) + hfs_brec_update_parent(fd); + return 0; +} + +struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd) { - struct hfs_belem *elem = brec->bottom; - hfs_u32 node; - int lock_type; + struct hfs_btree *tree; + struct hfs_bnode *node, *new_node; + struct hfs_bnode_desc node_desc; + int num_recs, new_rec_off, new_off, old_rec_off; + int data_start, data_end, size; - /* release unneeded ancestors */ - elem->flags = first(elem) | - overflow(brec->tree, elem->bnr.bn) | - underflow(brec->tree, elem->bnr.bn); - if (!(brec->keep_flags & elem->flags)) { - hfs_brec_relse(brec, brec->bottom-1); - } else if ((brec->bottom-2 >= brec->top) && - !(elem->flags & (elem-1)->flags)) { - hfs_brec_relse(brec, brec->bottom-2); + tree = fd->tree; + node = fd->bnode; + new_node = hfs_bmap_alloc(tree); + if (IS_ERR(new_node)) + return new_node; + hfs_bnode_get(node); + dprint(DBG_BNODE_MOD, "split_nodes: %d - %d - %d\n", + node->this, new_node->this, node->next); + new_node->next = node->next; + new_node->prev = node->this; + new_node->parent = node->parent; + new_node->type = node->type; + new_node->height = node->height; + + size = tree->node_size / 2 - node->num_recs * 2 - 14; + old_rec_off = tree->node_size - 4; + num_recs = 1; + for (;;) { + data_start = hfs_bnode_read_u16(node, old_rec_off); + if (data_start > size) + break; + old_rec_off -= 2; + if (++num_recs < node->num_recs) + continue; + /* panic? */ + hfs_bnode_put(node); + hfs_bnode_put(new_node); + return ERR_PTR(-ENOSPC); } - node = hfs_get_hl(belem_record(elem)); - lock_type = elem->bnr.lock_type; + if (fd->record + 1 < num_recs) { + /* new record is in the lower half, + * so leave some more space there + */ + old_rec_off += 2; + num_recs--; + data_start = hfs_bnode_read_u16(node, old_rec_off); + } else { + hfs_bnode_put(node); + hfs_bnode_get(new_node); + fd->bnode = new_node; + fd->record -= num_recs; + fd->keyoffset -= data_start - 14; + fd->entryoffset -= data_start - 14; + } + new_node->num_recs = node->num_recs - num_recs; + node->num_recs = num_recs; - if (!node || hfs_bnode_in_brec(node, brec)) { - hfs_warn("hfs_bfind: corrupt btree\n"); - hfs_brec_relse(brec, NULL); - return NULL; + new_rec_off = tree->node_size - 2; + new_off = 14; + size = data_start - new_off; + num_recs = new_node->num_recs; + data_end = data_start; + while (num_recs) { + hfs_bnode_write_u16(new_node, new_rec_off, new_off); + old_rec_off -= 2; + new_rec_off -= 2; + data_end = hfs_bnode_read_u16(node, old_rec_off); + new_off = data_end - size; + num_recs--; } + hfs_bnode_write_u16(new_node, new_rec_off, new_off); + hfs_bnode_copy(new_node, 14, node, data_start, data_end - data_start); - ++elem; - ++brec->bottom; + /* update new bnode header */ + node_desc.next = cpu_to_be32(new_node->next); + node_desc.prev = cpu_to_be32(new_node->prev); + node_desc.type = new_node->type; + node_desc.height = new_node->height; + node_desc.num_recs = cpu_to_be16(new_node->num_recs); + node_desc.reserved = 0; + hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc)); - elem->bnr = hfs_bnode_find(brec->tree, node, lock_type); - if (!elem->bnr.bn) { - hfs_brec_relse(brec, NULL); - return NULL; + /* update previous bnode header */ + node->next = new_node->this; + hfs_bnode_read(node, &node_desc, 0, sizeof(node_desc)); + node_desc.next = cpu_to_be32(node->next); + node_desc.num_recs = cpu_to_be16(node->num_recs); + hfs_bnode_write(node, &node_desc, 0, sizeof(node_desc)); + + /* update next bnode header */ + if (new_node->next) { + struct hfs_bnode *next_node = hfs_bnode_find(tree, new_node->next); + next_node->prev = new_node->this; + hfs_bnode_read(next_node, &node_desc, 0, sizeof(node_desc)); + node_desc.prev = cpu_to_be32(next_node->prev); + hfs_bnode_write(next_node, &node_desc, 0, sizeof(node_desc)); + hfs_bnode_put(next_node); + } else if (node->this == tree->leaf_tail) { + /* if there is no next node, this might be the new tail */ + tree->leaf_tail = new_node->this; + mark_inode_dirty(tree->inode); } - elem->record = elem->bnr.bn->ndNRecs; - return elem; + hfs_bnode_dump(node); + hfs_bnode_dump(new_node); + hfs_bnode_put(node); + + return new_node; } -/* - * hfs_brec_lock() - * - * Description: - * This function obtains HFS_LOCK_WRITE access to the bnode - * containing this hfs_brec. All descendents in the path from this - * record to the leaf are given HFS_LOCK_WRITE access and all - * ancestors in the path from the root to here are released. - * Input Variable(s): - * struct hfs_brec *brec: pointer to the brec to obtain - * HFS_LOCK_WRITE access to some of the nodes of. - * struct hfs_belem *elem: the first node to lock or NULL for all - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'brec' points to a "valid" (struct hfs_brec) - * Postconditions: - * All nodes between the indicated node and the beginning of the path - * are released. hfs_bnode_lock() is called in turn on each node - * from the indicated node to the leaf node of the path, with a - * lock_type argument of HFS_LOCK_WRITE. If one of those calls - * results in deadlock, then this function will never return. - */ -void hfs_brec_lock(struct hfs_brec *brec, struct hfs_belem *elem) +int hfs_brec_update_parent(struct hfs_find_data *fd) { - if (!elem) { - elem = brec->top; - } else if (elem > brec->top) { - hfs_brec_relse(brec, elem-1); + struct hfs_btree *tree; + struct hfs_bnode *node, *new_node, *parent; + int newkeylen, diff; + int rec, rec_off, end_rec_off; + int start_off, end_off; + + tree = fd->tree; + node = fd->bnode; + new_node = NULL; + if (!node->parent) + return 0; + +again: + parent = hfs_bnode_find(tree, node->parent); + if (IS_ERR(parent)) + return PTR_ERR(parent); + __hfs_brec_find(parent, fd); + hfs_bnode_dump(parent); + rec = fd->record; + + /* size difference between old and new key */ + if (tree->attributes & HFS_TREE_VARIDXKEYS) + newkeylen = (hfs_bnode_read_u8(node, 14) | 1) + 1; + else + fd->keylength = newkeylen = tree->max_key_len + 1; + dprint(DBG_BNODE_MOD, "update_rec: %d, %d, %d\n", rec, fd->keylength, newkeylen); + + rec_off = tree->node_size - (rec + 2) * 2; + end_rec_off = tree->node_size - (parent->num_recs + 1) * 2; + diff = newkeylen - fd->keylength; + if (!diff) + goto skip; + if (diff > 0) { + end_off = hfs_bnode_read_u16(parent, end_rec_off); + if (end_rec_off - end_off < diff) { + + printk("splitting index node...\n"); + fd->bnode = parent; + new_node = hfs_bnode_split(fd); + if (IS_ERR(new_node)) + return PTR_ERR(new_node); + parent = fd->bnode; + rec = fd->record; + rec_off = tree->node_size - (rec + 2) * 2; + end_rec_off = tree->node_size - (parent->num_recs + 1) * 2; + } } - while (elem <= brec->bottom) { - hfs_bnode_lock(&elem->bnr, HFS_LOCK_WRITE); - ++elem; + end_off = start_off = hfs_bnode_read_u16(parent, rec_off); + hfs_bnode_write_u16(parent, rec_off, start_off + diff); + start_off -= 4; /* move previous cnid too */ + + while (rec_off > end_rec_off) { + rec_off -= 2; + end_off = hfs_bnode_read_u16(parent, rec_off); + hfs_bnode_write_u16(parent, rec_off, end_off + diff); } + hfs_bnode_move(parent, start_off + diff, start_off, + end_off - start_off); +skip: + hfs_bnode_copy(parent, fd->keyoffset, node, 14, newkeylen); + if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) + hfs_bnode_write_u8(parent, fd->keyoffset, newkeylen - 1); + hfs_bnode_dump(parent); + + hfs_bnode_put(node); + node = parent; + + if (new_node) { + u32 cnid; + + fd->bnode = hfs_bnode_find(tree, new_node->parent); + /* create index key and entry */ + hfs_bnode_read_key(new_node, fd->search_key, 14); + cnid = cpu_to_be32(new_node->this); + + __hfs_brec_find(fd->bnode, fd); + hfs_brec_insert(fd, &cnid, sizeof(cnid)); + hfs_bnode_put(fd->bnode); + hfs_bnode_put(new_node); + + if (!rec) { + if (new_node == node) + goto out; + /* restore search_key */ + hfs_bnode_read_key(node, fd->search_key, 14); + } + } + + if (!rec && node->parent) + goto again; +out: + fd->bnode = node; + return 0; } -/* - * hfs_brec_init() - * - * Description: - * Obtain access to the root node of a B-tree. - * Note that this first must obtain access to the header node. - * Input Variable(s): - * struct hfs_brec *brec: pointer to the (struct hfs_brec) to - * initialize - * struct hfs_btree *btree: pointer to the (struct hfs_btree) - * int lock_type: the type of access to get to the nodes. - * Output Variable(s): - * NONE - * Returns: - * struct hfs_belem *: pointer to the root path element or NULL - * Preconditions: - * 'brec' points to a (struct hfs_brec). - * 'tree' points to a valid (struct hfs_btree). - * Postconditions: - * If the two calls to brec_bnode_find() succeed then the return value - * points to a (struct hfs_belem) which corresponds to the root node - * of 'brec->tree'. - * Both the root and header nodes are obtained with the type of lock - * given by (flags & HFS_LOCK_MASK). - * The fields 'record' field of the root is set to its last record. - * If the header node is not needed to complete the appropriate - * operation (as determined by the 'keep_flags' field of 'brec') then - * it is released before this function returns. - * If either call to brec_bnode_find() fails, NULL is returned and the - * (struct hfs_brec) pointed to by 'brec' is invalid. - */ -struct hfs_belem *hfs_brec_init(struct hfs_brec *brec, struct hfs_btree *tree, - int flags) +int hfs_btree_inc_height(struct hfs_btree *tree) { - struct hfs_belem *head = &brec->elem[0]; - struct hfs_belem *root = &brec->elem[1]; - int lock_type = flags & HFS_LOCK_MASK; + struct hfs_bnode *node, *new_node; + struct hfs_bnode_desc node_desc; + int key_size, rec; + u32 cnid; - brec->tree = tree; - - head->bnr = hfs_bnode_find(tree, 0, lock_type); - if (!head->bnr.bn) { - return NULL; + node = NULL; + if (tree->root) { + node = hfs_bnode_find(tree, tree->root); + if (IS_ERR(node)) + return PTR_ERR(node); + } + new_node = hfs_bmap_alloc(tree); + if (IS_ERR(new_node)) { + hfs_bnode_put(node); + return PTR_ERR(new_node); } - root->bnr = hfs_bnode_find(tree, tree->bthRoot, lock_type); - if (!root->bnr.bn) { - hfs_bnode_relse(&head->bnr); - return NULL; + tree->root = new_node->this; + if (!tree->depth) { + tree->leaf_head = tree->leaf_tail = new_node->this; + new_node->type = HFS_NODE_LEAF; + new_node->num_recs = 0; + } else { + new_node->type = HFS_NODE_INDEX; + new_node->num_recs = 1; } + new_node->parent = 0; + new_node->next = 0; + new_node->prev = 0; + new_node->height = ++tree->depth; - root->record = root->bnr.bn->ndNRecs; - - brec->top = head; - brec->bottom = root; - - brec->keep_flags = flags & HFS_BPATH_MASK; + node_desc.next = cpu_to_be32(new_node->next); + node_desc.prev = cpu_to_be32(new_node->prev); + node_desc.type = new_node->type; + node_desc.height = new_node->height; + node_desc.num_recs = cpu_to_be16(new_node->num_recs); + node_desc.reserved = 0; + hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc)); - /* HFS_BPATH_FIRST not applicable for root */ - /* and HFS_BPATH_UNDERFLOW is different */ - root->flags = overflow(tree, root->bnr.bn); - if (root->record < 3) { - root->flags |= HFS_BPATH_UNDERFLOW; - } + rec = tree->node_size - 2; + hfs_bnode_write_u16(new_node, rec, 14); + + if (node) { + /* insert old root idx into new root */ + node->parent = tree->root; + if (node->type == HFS_NODE_LEAF || + tree->attributes & HFS_TREE_VARIDXKEYS) + key_size = hfs_bnode_read_u8(node, 14) + 1; + else + key_size = tree->max_key_len + 1; + hfs_bnode_copy(new_node, 14, node, 14, key_size); + + if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) { + key_size = tree->max_key_len + 1; + hfs_bnode_write_u8(new_node, 14, tree->max_key_len); + } + key_size = (key_size + 1) & -2; + cnid = cpu_to_be32(node->this); + hfs_bnode_write(new_node, &cnid, 14 + key_size, 4); + + rec -= 2; + hfs_bnode_write_u16(new_node, rec, 14 + key_size + 4); - if (!(root->flags & brec->keep_flags)) { - hfs_brec_relse(brec, head); + hfs_bnode_put(node); } + hfs_bnode_put(new_node); + mark_inode_dirty(tree->inode); - return root; + return 0; } diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c index f14bb508e134..3e620efdd476 100644 --- a/fs/hfs/btree.c +++ b/fs/hfs/btree.c @@ -1,323 +1,328 @@ /* - * linux/fs/hfs/btree.c + * linux/fs/hfs/btree.c * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> * - * This file contains the code to manipulate the B-tree structure. - * The catalog and extents files are both B-trees. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - * - * The code in this file initializes some structures which contain - * pointers by calling memset(&foo, 0, sizeof(foo)). - * This produces the desired behavior only due to the non-ANSI - * assumption that the machine representation of NULL is all zeros. + * Handle opening/closing btree */ -#include "hfs_btree.h" +#include <linux/pagemap.h> -/*================ File-local functions ================*/ +#include "btree.h" -/* - * hfs_bnode_ditch() - * - * Description: - * This function deletes an entire linked list of bnodes, so it - * does not need to keep the linked list consistent as - * hfs_bnode_delete() does. - * Called by hfs_btree_init() for error cleanup and by hfs_btree_free(). - * Input Variable(s): - * struct hfs_bnode *bn: pointer to the first (struct hfs_bnode) in - * the linked list to be deleted. - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'bn' is NULL or points to a "valid" (struct hfs_bnode) with a 'prev' - * field of NULL. - * Postconditions: - * 'bn' and all (struct hfs_bnode)s in the chain of 'next' pointers - * are deleted, freeing the associated memory and hfs_buffer_put()ing - * the associated buffer. - */ -static void hfs_bnode_ditch(struct hfs_bnode *bn) { - struct hfs_bnode *tmp; -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - extern int bnode_count; -#endif - - while (bn != NULL) { - tmp = bn->next; -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - hfs_warn("deleting node %d from tree %d with count %d\n", - bn->node, (int)ntohl(bn->tree->entry.cnid), bn->count); - --bnode_count; -#endif - hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */ - - /* free all but the header */ - if (bn->node) { - HFS_DELETE(bn); - } - bn = tmp; +/* Get a reference to a B*Tree and do some initial checks */ +struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp keycmp) +{ + struct hfs_btree *tree; + struct hfs_btree_header_rec *head; + struct address_space *mapping; + struct page *page; + unsigned int shift, size; + + tree = kmalloc(sizeof(*tree), GFP_KERNEL); + if (!tree) + return NULL; + memset(tree, 0, sizeof(*tree)); + + init_MUTEX(&tree->tree_lock); + spin_lock_init(&tree->hash_lock); + /* Set the correct compare function */ + tree->sb = sb; + tree->cnid = id; + tree->keycmp = keycmp; + + tree->inode = iget_locked(sb, id); + if (!tree->inode) + goto free_tree; + if (!(tree->inode->i_state & I_NEW)) + BUG(); + { + struct hfs_mdb *mdb = HFS_SB(sb)->mdb; + HFS_I(tree->inode)->flags = 0; + init_MUTEX(&HFS_I(tree->inode)->extents_lock); + switch (id) { + case HFS_EXT_CNID: + hfs_inode_read_fork(tree->inode, mdb->drXTExtRec, mdb->drXTFlSize, + mdb->drXTFlSize, be32_to_cpu(mdb->drXTClpSiz)); + tree->inode->i_mapping->a_ops = &hfs_btree_aops; + break; + case HFS_CAT_CNID: + hfs_inode_read_fork(tree->inode, mdb->drCTExtRec, mdb->drCTFlSize, + mdb->drCTFlSize, be32_to_cpu(mdb->drCTClpSiz)); + tree->inode->i_mapping->a_ops = &hfs_btree_aops; + break; + default: + BUG(); } -} + } + unlock_new_inode(tree->inode); -/*================ Global functions ================*/ + mapping = tree->inode->i_mapping; + page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, NULL); + if (IS_ERR(page)) + goto free_tree; -/* - * hfs_btree_free() - * - * Description: - * This function frees a (struct hfs_btree) obtained from hfs_btree_init(). - * Called by hfs_put_super(). - * Input Variable(s): - * struct hfs_btree *bt: pointer to the (struct hfs_btree) to free - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'bt' is NULL or points to a "valid" (struct hfs_btree) - * Postconditions: - * If 'bt' points to a "valid" (struct hfs_btree) then all (struct - * hfs_bnode)s associated with 'bt' are freed by calling - * hfs_bnode_ditch() and the memory associated with the (struct - * hfs_btree) is freed. - * If 'bt' is NULL or not "valid" an error is printed and nothing - * is changed. - */ -void hfs_btree_free(struct hfs_btree *bt) + /* Load the header */ + head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc)); + tree->root = be32_to_cpu(head->root); + tree->leaf_count = be32_to_cpu(head->leaf_count); + tree->leaf_head = be32_to_cpu(head->leaf_head); + tree->leaf_tail = be32_to_cpu(head->leaf_tail); + tree->node_count = be32_to_cpu(head->node_count); + tree->free_nodes = be32_to_cpu(head->free_nodes); + tree->attributes = be32_to_cpu(head->attributes); + tree->node_size = be16_to_cpu(head->node_size); + tree->max_key_len = be16_to_cpu(head->max_key_len); + tree->depth = be16_to_cpu(head->depth); + + size = tree->node_size; + if (!size || size & (size - 1)) + goto fail_page; + if (!tree->node_count) + goto fail_page; + for (shift = 0; size >>= 1; shift += 1) + ; + tree->node_size_shift = shift; + + tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + + kunmap(page); + page_cache_release(page); + return tree; + + fail_page: + tree->inode->i_mapping->a_ops = &hfs_aops; + page_cache_release(page); + free_tree: + iput(tree->inode); + kfree(tree); + return NULL; +} + +/* Release resources used by a btree */ +void hfs_btree_close(struct hfs_btree *tree) { - int lcv; + struct hfs_bnode *node; + int i; - if (bt && (bt->magic == HFS_BTREE_MAGIC)) { - hfs_extent_free(&bt->entry.u.file.data_fork); + if (!tree) + return; - for (lcv=0; lcv<HFS_CACHELEN; ++lcv) { -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - hfs_warn("deleting nodes from bucket %d:\n", lcv); -#endif - hfs_bnode_ditch(bt->cache[lcv]); + for (i = 0; i < NODE_HASH_SIZE; i++) { + while ((node = tree->node_hash[i])) { + tree->node_hash[i] = node->next_hash; + if (atomic_read(&node->refcnt)) + printk("HFS: node %d:%d still has %d user(s)!\n", + node->tree->cnid, node->this, atomic_read(&node->refcnt)); + hfs_bnode_free(node); + tree->node_hash_cnt--; } + } + iput(tree->inode); + kfree(tree); +} -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - hfs_warn("deleting header and bitmap nodes\n"); -#endif - hfs_bnode_ditch(&bt->head); +void hfs_btree_write(struct hfs_btree *tree) +{ + struct hfs_btree_header_rec *head; + struct hfs_bnode *node; + struct page *page; -#if defined(DEBUG_BNODES) || defined(DEBUG_ALL) - hfs_warn("deleting root node\n"); -#endif - hfs_bnode_ditch(bt->root); + node = hfs_bnode_find(tree, 0); + if (IS_ERR(node)) + /* panic? */ + return; + /* Load the header */ + page = node->page[0]; + head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc)); - HFS_DELETE(bt); - } else if (bt) { - hfs_warn("hfs_btree_free: corrupted hfs_btree.\n"); - } + head->root = cpu_to_be32(tree->root); + head->leaf_count = cpu_to_be32(tree->leaf_count); + head->leaf_head = cpu_to_be32(tree->leaf_head); + head->leaf_tail = cpu_to_be32(tree->leaf_tail); + head->node_count = cpu_to_be32(tree->node_count); + head->free_nodes = cpu_to_be32(tree->free_nodes); + head->attributes = cpu_to_be32(tree->attributes); + head->depth = cpu_to_be16(tree->depth); + + kunmap(page); + set_page_dirty(page); + hfs_bnode_put(node); } -/* - * hfs_btree_init() - * - * Description: - * Given some vital information from the MDB (HFS superblock), - * initializes the fields of a (struct hfs_btree). - * Input Variable(s): - * struct hfs_mdb *mdb: pointer to the MDB - * ino_t cnid: the CNID (HFS_CAT_CNID or HFS_EXT_CNID) of the B-tree - * hfs_u32 tsize: the size, in bytes, of the B-tree - * hfs_u32 csize: the size, in bytes, of the clump size for the B-tree - * Output Variable(s): - * NONE - * Returns: - * (struct hfs_btree *): pointer to the initialized hfs_btree on success, - * or NULL on failure - * Preconditions: - * 'mdb' points to a "valid" (struct hfs_mdb) - * Postconditions: - * Assuming the inputs are what they claim to be, no errors occur - * reading from disk, and no inconsistencies are noticed in the data - * read from disk, the return value is a pointer to a "valid" - * (struct hfs_btree). If there are errors reading from disk or - * inconsistencies are noticed in the data read from disk, then and - * all resources that were allocated are released and NULL is - * returned. If the inputs are not what they claim to be or if they - * are unnoticed inconsistencies in the data read from disk then the - * returned hfs_btree is probably going to lead to errors when it is - * used in a non-trivial way. - */ -struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid, - hfs_byte_t ext[12], - hfs_u32 tsize, hfs_u32 csize) +static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx) { - struct hfs_btree * bt; - struct BTHdrRec * th; - struct hfs_bnode * tmp; - unsigned int next; -#if defined(DEBUG_HEADER) || defined(DEBUG_ALL) - unsigned char *p, *q; -#endif - - if (!mdb || !ext || !HFS_NEW(bt)) { - goto bail3; - } + struct hfs_btree *tree = prev->tree; + struct hfs_bnode *node; + struct hfs_bnode_desc desc; + u32 cnid; - bt->magic = HFS_BTREE_MAGIC; - bt->sys_mdb = mdb->sys_mdb; - bt->reserved = 0; - sema_init(&bt->sem, 1); - bt->dirt = 0; - memset(bt->cache, 0, sizeof(bt->cache)); - -#if 0 /* this is a fake entry. so we don't need to initialize it. */ - memset(&bt->entry, 0, sizeof(bt->entry)); - hfs_init_waitqueue(&bt->entry.wait); - INIT_LIST_HEAD(&bt->entry.hash); - INIT_LIST_HEAD(&bt->entry.list); -#endif - - bt->entry.mdb = mdb; - bt->entry.cnid = cnid; - bt->entry.type = HFS_CDR_FIL; - bt->entry.u.file.magic = HFS_FILE_MAGIC; - bt->entry.u.file.clumpablks = (csize / mdb->alloc_blksz) - >> HFS_SECTOR_SIZE_BITS; - bt->entry.u.file.data_fork.entry = &bt->entry; - bt->entry.u.file.data_fork.lsize = tsize; - bt->entry.u.file.data_fork.psize = tsize >> HFS_SECTOR_SIZE_BITS; - bt->entry.u.file.data_fork.fork = HFS_FK_DATA; - hfs_extent_in(&bt->entry.u.file.data_fork, ext); - - hfs_bnode_read(&bt->head, bt, 0, HFS_STICKY); - if (!hfs_buffer_ok(bt->head.buf)) { - goto bail2; - } - th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) + - sizeof(struct NodeDescriptor)); - - /* read in the bitmap nodes (if any) */ - tmp = &bt->head; - while ((next = tmp->ndFLink)) { - if (!HFS_NEW(tmp->next)) { - goto bail2; - } - hfs_bnode_read(tmp->next, bt, next, HFS_STICKY); - if (!hfs_buffer_ok(tmp->next->buf)) { - goto bail2; - } - tmp->next->prev = tmp; - tmp = tmp->next; - } + node = hfs_bnode_create(tree, idx); + if (IS_ERR(node)) + return node; - if (hfs_get_ns(th->bthNodeSize) != htons(HFS_SECTOR_SIZE)) { - hfs_warn("hfs_btree_init: bthNodeSize!=512 not supported\n"); - goto bail2; - } + if (!tree->free_nodes) + panic("FIXME!!!"); + tree->free_nodes--; + prev->next = idx; + cnid = cpu_to_be32(idx); + hfs_bnode_write(prev, &cnid, offsetof(struct hfs_bnode_desc, next), 4); - if (cnid == htonl(HFS_CAT_CNID)) { - bt->compare = (hfs_cmpfn)hfs_cat_compare; - } else if (cnid == htonl(HFS_EXT_CNID)) { - bt->compare = (hfs_cmpfn)hfs_ext_compare; - } else { - goto bail2; - } - bt->bthDepth = hfs_get_hs(th->bthDepth); - bt->bthRoot = hfs_get_hl(th->bthRoot); - bt->bthNRecs = hfs_get_hl(th->bthNRecs); - bt->bthFNode = hfs_get_hl(th->bthFNode); - bt->bthLNode = hfs_get_hl(th->bthLNode); - bt->bthNNodes = hfs_get_hl(th->bthNNodes); - bt->bthFree = hfs_get_hl(th->bthFree); - bt->bthKeyLen = hfs_get_hs(th->bthKeyLen); - -#if defined(DEBUG_HEADER) || defined(DEBUG_ALL) - hfs_warn("bthDepth %d\n", bt->bthDepth); - hfs_warn("bthRoot %d\n", bt->bthRoot); - hfs_warn("bthNRecs %d\n", bt->bthNRecs); - hfs_warn("bthFNode %d\n", bt->bthFNode); - hfs_warn("bthLNode %d\n", bt->bthLNode); - hfs_warn("bthKeyLen %d\n", bt->bthKeyLen); - hfs_warn("bthNNodes %d\n", bt->bthNNodes); - hfs_warn("bthFree %d\n", bt->bthFree); - p = (unsigned char *)hfs_buffer_data(bt->head.buf); - q = p + HFS_SECTOR_SIZE; - while (p < q) { - hfs_warn("%02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x\n", - *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, - *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++); - } -#endif - - /* Read in the root if it exists. - The header always exists, but the root exists only if the - tree is non-empty */ - if (bt->bthDepth && bt->bthRoot) { - if (!HFS_NEW(bt->root)) { - goto bail2; - } - hfs_bnode_read(bt->root, bt, bt->bthRoot, HFS_STICKY); - if (!hfs_buffer_ok(bt->root->buf)) { - goto bail1; - } - } else { - bt->root = NULL; + node->type = HFS_NODE_MAP; + node->num_recs = 1; + hfs_bnode_clear(node, 0, tree->node_size); + desc.next = 0; + desc.prev = 0; + desc.type = HFS_NODE_MAP; + desc.height = 0; + desc.num_recs = cpu_to_be16(1); + desc.reserved = 0; + hfs_bnode_write(node, &desc, 0, sizeof(desc)); + hfs_bnode_write_u16(node, 14, 0x8000); + hfs_bnode_write_u16(node, tree->node_size - 2, 14); + hfs_bnode_write_u16(node, tree->node_size - 4, tree->node_size - 6); + + return node; +} + +struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) +{ + struct hfs_bnode *node, *next_node; + struct page **pagep; + u32 nidx, idx; + u16 off, len; + u8 *data, byte, m; + int i; + + while (!tree->free_nodes) { + struct inode *inode = tree->inode; + u32 count; + int res; + + res = hfs_extend_file(inode); + if (res) + return ERR_PTR(res); + inode->i_blocks = HFS_I(inode)->alloc_blocks * + HFS_SB(tree->sb)->fs_div; + HFS_I(inode)->phys_size = inode->i_size = + (loff_t)inode->i_blocks << tree->sb->s_blocksize_bits; + count = inode->i_size >> tree->node_size_shift; + tree->free_nodes = count - tree->node_count; + tree->node_count = count; } - return bt; + nidx = 0; + node = hfs_bnode_find(tree, nidx); + if (IS_ERR(node)) + return node; + len = hfs_brec_lenoff(node, 2, &off); - bail1: - hfs_bnode_ditch(bt->root); - bail2: - hfs_bnode_ditch(&bt->head); - HFS_DELETE(bt); - bail3: - return NULL; + off += node->page_offset; + pagep = node->page + (off >> PAGE_CACHE_SHIFT); + data = kmap(*pagep); + off &= ~PAGE_CACHE_MASK; + idx = 0; + + for (;;) { + while (len) { + byte = data[off]; + if (byte != 0xff) { + for (m = 0x80, i = 0; i < 8; m >>= 1, i++) { + if (!(byte & m)) { + idx += i; + data[off] |= m; + set_page_dirty(*pagep); + kunmap(*pagep); + tree->free_nodes--; + mark_inode_dirty(tree->inode); + hfs_bnode_put(node); + return hfs_bnode_create(tree, idx); + } + } + } + if (++off >= PAGE_CACHE_SIZE) { + kunmap(*pagep); + data = kmap(*++pagep); + off = 0; + } + idx += 8; + len--; + } + kunmap(*pagep); + nidx = node->next; + if (!nidx) { + printk("create new bmap node...\n"); + next_node = hfs_bmap_new_bmap(node, idx); + } else + next_node = hfs_bnode_find(tree, nidx); + hfs_bnode_put(node); + if (IS_ERR(next_node)) + return next_node; + node = next_node; + + len = hfs_brec_lenoff(node, 0, &off); + off += node->page_offset; + pagep = node->page + (off >> PAGE_CACHE_SHIFT); + data = kmap(*pagep); + off &= ~PAGE_CACHE_MASK; + } } -/* - * hfs_btree_commit() - * - * Called to write a possibly dirty btree back to disk. - */ -void hfs_btree_commit(struct hfs_btree *bt, hfs_byte_t ext[12], hfs_lword_t size) +void hfs_bmap_free(struct hfs_bnode *node) { - if (bt->dirt) { - struct BTHdrRec *th; - th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) + - sizeof(struct NodeDescriptor)); - - hfs_put_hs(bt->bthDepth, th->bthDepth); - hfs_put_hl(bt->bthRoot, th->bthRoot); - hfs_put_hl(bt->bthNRecs, th->bthNRecs); - hfs_put_hl(bt->bthFNode, th->bthFNode); - hfs_put_hl(bt->bthLNode, th->bthLNode); - hfs_put_hl(bt->bthNNodes, th->bthNNodes); - hfs_put_hl(bt->bthFree, th->bthFree); - hfs_buffer_dirty(bt->head.buf); - - /* - * Commit the bnodes which are not cached. - * The map nodes don't need to be committed here because - * they are committed every time they are changed. - */ - hfs_bnode_commit(&bt->head); - if (bt->root) { - hfs_bnode_commit(bt->root); - } + struct hfs_btree *tree; + struct page *page; + u16 off, len; + u32 nidx; + u8 *data, byte, m; - - hfs_put_hl(bt->bthNNodes << HFS_SECTOR_SIZE_BITS, size); - hfs_extent_out(&bt->entry.u.file.data_fork, ext); - /* hfs_buffer_dirty(mdb->buf); (Done by caller) */ + dprint(DBG_BNODE_MOD, "btree_free_node: %u\n", node->this); + tree = node->tree; + nidx = node->this; + node = hfs_bnode_find(tree, 0); + if (IS_ERR(node)) + return; + len = hfs_brec_lenoff(node, 2, &off); + while (nidx >= len * 8) { + u32 i; - bt->dirt = 0; + nidx -= len * 8; + i = node->next; + hfs_bnode_put(node); + if (!i) { + /* panic */; + printk("HFS: unable to free bnode %u. bmap not found!\n", node->this); + return; + } + node = hfs_bnode_find(tree, i); + if (IS_ERR(node)) + return; + if (node->type != HFS_NODE_MAP) { + /* panic */; + printk("HFS: invalid bmap found! (%u,%d)\n", node->this, node->type); + hfs_bnode_put(node); + return; + } + len = hfs_brec_lenoff(node, 0, &off); + } + off += node->page_offset + nidx / 8; + page = node->page[off >> PAGE_CACHE_SHIFT]; + data = kmap(page); + off &= ~PAGE_CACHE_MASK; + m = 1 << (~nidx & 7); + byte = data[off]; + if (!(byte & m)) { + printk("HFS: trying to free free bnode %u(%d)\n", node->this, node->type); + kunmap(page); + hfs_bnode_put(node); + return; } + data[off] = byte & ~m; + set_page_dirty(page); + kunmap(page); + hfs_bnode_put(node); + tree->free_nodes++; + mark_inode_dirty(tree->inode); } diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h new file mode 100644 index 000000000000..266af5e54609 --- /dev/null +++ b/fs/hfs/btree.h @@ -0,0 +1,171 @@ +/* + * linux/fs/hfs/btree.h + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + */ + +#include "hfs_fs.h" + +typedef int (*btree_keycmp)(const btree_key *, const btree_key *); + +#define NODE_HASH_SIZE 256 + +/* A HFS BTree held in memory */ +struct hfs_btree { + struct super_block *sb; + struct inode *inode; + btree_keycmp keycmp; + + u32 cnid; + u32 root; + u32 leaf_count; + u32 leaf_head; + u32 leaf_tail; + u32 node_count; + u32 free_nodes; + u32 attributes; + + unsigned int node_size; + unsigned int node_size_shift; + unsigned int max_key_len; + unsigned int depth; + + //unsigned int map1_size, map_size; + struct semaphore tree_lock; + + unsigned int pages_per_bnode; + spinlock_t hash_lock; + struct hfs_bnode *node_hash[NODE_HASH_SIZE]; + int node_hash_cnt; +}; + +/* A HFS BTree node in memory */ +struct hfs_bnode { + struct hfs_btree *tree; + + u32 prev; + u32 this; + u32 next; + u32 parent; + + u16 num_recs; + u8 type; + u8 height; + + struct hfs_bnode *next_hash; + unsigned long flags; + wait_queue_head_t lock_wq; + atomic_t refcnt; + unsigned int page_offset; + struct page *page[0]; +}; + +#define HFS_BNODE_ERROR 0 +#define HFS_BNODE_NEW 1 +#define HFS_BNODE_DELETED 2 + +struct hfs_find_data { + btree_key *key; + btree_key *search_key; + struct hfs_btree *tree; + struct hfs_bnode *bnode; + int record; + int keyoffset, keylength; + int entryoffset, entrylength; +}; + + +/* btree.c */ +extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp); +extern void hfs_btree_close(struct hfs_btree *); +extern void hfs_btree_write(struct hfs_btree *); +extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *); +extern void hfs_bmap_free(struct hfs_bnode *node); + +/* bnode.c */ +extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int); +extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int); +extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int); +extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int); +extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int); +extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16); +extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8); +extern void hfs_bnode_clear(struct hfs_bnode *, int, int); +extern void hfs_bnode_copy(struct hfs_bnode *, int, + struct hfs_bnode *, int, int); +extern void hfs_bnode_move(struct hfs_bnode *, int, int, int); +extern void hfs_bnode_dump(struct hfs_bnode *); +extern void hfs_bnode_unlink(struct hfs_bnode *); +extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32); +extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32); +extern void hfs_bnode_unhash(struct hfs_bnode *); +extern void hfs_bnode_free(struct hfs_bnode *); +extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32); +extern void hfs_bnode_get(struct hfs_bnode *); +extern void hfs_bnode_put(struct hfs_bnode *); + +/* brec.c */ +extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *); +extern u16 hfs_brec_keylen(struct hfs_bnode *, u16); +extern int hfs_brec_insert(struct hfs_find_data *, void *, int); +extern int hfs_brec_remove(struct hfs_find_data *); +extern struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *); +extern int hfs_brec_update_parent(struct hfs_find_data *); +extern int hfs_btree_inc_height(struct hfs_btree *); + +/* bfind.c */ +extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); +extern void hfs_find_exit(struct hfs_find_data *); +extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *); +extern int hfs_brec_find(struct hfs_find_data *); +extern int hfs_brec_read(struct hfs_find_data *, void *, int); +extern int hfs_brec_goto(struct hfs_find_data *, int); + + +struct hfs_bnode_desc { + u32 next; /* (V) Number of the next node at this level */ + u32 prev; /* (V) Number of the prev node at this level */ + u8 type; /* (F) The type of node */ + u8 height; /* (F) The level of this node (leaves=1) */ + u16 num_recs; /* (V) The number of records in this node */ + u16 reserved; +} __packed; + +#define HFS_NODE_INDEX 0x00 /* An internal (index) node */ +#define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */ +#define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */ +#define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */ + +struct hfs_btree_header_rec { + u16 depth; /* (V) The number of levels in this B-tree */ + u32 root; /* (V) The node number of the root node */ + u32 leaf_count; /* (V) The number of leaf records */ + u32 leaf_head; /* (V) The number of the first leaf node */ + u32 leaf_tail; /* (V) The number of the last leaf node */ + u16 node_size; /* (F) The number of bytes in a node (=512) */ + u16 max_key_len; /* (F) The length of a key in an index node */ + u32 node_count; /* (V) The total number of nodes */ + u32 free_nodes; /* (V) The number of unused nodes */ + u16 reserved1; + u32 clump_size; /* (F) clump size. not usually used. */ + u8 btree_type; /* (F) BTree type */ + u8 reserved2; + u32 attributes; /* (F) attributes */ + u32 reserved3[16]; +} __packed; + +#define HFS_NODE_INDEX 0x00 /* An internal (index) node */ +#define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */ +#define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */ +#define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */ + +#define BTREE_ATTR_BADCLOSE 0x00000001 /* b-tree not closed properly. not + used by hfsplus. */ +#define HFS_TREE_BIGKEYS 0x00000002 /* key length is u16 instead of u8. + used by hfsplus. */ +#define HFS_TREE_VARIDXKEYS 0x00000004 /* variable key length instead of + max key length. use din catalog + b-tree but not in extents + b-tree (hfsplus). */ diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 1d6f65ef72e2..701f896f10f8 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -1,641 +1,75 @@ /* - * linux/fs/hfs/catalog.c + * linux/fs/hfs/catalog.c * * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * This file contains the functions related to the catalog B-tree. * - * "XXX" in a comment is a note to myself to consider changing something. - * - * Cache code shamelessly stolen from + * Cache code shamelessly stolen from * linux/fs/inode.c Copyright (C) 1991, 1992 Linus Torvalds * re-shamelessly stolen Copyright (C) 1997 Linus Torvalds - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - * - * The code in this file initializes some structures by calling - * memset(&foo, 0, sizeof(foo)). This produces the desired behavior - * only due to the non-ANSI assumption that the machine representation - */ - -#include "hfs.h" - -/*================ Variable-like macros ================*/ - -/* Number of hash table slots */ -#define C_HASHBITS 10 -#define C_HASHSIZE (1UL << C_HASHBITS) -#define C_HASHMASK (C_HASHSIZE - 1) - -/* Number of entries to fit in a single page on an i386. - * Actually, now it's used to increment the free entry pool. */ -#define CCACHE_INC (PAGE_SIZE/sizeof(struct hfs_cat_entry)) -#define CCACHE_MAX (CCACHE_INC * 8) - -/*================ File-local data types ================*/ - -/* The catalog record for a file */ -typedef struct { - hfs_byte_t Flags; /* Flags such as read-only */ - hfs_byte_t Typ; /* file version number = 0 */ - hfs_finfo_t UsrWds; /* data used by the Finder */ - hfs_lword_t FlNum; /* The CNID */ - hfs_word_t StBlk; /* obsolete */ - hfs_lword_t LgLen; /* The logical EOF of the data fork*/ - hfs_lword_t PyLen; /* The physical EOF of the data fork */ - hfs_word_t RStBlk; /* obsolete */ - hfs_lword_t RLgLen; /* The logical EOF of the rsrc fork */ - hfs_lword_t RPyLen; /* The physical EOF of the rsrc fork */ - hfs_lword_t CrDat; /* The creation date */ - hfs_lword_t MdDat; /* The modified date */ - hfs_lword_t BkDat; /* The last backup date */ - hfs_fxinfo_t FndrInfo; /* more data for the Finder */ - hfs_word_t ClpSize; /* number of bytes to allocate - when extending files */ - hfs_byte_t ExtRec[12]; /* first extent record - for the data fork */ - hfs_byte_t RExtRec[12]; /* first extent record - for the resource fork */ - hfs_lword_t Resrv; /* reserved by Apple */ -} __attribute__((packed)) FIL_REC; - -/* the catalog record for a directory */ -typedef struct { - hfs_word_t Flags; /* flags */ - hfs_word_t Val; /* Valence: number of files and - dirs in the directory */ - hfs_lword_t DirID; /* The CNID */ - hfs_lword_t CrDat; /* The creation date */ - hfs_lword_t MdDat; /* The modification date */ - hfs_lword_t BkDat; /* The last backup date */ - hfs_dinfo_t UsrInfo; /* data used by the Finder */ - hfs_dxinfo_t FndrInfo; /* more data used by Finder */ - hfs_byte_t Resrv[16]; /* reserved by Apple */ -} __attribute__((packed)) DIR_REC; - -/* the catalog record for a thread */ -typedef struct { - hfs_byte_t Reserv[8]; /* reserved by Apple */ - hfs_lword_t ParID; /* CNID of parent directory */ - struct hfs_name CName; /* The name of this entry */ -} __attribute__((packed)) THD_REC; - -/* A catalog tree record */ -struct hfs_cat_rec { - hfs_byte_t cdrType; /* The type of entry */ - hfs_byte_t cdrResrv2; /* padding */ - union { - FIL_REC fil; - DIR_REC dir; - THD_REC thd; - } u; -} __attribute__((packed)); - -/*================ File-local variables ================*/ - -static LIST_HEAD(entry_in_use); -static LIST_HEAD(entry_unused); -static struct list_head hash_table[C_HASHSIZE]; - -static spinlock_t entry_lock = SPIN_LOCK_UNLOCKED; - -static struct { - int nr_entries; - int nr_free_entries; -} entries_stat; - -/*================ File-local functions ================*/ - -/* - * brec_to_id - * - * Get the CNID from a brec - */ -static inline hfs_u32 brec_to_id(struct hfs_brec *brec) -{ - struct hfs_cat_rec *rec = brec->data; - - return hfs_get_nl((rec->cdrType==HFS_CDR_FIL) ? - rec->u.fil.FlNum : rec->u.dir.DirID); -} - -/* - * hashfn() - * - * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer. - */ -static inline unsigned int hashfn(const struct hfs_mdb *mdb, - const struct hfs_cat_key *key) -{ - unsigned int hash; - - hash = (unsigned long) mdb | (unsigned long) key->ParID[3] | - hfs_strhash(key->CName.Name, key->CName.Len); - hash = hash ^ (hash >> C_HASHBITS) ^ (hash >> C_HASHBITS*2); - return hash & C_HASHMASK; -} - -/* - * hash() - * - * hash an (struct mdb *) and a (struct hfs_cat_key *) - * to a pointer to a slot in the hash table. - */ -static inline struct list_head *hash(struct hfs_mdb *mdb, - const struct hfs_cat_key *key) -{ - return hash_table + hashfn(mdb, key); -} - -static inline void insert_hash(struct hfs_cat_entry *entry) -{ - struct list_head *head = hash(entry->mdb, &entry->key); - list_add(&entry->hash, head); -} - -static inline void remove_hash(struct hfs_cat_entry *entry) -{ - list_del_init(&entry->hash); -} - -/* - * wait_on_entry() - * - * Sleep until a locked entry is unlocked. - */ -static inline void wait_on_entry(struct hfs_cat_entry * entry) -{ - while ((entry->state & HFS_LOCK)) { - hfs_sleep_on(&entry->wait); - } -} - -/* - * lock_entry() - * - * Obtain an exclusive lock on an entry. */ -static void lock_entry(struct hfs_cat_entry * entry) -{ - wait_on_entry(entry); - spin_lock(&entry_lock); - entry->state |= HFS_LOCK; - spin_unlock(&entry_lock); -} - -/* - * lock_entry() - * - * Relinquish an exclusive lock on an entry. - */ -static void unlock_entry(struct hfs_cat_entry * entry) -{ - spin_lock(&entry_lock); - entry->state &= ~HFS_LOCK; - spin_unlock(&entry_lock); - hfs_wake_up(&entry->wait); -} - -/* put entry on mdb dirty list. */ -void hfs_cat_mark_dirty(struct hfs_cat_entry *entry) -{ - struct hfs_mdb *mdb = entry->mdb; - spin_lock(&entry_lock); - if (!(entry->state & HFS_DIRTY)) { - entry->state |= HFS_DIRTY; - - /* Only add valid (ie hashed) entries to the dirty list. */ - if (!list_empty(&entry->hash)) { - list_del(&entry->list); - list_add(&entry->list, &mdb->entry_dirty); - } - } - spin_unlock(&entry_lock); -} - -/* delete an entry and remove it from the hash table. */ -static void delete_entry(struct hfs_cat_entry *entry) -{ - if (!(entry->state & HFS_DELETED)) { - entry->state |= HFS_DELETED; - list_del_init(&entry->hash); - - if (entry->type == HFS_CDR_FIL) { - /* free all extents */ - entry->u.file.data_fork.lsize = 0; - hfs_extent_adj(&entry->u.file.data_fork); - entry->u.file.rsrc_fork.lsize = 0; - hfs_extent_adj(&entry->u.file.rsrc_fork); - } - } -} - - -static inline void init_entry(struct hfs_cat_entry *entry) -{ - memset(entry, 0, sizeof(*entry)); - hfs_init_waitqueue(&entry->wait); - INIT_LIST_HEAD(&entry->hash); - INIT_LIST_HEAD(&entry->list); -} - -/* - * hfs_cat_alloc() - * - * Try to allocate another entry. - */ -static inline struct hfs_cat_entry *hfs_cat_alloc(void) -{ - struct hfs_cat_entry *entry; - - if (!HFS_NEW(entry)) - return NULL; - - init_entry(entry); - return entry; -} - -/* this gets called with the spinlock held. */ -static int grow_entries(void) -{ - struct hfs_cat_entry *entry; - int i; - - for (i = 0; i < CCACHE_INC; i++) { - if (!(entry = hfs_cat_alloc())) - break; - list_add(&entry->list, &entry_unused); - } - - entries_stat.nr_entries += i; - entries_stat.nr_free_entries += i; - - return i; -} +#include "hfs_fs.h" +#include "btree.h" /* - * __read_entry() + * hfs_cat_build_key() * - * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry). + * Given the ID of the parent and the name build a search key. */ -static void __read_entry(struct hfs_cat_entry *entry, - const struct hfs_cat_rec *cat) +void hfs_cat_build_key(btree_key *key, u32 parent, struct qstr *name) { - entry->type = cat->cdrType; - - if (cat->cdrType == HFS_CDR_DIR) { - struct hfs_dir *dir = &entry->u.dir; - - entry->cnid = hfs_get_nl(cat->u.dir.DirID); - - dir->magic = HFS_DIR_MAGIC; - dir->flags = hfs_get_ns(cat->u.dir.Flags); - memcpy(&entry->info.dir.dinfo, &cat->u.dir.UsrInfo, 16); - memcpy(&entry->info.dir.dxinfo, &cat->u.dir.FndrInfo, 16); - entry->create_date = hfs_get_nl(cat->u.dir.CrDat); - entry->modify_date = hfs_get_nl(cat->u.dir.MdDat); - entry->backup_date = hfs_get_nl(cat->u.dir.BkDat); - dir->dirs = dir->files = 0; - init_rwsem(&dir->sem); - } else if (cat->cdrType == HFS_CDR_FIL) { - struct hfs_file *fil = &entry->u.file; - - entry->cnid = hfs_get_nl(cat->u.fil.FlNum); - - fil->magic = HFS_FILE_MAGIC; - - fil->data_fork.fork = HFS_FK_DATA; - fil->data_fork.entry = entry; - fil->data_fork.lsize = hfs_get_hl(cat->u.fil.LgLen); - fil->data_fork.psize = hfs_get_hl(cat->u.fil.PyLen) >> - HFS_SECTOR_SIZE_BITS; - hfs_extent_in(&fil->data_fork, cat->u.fil.ExtRec); - - fil->rsrc_fork.fork = HFS_FK_RSRC; - fil->rsrc_fork.entry = entry; - fil->rsrc_fork.lsize = hfs_get_hl(cat->u.fil.RLgLen); - fil->rsrc_fork.psize = hfs_get_hl(cat->u.fil.RPyLen) >> - HFS_SECTOR_SIZE_BITS; - hfs_extent_in(&fil->rsrc_fork, cat->u.fil.RExtRec); - - memcpy(&entry->info.file.finfo, &cat->u.fil.UsrWds, 16); - memcpy(&entry->info.file.fxinfo, &cat->u.fil.FndrInfo, 16); - - entry->create_date = hfs_get_nl(cat->u.fil.CrDat); - entry->modify_date = hfs_get_nl(cat->u.fil.MdDat); - entry->backup_date = hfs_get_nl(cat->u.fil.BkDat); - fil->clumpablks = (hfs_get_hs(cat->u.fil.ClpSize) - / entry->mdb->alloc_blksz) - >> HFS_SECTOR_SIZE_BITS; - fil->flags = cat->u.fil.Flags; + key->cat.reserved = 0; + key->cat.ParID = cpu_to_be32(parent); + if (name) { + hfs_triv2mac(&key->cat.CName, name); + key->key_len = 6 + key->cat.CName.len; } else { - hfs_warn("hfs_fs: entry is neither file nor directory!\n"); + memset(&key->cat.CName, 0, sizeof(struct hfs_name)); + key->key_len = 6; } } -/* - * count_dir_entries() - * - * Count the number of files and directories in a given directory. - */ -static inline void count_dir_entries(struct hfs_cat_entry *entry, - struct hfs_brec *brec) -{ - int error = 0; - hfs_u32 cnid; - hfs_u8 type; - - if (!hfs_cat_open(entry, brec)) { - while (!(error = hfs_cat_next(entry, brec, 1, &cnid, &type))) { - if (type == HFS_CDR_FIL) { - ++entry->u.dir.files; - } else if (type == HFS_CDR_DIR) { - ++entry->u.dir.dirs; - } - } /* -ENOENT is normal termination */ - } - if (error != -ENOENT) { - entry->cnid = 0; - } -} - -/* - * read_entry() - * - * Convert a (struct hfs_brec) to a (struct hfs_cat_entry). - */ -static inline void read_entry(struct hfs_cat_entry *entry, - struct hfs_brec *brec) -{ - int need_count; - struct hfs_cat_rec *rec = brec->data; - - __read_entry(entry, rec); - - need_count = (rec->cdrType == HFS_CDR_DIR) && rec->u.dir.Val; - - hfs_brec_relse(brec, NULL); - - if (need_count) { - count_dir_entries(entry, brec); - } -} - -/* - * __write_entry() - * - * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec). - */ -static void __write_entry(const struct hfs_cat_entry *entry, - struct hfs_cat_rec *cat) +int hfs_cat_build_record(hfs_cat_rec *rec, u32 cnid, struct inode *inode) { - if (entry->type == HFS_CDR_DIR) { - const struct hfs_dir *dir = &entry->u.dir; + u32 mtime = hfs_mtime(); - hfs_put_ns(dir->flags, cat->u.dir.Flags); - hfs_put_hs(dir->dirs + dir->files, cat->u.dir.Val); - hfs_put_nl(entry->cnid, cat->u.dir.DirID); - hfs_put_nl(entry->create_date, cat->u.dir.CrDat); - hfs_put_nl(entry->modify_date, cat->u.dir.MdDat); - hfs_put_nl(entry->backup_date, cat->u.dir.BkDat); - memcpy(&cat->u.dir.UsrInfo, &entry->info.dir.dinfo, 16); - memcpy(&cat->u.dir.FndrInfo, &entry->info.dir.dxinfo, 16); - } else if (entry->type == HFS_CDR_FIL) { - const struct hfs_file *fil = &entry->u.file; - - cat->u.fil.Flags = fil->flags; - hfs_put_nl(entry->cnid, cat->u.fil.FlNum); - memcpy(&cat->u.fil.UsrWds, &entry->info.file.finfo, 16); - hfs_put_hl(fil->data_fork.lsize, cat->u.fil.LgLen); - hfs_put_hl(fil->data_fork.psize << HFS_SECTOR_SIZE_BITS, - cat->u.fil.PyLen); - hfs_put_hl(fil->rsrc_fork.lsize, cat->u.fil.RLgLen); - hfs_put_hl(fil->rsrc_fork.psize << HFS_SECTOR_SIZE_BITS, - cat->u.fil.RPyLen); - hfs_put_nl(entry->create_date, cat->u.fil.CrDat); - hfs_put_nl(entry->modify_date, cat->u.fil.MdDat); - hfs_put_nl(entry->backup_date, cat->u.fil.BkDat); - memcpy(&cat->u.fil.FndrInfo, &entry->info.file.fxinfo, 16); - hfs_put_hs((fil->clumpablks * entry->mdb->alloc_blksz) - << HFS_SECTOR_SIZE_BITS, cat->u.fil.ClpSize); - hfs_extent_out(&fil->data_fork, cat->u.fil.ExtRec); - hfs_extent_out(&fil->rsrc_fork, cat->u.fil.RExtRec); + memset(rec, 0, sizeof(*rec)); + if (S_ISDIR(inode->i_mode)) { + rec->type = HFS_CDR_DIR; + rec->dir.DirID = cpu_to_be32(cnid); + rec->dir.CrDat = mtime; + rec->dir.MdDat = mtime; + rec->dir.BkDat = 0; + rec->dir.UsrInfo.frView = cpu_to_be16(0xff); + return sizeof(struct hfs_cat_dir); } else { - hfs_warn("__write_entry: invalid entry\n"); - } -} - -/* - * write_entry() - * - * Write a modified entry back to the catalog B-tree. this gets called - * with the entry locked. - */ -static void write_entry(struct hfs_cat_entry * entry) -{ - struct hfs_brec brec; - int error; - - if (!(entry->state & HFS_DELETED)) { - error = hfs_bfind(&brec, entry->mdb->cat_tree, - HFS_BKEY(&entry->key), HFS_BFIND_WRITE); - if (!error) { - if ((entry->state & HFS_KEYDIRTY)) { - /* key may have changed case due to a rename */ - entry->state &= ~HFS_KEYDIRTY; - if (brec.key->KeyLen != entry->key.KeyLen) { - hfs_warn("hfs_write_entry: key length " - "changed!\n"); - error = 1; - } else { - memcpy(brec.key, &entry->key, - entry->key.KeyLen); - } - } else if (entry->cnid != brec_to_id(&brec)) { - hfs_warn("hfs_write_entry: CNID " - "changed unexpectedly!\n"); - error = 1; - } - if (!error) { - __write_entry(entry, brec.data); - } - hfs_brec_relse(&brec, NULL); - } - if (error) { - hfs_warn("hfs_write_entry: unable to write " - "entry %08x\n", entry->cnid); - } - } -} - - -/* this gets called with the spinlock held. */ -static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb, - const struct hfs_cat_key *key) -{ - struct list_head *tmp, *head = hash(mdb, key); - struct hfs_cat_entry * entry; - - tmp = head; - for (;;) { - tmp = tmp->next; - entry = NULL; - if (tmp == head) - break; - entry = list_entry(tmp, struct hfs_cat_entry, hash); - if (entry->mdb != mdb) - continue; - if (hfs_cat_compare(&entry->key, key)) { - continue; - } - entry->count++; - break; - } - - return entry; -} - - -/* be careful. this gets called with the spinlock held. */ -static struct hfs_cat_entry *get_new_entry(struct hfs_mdb *mdb, - const struct hfs_cat_key *key, - const int read) -{ - struct hfs_cat_entry *entry; - struct list_head *head = hash(mdb, key); - struct list_head *tmp; - -add_new_entry: - tmp = entry_unused.next; - if ((tmp != &entry_unused) ) { - list_del(tmp); - entries_stat.nr_free_entries--; - entry = list_entry(tmp, struct hfs_cat_entry, list); - list_add(&entry->list, &entry_in_use); - list_add(&entry->hash, head); - entry->mdb = mdb; - entry->count = 1; - memcpy(&entry->key, key, sizeof(*key)); - entry->state = HFS_LOCK; - spin_unlock(&entry_lock); - - if (read) { - struct hfs_brec brec; - - if (hfs_bfind(&brec, mdb->cat_tree, - HFS_BKEY(key), HFS_BFIND_READ_EQ)) { - /* uh oh. we failed to read the record. - * the entry doesn't actually exist. */ - goto read_fail; - } - - read_entry(entry, &brec); - - /* error */ - if (!entry->cnid) { - goto read_fail; - } - - /* we don't have to acquire a spinlock here or - * below for the unlocking bits as we're the first - * user of this entry. */ - entry->state &= ~HFS_LOCK; - hfs_wake_up(&entry->wait); - } - - return entry; + /* init some fields for the file record */ + rec->type = HFS_CDR_FIL; + rec->file.Flags = HFS_FIL_USED | HFS_FIL_THD; + if (!(inode->i_mode & S_IWUSR)) + rec->file.Flags |= HFS_FIL_LOCK; + rec->file.FlNum = cpu_to_be32(cnid); + rec->file.CrDat = mtime; + rec->file.MdDat = mtime; + rec->file.BkDat = 0; + rec->file.UsrWds.fdType = HFS_SB(inode->i_sb)->s_type; + rec->file.UsrWds.fdCreator = HFS_SB(inode->i_sb)->s_creator; + return sizeof(struct hfs_cat_file); } - - - /* try to allocate more entries. grow_entries() doesn't release - * the spinlock. */ - if (grow_entries()) - goto add_new_entry; - - spin_unlock(&entry_lock); - return NULL; - -read_fail: - /* short-cut hfs_cat_put by doing everything here. */ - spin_lock(&entry_lock); - list_del(&entry->hash); - list_del(&entry->list); - init_entry(entry); - list_add(&entry->list, &entry_unused); - entries_stat.nr_free_entries++; - spin_unlock(&entry_lock); - return NULL; } -/* - * get_entry() - * - * Try to return an entry for the indicated file or directory. - * If ('read' == 0) then no attempt will be made to read it from disk - * and a locked, but uninitialized, entry is returned. - */ -static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb, - const struct hfs_cat_key *key, - const int read) -{ - struct hfs_cat_entry * entry; - -#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) - hfs_warn("hfs_get_entry: mdb=%p key=%s read=%d\n", - mdb, key->CName.Name, read); -#endif - - spin_lock(&entry_lock); - entry = find_entry(mdb, key); - if (!entry) { - return get_new_entry(mdb, key, read); - } - spin_unlock(&entry_lock); - wait_on_entry(entry); - return entry; -} - -/* - * new_cnid() - * - * Allocate a CNID to use for a new file or directory. - */ -static inline hfs_u32 new_cnid(struct hfs_mdb *mdb) +static int hfs_cat_build_thread(hfs_cat_rec *rec, int type, + u32 parentid, struct qstr *name) { - /* If the create succeeds then the mdb will get dirtied */ - return htonl(mdb->next_id++); -} - -/* - * update_dir() - * - * Update counts, times and dirt on a changed directory - */ -static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir, - int is_dir, int count) -{ - /* update counts */ - if (is_dir) { - mdb->dir_count += count; - dir->u.dir.dirs += count; - if (dir->cnid == htonl(HFS_ROOT_CNID)) { - mdb->root_dirs += count; - } - } else { - mdb->file_count += count; - dir->u.dir.files += count; - if (dir->cnid == htonl(HFS_ROOT_CNID)) { - mdb->root_files += count; - } - } - - /* update times and dirt */ - dir->modify_date = hfs_time(); - hfs_cat_mark_dirty(dir); + rec->type = type; + memset(rec->thread.reserved, 0, sizeof(rec->thread.reserved)); + rec->thread.ParID = cpu_to_be32(parentid); + hfs_triv2mac(&rec->thread.CName, name); + return sizeof(struct hfs_cat_thread); } /* @@ -644,301 +78,53 @@ static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir, * Add a new file or directory to the catalog B-tree and * return a (struct hfs_cat_entry) for it in '*result'. */ -static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key, - const struct hfs_cat_rec *record, int is_dir, - hfs_u32 cnid, struct hfs_cat_entry **result) +int hfs_cat_create(u32 cnid, struct inode *dir, struct qstr *str, struct inode *inode) { - struct hfs_mdb *mdb = parent->mdb; - struct hfs_cat_entry *entry; - struct hfs_cat_key thd_key; - struct hfs_cat_rec thd_rec; - int error, has_thread; - - if (result) { - *result = NULL; - } - - /* keep readers from getting confused by changing dir size */ - down_write(&parent->u.dir.sem); - - /* create a locked entry in the cache */ - entry = get_entry(mdb, key, 0); - if (!entry) { - /* The entry exists but can't be read */ - error = -EIO; - goto done; - } - - if (entry->cnid) { - /* The (unlocked) entry exists in the cache */ - error = -EEXIST; - goto bail2; - } + struct hfs_find_data fd; + struct super_block *sb; + union hfs_cat_rec entry; + int entry_size; + int err; - /* limit directory valence to signed 16-bit integer */ - if ((parent->u.dir.dirs + parent->u.dir.files) >= HFS_MAX_VALENCE) { - error = -ENOSPC; - goto bail1; - } - - has_thread = is_dir || (record->u.fil.Flags & HFS_FIL_THD); + dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n", str->name, cnid, inode->i_nlink); + if (dir->i_size >= HFS_MAX_VALENCE) + return -ENOSPC; - if (has_thread) { - /* init some fields for the thread record */ - memset(&thd_rec, 0, sizeof(thd_rec)); - thd_rec.cdrType = is_dir ? HFS_CDR_THD : HFS_CDR_FTH; - memcpy(&thd_rec.u.thd.ParID, &key->ParID, - sizeof(hfs_u32) + sizeof(struct hfs_name)); + sb = dir->i_sb; + hfs_find_init(HFS_SB(sb)->cat_tree, &fd); - /* insert the thread record */ - hfs_cat_build_key(cnid, NULL, &thd_key); - error = hfs_binsert(mdb->cat_tree, HFS_BKEY(&thd_key), - &thd_rec, 2 + sizeof(THD_REC)); - if (error) { - goto bail1; - } + hfs_cat_build_key(fd.search_key, cnid, NULL); + entry_size = hfs_cat_build_thread(&entry, S_ISDIR(inode->i_mode) ? + HFS_CDR_THD : HFS_CDR_FTH, + dir->i_ino, str); + err = hfs_brec_find(&fd); + if (err != -ENOENT) { + if (!err) + err = -EEXIST; + goto out; } + err = hfs_brec_insert(&fd, &entry, entry_size); + if (err) + goto out; - /* insert the record */ - error = hfs_binsert(mdb->cat_tree, HFS_BKEY(key), record, - is_dir ? 2 + sizeof(DIR_REC) : - 2 + sizeof(FIL_REC)); - if (error) { - if (has_thread && (error != -EIO)) { - /* at least TRY to remove the thread record */ - (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key)); - } - goto bail1; + hfs_cat_build_key(fd.search_key, dir->i_ino, str); + entry_size = hfs_cat_build_record(&entry, cnid, inode); + err = hfs_brec_find(&fd); + if (err != -ENOENT) { + /* panic? */ + if (!err) + err = -EEXIST; + goto out; } - - /* update the parent directory */ - update_dir(mdb, parent, is_dir, 1); - - /* complete the cache entry and return success */ - __read_entry(entry, record); - unlock_entry(entry); - - if (result) { - *result = entry; - } else { - hfs_cat_put(entry); + err = hfs_brec_insert(&fd, &entry, entry_size); + if (!err) { + dir->i_size++; + mark_inode_dirty(dir); } - goto done; +out: + hfs_find_exit(&fd); -bail1: - /* entry really didn't exist, so we don't need to really delete it. - * we do need to remove it from the hash, though. */ - entry->state |= HFS_DELETED; - remove_hash(entry); - unlock_entry(entry); -bail2: - hfs_cat_put(entry); -done: - up_write(&parent->u.dir.sem); - return error; -} - -/*================ Global functions ================*/ - -/* - * hfs_cat_put() - * - * Release an entry we aren't using anymore. - * - * nothing in hfs_cat_put goes to sleep now except on the initial entry. - */ -void hfs_cat_put(struct hfs_cat_entry * entry) -{ - if (entry) { - wait_on_entry(entry); - - /* just in case. this should never happen. */ - if (!entry->count) { - hfs_warn("hfs_cat_put: trying to free free entry: %p\n", - entry); - return; - } - -#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) - hfs_warn("hfs_cat_put: %p(%u) type=%d state=%lu\n", - entry, entry->count, entry->type, entry->state); -#endif - spin_lock(&entry_lock); - if (!--entry->count) { - if ((entry->state & HFS_DELETED)) - goto entry_deleted; - - if ((entry->type == HFS_CDR_FIL)) { - /* clear out any cached extents */ - if (entry->u.file.data_fork.first.next) { - hfs_extent_free(&entry->u.file.data_fork); - } - if (entry->u.file.rsrc_fork.first.next) { - hfs_extent_free(&entry->u.file.rsrc_fork); - } - } - - /* if we put a dirty entry, write it out. */ - if ((entry->state & HFS_DIRTY)) { - entry->state ^= HFS_DIRTY | HFS_LOCK; - write_entry(entry); - entry->state &= ~HFS_LOCK; - } - - list_del(&entry->hash); -entry_deleted: /* deleted entries have already been removed - * from the hash list. */ - list_del(&entry->list); - if (entries_stat.nr_free_entries > CCACHE_MAX) { - HFS_DELETE(entry); - entries_stat.nr_entries--; - } else { - init_entry(entry); - list_add(&entry->list, &entry_unused); - entries_stat.nr_free_entries++; - } - } - spin_unlock(&entry_lock); - } -} - -/* - * hfs_cat_get() - * - * Wrapper for get_entry() which always calls with ('read'==1). - * Used for access to get_entry() from outside this file. - */ -struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *mdb, - const struct hfs_cat_key *key) -{ - return get_entry(mdb, key, 1); -} - -/* invalidate all entries for a device */ -static void invalidate_list(struct list_head *head, struct hfs_mdb *mdb, - struct list_head *dispose) -{ - struct list_head *next; - - next = head->next; - for (;;) { - struct list_head *tmp = next; - struct hfs_cat_entry * entry; - - next = next->next; - if (tmp == head) - break; - entry = list_entry(tmp, struct hfs_cat_entry, list); - if (entry->mdb != mdb) { - continue; - } - - if (!entry->count) { - list_del_init(&entry->hash); - list_del(&entry->list); - list_add(&entry->list, dispose); - continue; - } - - hfs_warn("hfs_fs: entry %p(%u) busy on removed device %s.\n", - entry, entry->count, - hfs_mdb_name(entry->mdb->sys_mdb)); - } -} - -/* delete entries from a list */ -static void delete_list(struct list_head *head) -{ - struct list_head *next = head->next; - struct hfs_cat_entry *entry; - - for (;;) { - struct list_head * tmp = next; - - next = next->next; - if (tmp == head) { - break; - } - entry = list_entry(tmp, struct hfs_cat_entry, list); - HFS_DELETE(entry); - } -} - -/* - * hfs_cat_invalidate() - * - * Called by hfs_mdb_put() to remove all the entries - * in the cache that are associated with a given MDB. - */ -void hfs_cat_invalidate(struct hfs_mdb *mdb) -{ - LIST_HEAD(throw_away); - - spin_lock(&entry_lock); - invalidate_list(&entry_in_use, mdb, &throw_away); - invalidate_list(&mdb->entry_dirty, mdb, &throw_away); - spin_unlock(&entry_lock); - - delete_list(&throw_away); -#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) - hfs_warn("hfs_cat_invalidate: free=%d total=%d\n", - entries_stat.nr_free_entries, - entries_stat.nr_entries); -#endif -} - -/* - * hfs_cat_commit() - * - * Called by hfs_mdb_commit() to write dirty entries to the disk buffers. - */ -void hfs_cat_commit(struct hfs_mdb *mdb) -{ - struct list_head *tmp, *head = &mdb->entry_dirty; - struct hfs_cat_entry *entry; - - spin_lock(&entry_lock); - while ((tmp = head->prev) != head) { - entry = list_entry(tmp, struct hfs_cat_entry, list); - - if ((entry->state & HFS_LOCK)) { - spin_unlock(&entry_lock); - wait_on_entry(entry); - spin_lock(&entry_lock); - } else { - struct list_head *insert = &entry_in_use; - - if (!entry->count) - insert = entry_in_use.prev; - - /* add to in_use list */ - list_del(&entry->list); - list_add(&entry->list, insert); - - /* reset DIRTY, set LOCK */ - entry->state ^= HFS_DIRTY | HFS_LOCK; - spin_unlock(&entry_lock); - write_entry(entry); - spin_lock(&entry_lock); - entry->state &= ~HFS_LOCK; - hfs_wake_up(&entry->wait); - } - } - spin_unlock(&entry_lock); -} - -/* - * hfs_cat_free() - * - * Releases all the memory allocated in grow_entries(). - * Must call hfs_cat_invalidate() on all MDBs before calling this. - * This only gets rid of the unused pool of entries. all the other - * entry references should have either been freed by cat_invalidate - * or moved onto the unused list. - */ -void hfs_cat_free(void) -{ - delete_list(&entry_unused); + return err; } /* @@ -962,223 +148,43 @@ void hfs_cat_free(void) * Postconditions: * This function has no side-effects */ -int hfs_cat_compare(const struct hfs_cat_key *key1, - const struct hfs_cat_key *key2) +int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2) { - unsigned int parents; int retval; - parents = hfs_get_hl(key1->ParID) - hfs_get_hl(key2->ParID); - if (parents != 0) { - retval = (int)parents; - } else { - retval = hfs_strcmp(key1->CName.Name, key1->CName.Len, - key2->CName.Name, key2->CName.Len); - } - return retval; -} - -/* - * hfs_cat_build_key() - * - * Given the ID of the parent and the name build a search key. - */ -void hfs_cat_build_key(hfs_u32 parent, const struct hfs_name *cname, - struct hfs_cat_key *key) -{ - hfs_put_nl(parent, key->ParID); - - if (cname) { - key->KeyLen = 6 + cname->Len; - memcpy(&key->CName, cname, sizeof(*cname)); - } else { - key->KeyLen = 6; - memset(&key->CName, 0, sizeof(*cname)); - } -} - -/* - * hfs_cat_open() - * - * Given a directory on an HFS filesystem get its thread and - * lock the directory against insertions and deletions. - * Return 0 on success or an error code on failure. - */ -int hfs_cat_open(struct hfs_cat_entry *dir, struct hfs_brec *brec) -{ - struct hfs_cat_key key; - int error; - - if (dir->type != HFS_CDR_DIR) - return -EINVAL; - - /* Block writers */ - down_read(&dir->u.dir.sem); - - /* Find the directory */ - hfs_cat_build_key(dir->cnid, NULL, &key); - error = hfs_bfind(brec, dir->mdb->cat_tree, - HFS_BKEY(&key), HFS_BFIND_READ_EQ); - - if (error) - up_read(&dir->u.dir.sem); - - return error; -} - -/* - * hfs_cat_next() - * - * Given a catalog brec structure, replace it with the count'th next brec - * in the same directory. - * Return an error code if there is a problem, 0 if OK. - * Note that an error code of -ENOENT means there are no more entries - * in this directory. - * The directory is "closed" on an error. - */ -int hfs_cat_next(struct hfs_cat_entry *dir, struct hfs_brec *brec, - hfs_u16 count, hfs_u32 *cnid, hfs_u8 *type) -{ - int error; - - if (!dir || !brec) { - return -EINVAL; - } - - /* Get the count'th next catalog tree entry */ - error = hfs_bsucc(brec, count); - if (!error) { - struct hfs_cat_key *key = (struct hfs_cat_key *)brec->key; - if (hfs_get_nl(key->ParID) != dir->cnid) { - hfs_brec_relse(brec, NULL); - error = -ENOENT; - } - } - if (!error) { - *type = ((struct hfs_cat_rec *)brec->data)->cdrType; - *cnid = brec_to_id(brec); - } else { - up_read(&dir->u.dir.sem); - } - return error; -} + retval = be32_to_cpu(key1->cat.ParID) - be32_to_cpu(key2->cat.ParID); + if (!retval) + retval = hfs_strcmp(key1->cat.CName.name, key1->cat.CName.len, + key2->cat.CName.name, key2->cat.CName.len); -/* - * hfs_cat_close() - * - * Given a catalog brec structure, replace it with the count'th next brec - * in the same directory. - * Return an error code if there is a problem, 0 if OK. - * Note that an error code of -ENOENT means there are no more entries - * in this directory. - */ -void hfs_cat_close(struct hfs_cat_entry *dir, struct hfs_brec *brec) -{ - if (dir && brec) { - hfs_brec_relse(brec, NULL); - up_read(&dir->u.dir.sem); - } + return retval; } -/* - * hfs_cat_parent() - * - * Given a catalog entry, return the entry for its parent. - * Uses catalog key for the entry to get its parent's ID - * and then uses the parent's thread record to locate the - * parent's actual catalog entry. - */ -struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry) +/* Try to get a catalog entry for given catalog id */ +// move to read_super??? +int hfs_cat_find_brec(struct super_block *sb, u32 cnid, + struct hfs_find_data *fd) { - struct hfs_cat_entry *retval = NULL; - struct hfs_mdb *mdb = entry->mdb; - struct hfs_brec brec; - struct hfs_cat_key key; - int error; - - lock_entry(entry); - if (!(entry->state & HFS_DELETED)) { - hfs_cat_build_key(hfs_get_nl(entry->key.ParID), NULL, &key); - error = hfs_bfind(&brec, mdb->cat_tree, - HFS_BKEY(&key), HFS_BFIND_READ_EQ); - if (!error) { - /* convert thread record to key */ - struct hfs_cat_rec *rec = brec.data; - key.KeyLen = 6 + rec->u.thd.CName.Len; - memcpy(&key.ParID, &rec->u.thd.ParID, - sizeof(hfs_u32) + sizeof(struct hfs_name)); + hfs_cat_rec rec; + int res, len, type; - hfs_brec_relse(&brec, NULL); + hfs_cat_build_key(fd->search_key, cnid, NULL); + res = hfs_brec_read(fd, &rec, sizeof(rec)); + if (res) + return res; - retval = hfs_cat_get(mdb, &key); - } + type = rec.type; + if (type != HFS_CDR_THD && type != HFS_CDR_FTH) { + printk("HFS-fs: Found bad thread record in catalog\n"); + return -EIO; } - unlock_entry(entry); - return retval; -} - -/* - * hfs_cat_create() - * - * Create a new file with the indicated name in the indicated directory. - * The file will have the indicated flags, type and creator. - * If successful an (struct hfs_cat_entry) is returned in '*result'. - */ -int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key, - hfs_u8 flags, hfs_u32 type, hfs_u32 creator, - struct hfs_cat_entry **result) -{ - struct hfs_cat_rec record; - hfs_u32 id = new_cnid(parent->mdb); - hfs_u32 mtime = hfs_time(); - -#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) - hfs_warn("hfs_cat_create: %p/%s flags=%d res=%p\n", - parent, key->CName.Name, flags, result); -#endif - /* init some fields for the file record */ - memset(&record, 0, sizeof(record)); - record.cdrType = HFS_CDR_FIL; - record.u.fil.Flags = flags | HFS_FIL_USED; - hfs_put_nl(id, record.u.fil.FlNum); - hfs_put_nl(mtime, record.u.fil.CrDat); - hfs_put_nl(mtime, record.u.fil.MdDat); - hfs_put_nl(0, record.u.fil.BkDat); - hfs_put_nl(type, record.u.fil.UsrWds.fdType); - hfs_put_nl(creator, record.u.fil.UsrWds.fdCreator); - return create_entry(parent, key, &record, 0, id, result); + fd->search_key->cat.ParID = rec.thread.ParID; + len = fd->search_key->cat.CName.len = rec.thread.CName.len; + memcpy(fd->search_key->cat.CName.name, rec.thread.CName.name, len); + return hfs_brec_find(fd); } -/* - * hfs_cat_mkdir() - * - * Create a new directory with the indicated name in the indicated directory. - * If successful an (struct hfs_cat_entry) is returned in '*result'. - */ -int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key, - struct hfs_cat_entry **result) -{ - struct hfs_cat_rec record; - hfs_u32 id = new_cnid(parent->mdb); - hfs_u32 mtime = hfs_time(); - -#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) - hfs_warn("hfs_cat_mkdir: %p/%s res=%p\n", parent, key->CName.Name, - result); -#endif - - /* init some fields for the directory record */ - memset(&record, 0, sizeof(record)); - record.cdrType = HFS_CDR_DIR; - hfs_put_nl(id, record.u.dir.DirID); - hfs_put_nl(mtime, record.u.dir.CrDat); - hfs_put_nl(mtime, record.u.dir.MdDat); - hfs_put_nl(0, record.u.dir.BkDat); - hfs_put_hs(0xff, record.u.dir.UsrInfo.frView); - - return create_entry(parent, key, &record, 1, id, result); -} /* * hfs_cat_delete() @@ -1186,72 +192,60 @@ int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key, * Delete the indicated file or directory. * The associated thread is also removed unless ('with_thread'==0). */ -int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry, - int with_thread) -{ - struct hfs_cat_key key; - struct hfs_mdb *mdb = parent->mdb; - int is_dir, error = 0; - -#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) - hfs_warn("hfs_cat_delete: %p/%p type=%d state=%lu, thread=%d\n", - parent, entry, entry->type, entry->state, with_thread); +int hfs_cat_delete(u32 cnid, struct inode *dir, struct qstr *str) +{ + struct super_block *sb; + struct hfs_find_data fd; + struct list_head *pos; + int res, type; + + dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid); + sb = dir->i_sb; + hfs_find_init(HFS_SB(sb)->cat_tree, &fd); + + hfs_cat_build_key(fd.search_key, dir->i_ino, str); + res = hfs_brec_find(&fd); + if (res) + goto out; + + type = hfs_bnode_read_u8(fd.bnode, fd.entryoffset); + if (type == HFS_CDR_FIL) { + struct hfs_cat_file file; + hfs_bnode_read(fd.bnode, &file, fd.entryoffset, sizeof(file)); + if (be32_to_cpu(file.FlNum) == cnid) { +#if 0 + hfs_free_fork(sb, &file, HFS_FK_DATA); #endif - if (parent->mdb != entry->mdb) { - return -EINVAL; - } - - if (entry->type == HFS_CDR_FIL) { - with_thread = (entry->u.file.flags&HFS_FIL_THD) && with_thread; - is_dir = 0; - } else { - is_dir = 1; - } - - /* keep readers from getting confused by changing dir size */ - down_write(&parent->u.dir.sem); - - /* don't delete a busy directory */ - if (entry->type == HFS_CDR_DIR) { - down_read(&entry->u.dir.sem); - - error = -ENOTEMPTY; - if (entry->u.dir.files || entry->u.dir.dirs) - goto hfs_delete_end; + hfs_free_fork(sb, &file, HFS_FK_RSRC); + } } - /* try to delete the file or directory */ - lock_entry(entry); - error = -ENOENT; - if ((entry->state & HFS_DELETED)) { - /* somebody beat us to it. */ - goto hfs_delete_unlock; - } - - /* delete the catalog record */ - if ((error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)))) { - goto hfs_delete_unlock; + list_for_each(pos, &HFS_I(dir)->open_dir_list) { + struct hfs_readdir_data *rd = + list_entry(pos, struct hfs_readdir_data, list); + if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) + rd->file->f_pos--; } - /* Mark the entry deleted and remove it from the cache */ - delete_entry(entry); + res = hfs_brec_remove(&fd); + if (res) + goto out; - /* try to delete the thread entry if it exists */ - if (with_thread) { - hfs_cat_build_key(entry->cnid, NULL, &key); - (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key)); + hfs_cat_build_key(fd.search_key, cnid, NULL); + res = hfs_brec_find(&fd); + if (!res) { + res = hfs_brec_remove(&fd); + if (res) + goto out; } - - update_dir(mdb, parent, is_dir, -1); -hfs_delete_unlock: - unlock_entry(entry); + dir->i_size--; + mark_inode_dirty(dir); + res = 0; +out: + hfs_find_exit(&fd); -hfs_delete_end: - if (entry->type == HFS_CDR_DIR) - up_read(&entry->u.dir.sem); - up_write(&parent->u.dir.sem); - return error; + return res; } /* @@ -1261,267 +255,82 @@ hfs_delete_end: * If the destination exists it is removed and a * (struct hfs_cat_entry) for it is returned in '*result'. */ -int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir, - struct hfs_cat_entry *entry, struct hfs_cat_key *new_key, - struct hfs_cat_entry **removed) -{ - struct hfs_cat_entry *dest; - struct hfs_mdb *mdb; - int error = 0; - int is_dir, has_thread; - - if (removed) { - *removed = NULL; - } - - /* sanity checks */ - if (!old_dir || !new_dir) { - return -EINVAL; - } - mdb = old_dir->mdb; - if (mdb != new_dir->mdb) { - return -EXDEV; - } - - /* precompute a few things */ - if (entry->type == HFS_CDR_DIR) { - is_dir = 1; - has_thread = 1; - } else if (entry->type == HFS_CDR_FIL) { - is_dir = 0; - has_thread = entry->u.file.flags & HFS_FIL_THD; - } else { - return -EINVAL; - } - - /* keep readers from getting confused by changing dir size */ - down_write(&new_dir->u.dir.sem); - /* AV: smells like a deadlock */ - if (old_dir != new_dir) - down_write(&old_dir->u.dir.sem); - - /* Don't move a directory inside itself */ - if (is_dir) { - struct hfs_cat_key thd_key; - struct hfs_brec brec; - - hfs_u32 id = new_dir->cnid; - while (id != htonl(HFS_ROOT_CNID)) { - if (id == entry->cnid) { - error = -EINVAL; - } else { - hfs_cat_build_key(id, NULL, &thd_key); - error = hfs_bfind(&brec, mdb->cat_tree, - HFS_BKEY(&thd_key), - HFS_BFIND_READ_EQ); - } - if (error) { - goto done; - } else { - struct hfs_cat_rec *rec = brec.data; - id = hfs_get_nl(rec->u.thd.ParID); - hfs_brec_relse(&brec, NULL); - } - } - } - -restart: - /* see if the destination exists, getting it if it does */ - dest = hfs_cat_get(mdb, new_key); - if (!dest) { - /* destination doesn't exist, so create it */ - struct hfs_cat_rec new_record; - - /* create a locked entry in the cache */ - dest = get_entry(mdb, new_key, 0); - if (!dest) { - error = -EIO; - goto done; - } - if (dest->cnid) { - /* The (unlocked) entry exists in the cache */ - goto have_distinct; - } - - /* limit directory valence to signed 16-bit integer */ - if ((new_dir->u.dir.dirs + new_dir->u.dir.files) >= - HFS_MAX_VALENCE) { - error = -ENOSPC; - goto bail3; - } - - /* build the new record. make sure to zero out the - record. */ - memset(&new_record, 0, sizeof(new_record)); - new_record.cdrType = entry->type; - __write_entry(entry, &new_record); - - /* insert the new record */ - error = hfs_binsert(mdb->cat_tree, HFS_BKEY(new_key), - &new_record, is_dir ? 2 + sizeof(DIR_REC) : - 2 + sizeof(FIL_REC)); - if (error == -EEXIST) { - delete_entry(dest); - unlock_entry(dest); - hfs_cat_put(dest); - goto restart; - } else if (error) { - goto bail3; - } - - /* update the destination directory */ - update_dir(mdb, new_dir, is_dir, 1); - } else if (entry != dest) { -have_distinct: - /* The destination exists and is not same as source */ - lock_entry(dest); - if ((dest->state & HFS_DELETED)) { - unlock_entry(dest); - hfs_cat_put(dest); - goto restart; - } - if (dest->type != entry->type) { - /* can't move a file on top - of a dir nor vice versa. */ - error = is_dir ? -ENOTDIR : -EISDIR; - } else if (is_dir && (dest->u.dir.dirs || dest->u.dir.files)) { - /* directory to replace is not empty */ - error = -ENOTEMPTY; - } - - if (error) { - goto bail2; - } - } else { - /* The destination exists but is same as source */ - --entry->count; - dest = NULL; - } - - /* lock the entry */ - lock_entry(entry); - if ((entry->state & HFS_DELETED)) { - error = -ENOENT; - goto bail1; - } - - if (dest) { - /* remove the old entry */ - error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)); - - if (error) { - /* We couldn't remove the entry for the - original file, so nothing has changed. */ - goto bail1; - } - update_dir(mdb, old_dir, is_dir, -1); - } - - /* update the thread of the dir/file we're moving */ - if (has_thread) { - struct hfs_cat_key thd_key; - struct hfs_brec brec; - - hfs_cat_build_key(entry->cnid, NULL, &thd_key); - error = hfs_bfind(&brec, mdb->cat_tree, - HFS_BKEY(&thd_key), HFS_BFIND_WRITE); - if (error == -ENOENT) { - if (is_dir) { - /* directory w/o a thread! */ - error = -EIO; - } else { - /* We were lied to! */ - entry->u.file.flags &= ~HFS_FIL_THD; - hfs_cat_mark_dirty(entry); - } - } - if (!error) { - struct hfs_cat_rec *rec = brec.data; - memcpy(&rec->u.thd.ParID, &new_key->ParID, - sizeof(hfs_u32) + sizeof(struct hfs_name)); - hfs_brec_relse(&brec, NULL); - } else if (error == -ENOENT) { - error = 0; - } else if (!dest) { - /* Nothing was changed */ - unlock_entry(entry); - goto done; - } else { - /* Something went seriously wrong. - The dir/file has been deleted. */ - /* XXX try some recovery? */ - delete_entry(entry); - goto bail1; - } - } - - /* TRY to remove the thread for the pre-existing entry */ - if (dest && dest->cnid && - (is_dir || (dest->u.file.flags & HFS_FIL_THD))) { - struct hfs_cat_key thd_key; - - hfs_cat_build_key(dest->cnid, NULL, &thd_key); - (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key)); - } - - /* update directories */ - new_dir->modify_date = hfs_time(); - hfs_cat_mark_dirty(new_dir); - - /* update key */ - remove_hash(entry); - memcpy(&entry->key, new_key, sizeof(*new_key)); - /* KEYDIRTY as case might differ */ - entry->state |= HFS_KEYDIRTY; - insert_hash(entry); - hfs_cat_mark_dirty(entry); - unlock_entry(entry); - - /* delete any pre-existing or place-holder entry */ - if (dest) { - delete_entry(dest); - unlock_entry(dest); - if (removed && dest->cnid) { - *removed = dest; - } else { - hfs_cat_put(dest); - } - } - goto done; - -bail1: - unlock_entry(entry); -bail2: - if (dest) { - if (!dest->cnid) { - /* TRY to remove the new entry */ - (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key)); - update_dir(mdb, new_dir, is_dir, -1); -bail3: - delete_entry(dest); - } - unlock_entry(dest); - hfs_cat_put(dest); - } -done: - if (new_dir != old_dir) - up_write(&old_dir->u.dir.sem); - up_write(&new_dir->u.dir.sem); - return error; -} - -/* - * Initialize the hash tables - */ -void hfs_cat_init(void) -{ - int i; - struct list_head *head = hash_table; - - i = C_HASHSIZE; - do { - INIT_LIST_HEAD(head); - head++; - i--; - } while (i); +int hfs_cat_move(u32 cnid, struct inode *src_dir, struct qstr *src_name, + struct inode *dst_dir, struct qstr *dst_name) +{ + struct super_block *sb; + struct hfs_find_data src_fd, dst_fd; + union hfs_cat_rec entry; + int entry_size, type; + int err; + + dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name, + dst_dir->i_ino, dst_name->name); + sb = src_dir->i_sb; + hfs_find_init(HFS_SB(sb)->cat_tree, &src_fd); + dst_fd = src_fd; + + /* find the old dir entry and read the data */ + hfs_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); + err = hfs_brec_find(&src_fd); + if (err) + goto out; + + hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset, + src_fd.entrylength); + + /* create new dir entry with the data from the old entry */ + hfs_cat_build_key(dst_fd.search_key, dst_dir->i_ino, dst_name); + err = hfs_brec_find(&dst_fd); + if (err != -ENOENT) { + if (!err) + err = -EEXIST; + goto out; + } + + err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength); + if (err) + goto out; + dst_dir->i_size++; + mark_inode_dirty(dst_dir); + + /* finally remove the old entry */ + hfs_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); + err = hfs_brec_find(&src_fd); + if (err) + goto out; + err = hfs_brec_remove(&src_fd); + if (err) + goto out; + src_dir->i_size--; + mark_inode_dirty(src_dir); + + type = entry.type; + if (type == HFS_CDR_FIL && !(entry.file.Flags & HFS_FIL_THD)) + goto out; + + /* remove old thread entry */ + hfs_cat_build_key(src_fd.search_key, cnid, NULL); + err = hfs_brec_find(&src_fd); + if (err) + goto out; + err = hfs_brec_remove(&src_fd); + if (err) + goto out; + + /* create new thread entry */ + hfs_cat_build_key(dst_fd.search_key, cnid, NULL); + entry_size = hfs_cat_build_thread(&entry, type == HFS_CDR_FIL ? HFS_CDR_FTH : HFS_CDR_THD, + dst_dir->i_ino, dst_name); + err = hfs_brec_find(&dst_fd); + if (err != -ENOENT) { + if (!err) + err = -EEXIST; + goto out; + } + err = hfs_brec_insert(&dst_fd, &entry, entry_size); +out: + hfs_bnode_put(dst_fd.bnode); + hfs_find_exit(&src_fd); + return err; } diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index 40df8a2b116c..6106ab6e34cd 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -1,160 +1,174 @@ /* - * linux/fs/hfs/dir.c + * linux/fs/hfs/dir.c * * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * This file contains directory-related functions independent of which * scheme is being used to represent forks. * * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. */ -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> -#include <linux/smp_lock.h> - -/*================ File-local functions ================*/ +#include "hfs_fs.h" +#include "btree.h" /* - * build_key() - * - * Build a key for a file by the given name in the given directory. - * If the name matches one of the reserved names returns 1 otherwise 0. + * hfs_lookup() */ -static int build_key(struct hfs_cat_key *key, struct inode *dir, - const char *name, int len) +struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) { - struct hfs_name cname; - const struct hfs_name *reserved; - - /* mangle the name */ - hfs_nameout(dir, &cname, name, len); - - /* check against reserved names */ - reserved = HFS_SB(dir->i_sb)->s_reserved1; - while (reserved->Len) { - if (hfs_streq(reserved->Name, reserved->Len, - cname.Name, cname.Len)) { - return 1; - } - ++reserved; - } - - /* check against the names reserved only in the root directory */ - if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) { - reserved = HFS_SB(dir->i_sb)->s_reserved2; - while (reserved->Len) { - if (hfs_streq(reserved->Name, reserved->Len, - cname.Name, cname.Len)) { - return 1; - } - ++reserved; + hfs_cat_rec rec; + struct hfs_find_data fd; + struct inode *inode = NULL; + int res; + + dentry->d_op = &hfs_dentry_operations; + + hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd); + hfs_cat_build_key(fd.search_key, dir->i_ino, &dentry->d_name); + res = hfs_brec_read(&fd, &rec, sizeof(rec)); + if (res) { + hfs_find_exit(&fd); + if (res == -ENOENT) { + /* No such entry */ + inode = NULL; + goto done; } + return ERR_PTR(res); } - - /* build the key */ - hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key); - - return 0; + inode = hfs_iget(dir->i_sb, &fd.search_key->cat, &rec); + hfs_find_exit(&fd); + if (!inode) + return ERR_PTR(-EACCES); +done: + d_add(dentry, inode); + return NULL; } /* - * update_dirs_plus() - * - * Update the fields 'i_size', 'i_nlink', 'i_ctime' and 'i_mtime' - * of the inodes associated with a directory that has - * had a file ('is_dir'==0) or directory ('is_dir'!=0) added to it. + * hfs_readdir */ -static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir) +int hfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { - int i; - - for (i = 0; i < 4; ++i) { - struct dentry *de = dir->sys_entry[i]; - if (de) { - struct inode *tmp = de->d_inode; - if (S_ISDIR(tmp->i_mode)) { - if (is_dir && - (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) { - /* In "normal" directory only */ - ++(tmp->i_nlink); - } - tmp->i_size += HFS_I(tmp)->dir_size; - } - tmp->i_ctime = tmp->i_mtime = CURRENT_TIME; - mark_inode_dirty(tmp); + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; + int len, err; + char strbuf[HFS_NAMELEN + 1]; + union hfs_cat_rec entry; + struct hfs_find_data fd; + struct hfs_readdir_data *rd; + u16 type; + + if (filp->f_pos >= inode->i_size) + return 0; + + hfs_find_init(HFS_SB(sb)->cat_tree, &fd); + hfs_cat_build_key(fd.search_key, inode->i_ino, NULL); + err = hfs_brec_find(&fd); + if (err) + goto out; + + switch ((u32)filp->f_pos) { + case 0: + /* This is completely artificial... */ + if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR)) + goto out; + filp->f_pos++; + /* fall through */ + case 1: + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); + if (entry.type != HFS_CDR_THD) { + printk("HFS: bad catalog folder thread\n"); + err = -EIO; + goto out; } + //if (fd.entrylength < HFS_MIN_THREAD_SZ) { + // printk("HFS: truncated catalog thread\n"); + // err = -EIO; + // goto out; + //} + if (filldir(dirent, "..", 2, 1, + be32_to_cpu(entry.thread.ParID), DT_DIR)) + goto out; + filp->f_pos++; + /* fall through */ + default: + if (filp->f_pos >= inode->i_size) + goto out; + err = hfs_brec_goto(&fd, filp->f_pos - 1); + if (err) + goto out; } -} - -/* - * update_dirs_minus() - * - * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and - * of the inodes associated with a directory that has - * had a file ('is_dir'==0) or directory ('is_dir'!=0) removed. - */ -static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir) -{ - int i; - for (i = 0; i < 4; ++i) { - struct dentry *de = dir->sys_entry[i]; - if (de) { - struct inode *tmp = de->d_inode; - if (S_ISDIR(tmp->i_mode)) { - if (is_dir && - (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) { - /* In "normal" directory only */ - --(tmp->i_nlink); - } - tmp->i_size -= HFS_I(tmp)->dir_size; + for (;;) { + if (be32_to_cpu(fd.key->cat.ParID) != inode->i_ino) { + printk("HFS: walked past end of dir\n"); + err = -EIO; + goto out; + } + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); + type = entry.type; + len = hfs_mac2triv(strbuf, &fd.key->cat.CName); + if (type == HFS_CDR_DIR) { + if (fd.entrylength < sizeof(struct hfs_cat_dir)) { + printk("HFS: small dir entry\n"); + err = -EIO; + goto out; } - tmp->i_ctime = tmp->i_mtime = CURRENT_TIME; - mark_inode_dirty(tmp); + if (filldir(dirent, strbuf, len, filp->f_pos, + be32_to_cpu(entry.dir.DirID), DT_DIR)) + break; + } else if (type == HFS_CDR_FIL) { + if (fd.entrylength < sizeof(struct hfs_cat_file)) { + printk("HFS: small file entry\n"); + err = -EIO; + goto out; + } + if (filldir(dirent, strbuf, len, filp->f_pos, + be32_to_cpu(entry.file.FlNum), DT_REG)) + break; + } else { + printk("HFS: bad catalog entry type %d\n", type); + err = -EIO; + goto out; } + filp->f_pos++; + if (filp->f_pos >= inode->i_size) + goto out; + err = hfs_brec_goto(&fd, 1); + if (err) + goto out; } + rd = filp->private_data; + if (!rd) { + rd = kmalloc(sizeof(struct hfs_readdir_data), GFP_KERNEL); + if (!rd) { + err = -ENOMEM; + goto out; + } + filp->private_data = rd; + rd->file = filp; + list_add(&rd->list, &HFS_I(inode)->open_dir_list); + } + memcpy(&rd->key, &fd.key, sizeof(struct hfs_cat_key)); +out: + hfs_find_exit(&fd); + return err; } -/* - * mark_inodes_deleted() - * - * Update inodes associated with a deleted entry to reflect its deletion. - * Well, we really just drop the dentry. - * - * XXX: we should be using delete_inode for some of this stuff. - */ -static inline void mark_inodes_deleted(struct hfs_cat_entry *entry, - struct dentry *dentry) +static int hfs_dir_release(struct inode *inode, struct file *file) { - struct dentry *de; - struct inode *tmp; - int i; - - for (i = 0; i < 4; ++i) { - if ((de = entry->sys_entry[i]) && (dentry != de)) { - dget(de); - tmp = de->d_inode; - tmp->i_nlink = 0; - tmp->i_ctime = CURRENT_TIME; - mark_inode_dirty(tmp); - d_delete(de); - dput(de); - } + struct hfs_readdir_data *rd = file->private_data; + if (rd) { + list_del(&rd->list); + kfree(rd); } + return 0; } -/*================ Global functions ================*/ - /* * hfs_create() * @@ -163,47 +177,25 @@ static inline void mark_inodes_deleted(struct hfs_cat_entry *entry, * a directory and return a corresponding inode, given the inode for * the directory and the name (and its length) of the new file. */ -int hfs_create(struct inode * dir, struct dentry *dentry, int mode, struct nameidata *nd) +int hfs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) { - struct hfs_cat_entry *entry = HFS_I(dir)->entry; - struct hfs_cat_entry *new; - struct hfs_cat_key key; struct inode *inode; - int error; - - lock_kernel(); - /* build the key, checking against reserved names */ - if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) { - unlock_kernel(); - return -EEXIST; - } - - if ((error = hfs_cat_create(entry, &key, - (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK, - HFS_SB(dir->i_sb)->s_type, - HFS_SB(dir->i_sb)->s_creator, &new))) { - unlock_kernel(); - return error; - } - - /* create an inode for the new file. back out if we run - * into trouble. */ - new->count++; /* hfs_iget() eats one */ - if (!(inode = hfs_iget(new, HFS_I(dir)->file_type, dentry))) { - hfs_cat_delete(entry, new, 1); - hfs_cat_put(new); - unlock_kernel(); - return -EIO; + int res; + + inode = hfs_new_inode(dir, &dentry->d_name, mode); + if (!inode) + return -ENOSPC; + + res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode); + if (res) { + inode->i_nlink = 0; + hfs_delete_inode(inode); + iput(inode); + return res; } - - hfs_cat_put(new); - update_dirs_plus(entry, 0); - /* toss any relevant negative dentries */ - if (HFS_I(dir)->d_drop_op) - HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type); - mark_inode_dirty(inode); - unlock_kernel(); d_instantiate(dentry, inode); + mark_inode_dirty(inode); return 0; } @@ -215,43 +207,24 @@ int hfs_create(struct inode * dir, struct dentry *dentry, int mode, struct namei * in a directory, given the inode for the parent directory and the * name (and its length) of the new directory. */ -int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode) +int hfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { - struct hfs_cat_entry *entry; - struct hfs_cat_entry *new; - struct hfs_cat_key key; struct inode *inode; - int error; - - lock_kernel(); - entry = HFS_I(parent)->entry; - /* build the key, checking against reserved names */ - if (build_key(&key, parent, dentry->d_name.name, - dentry->d_name.len)) { - unlock_kernel(); - return -EEXIST; - } - - /* try to create the directory */ - if ((error = hfs_cat_mkdir(entry, &key, &new))) { - unlock_kernel(); - return error; - } - - /* back out if we run into trouble */ - new->count++; /* hfs_iget eats one */ - if (!(inode = hfs_iget(new, HFS_I(parent)->file_type, dentry))) { - hfs_cat_delete(entry, new, 1); - hfs_cat_put(new); - unlock_kernel(); - return -EIO; + int res; + + inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode); + if (!inode) + return -ENOSPC; + + res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode); + if (res) { + inode->i_nlink = 0; + hfs_delete_inode(inode); + iput(inode); + return res; } - - hfs_cat_put(new); - update_dirs_plus(entry, 1); - mark_inode_dirty(inode); d_instantiate(dentry, inode); - unlock_kernel(); + mark_inode_dirty(inode); return 0; } @@ -263,44 +236,22 @@ int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode) * file, given the inode for the parent directory and the name * (and its length) of the existing file. */ -int hfs_unlink(struct inode * dir, struct dentry *dentry) +int hfs_unlink(struct inode *dir, struct dentry *dentry) { - struct hfs_cat_entry *entry = HFS_I(dir)->entry; - struct hfs_cat_entry *victim = NULL; - struct hfs_cat_key key; - int error; - - lock_kernel(); - entry = HFS_I(dir)->entry; - if (build_key(&key, dir, dentry->d_name.name, - dentry->d_name.len)) { - unlock_kernel(); - return -EPERM; - } - - if (!(victim = hfs_cat_get(entry->mdb, &key))) { - unlock_kernel(); - return -ENOENT; - } - - error = -EPERM; - if (victim->type != HFS_CDR_FIL) - goto hfs_unlink_put; + struct inode *inode; + int res; - if (!(error = hfs_cat_delete(entry, victim, 1))) { - struct inode *inode = dentry->d_inode; + inode = dentry->d_inode; + res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); + if (res) + return res; - mark_inodes_deleted(victim, dentry); - inode->i_nlink--; - inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); - update_dirs_minus(entry, 0); - } + inode->i_nlink--; + hfs_delete_inode(inode); + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); -hfs_unlink_put: - hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ - unlock_kernel(); - return error; + return res; } /* @@ -311,55 +262,22 @@ hfs_unlink_put: * directory, given the inode for the parent directory and the name * (and its length) of the existing directory. */ -int hfs_rmdir(struct inode * parent, struct dentry *dentry) +int hfs_rmdir(struct inode *dir, struct dentry *dentry) { - struct hfs_cat_entry *entry; - struct hfs_cat_entry *victim = NULL; - struct inode *inode = dentry->d_inode; - struct hfs_cat_key key; - int error; - - lock_kernel(); - entry = HFS_I(parent)->entry; - if (build_key(&key, parent, dentry->d_name.name, - dentry->d_name.len)) { - unlock_kernel(); - return -EPERM; - } - - if (!(victim = hfs_cat_get(entry->mdb, &key))) { - unlock_kernel(); - return -ENOENT; - } - - error = -ENOTDIR; - if (victim->type != HFS_CDR_DIR) - goto hfs_rmdir_put; - - error = -EBUSY; - if (!d_unhashed(dentry)) - goto hfs_rmdir_put; - - /* we only have to worry about 2 and 3 for mount points */ - if (victim->sys_entry[2] && d_mountpoint(victim->sys_entry[2])) - goto hfs_rmdir_put; - if (victim->sys_entry[3] && d_mountpoint(victim->sys_entry[3])) - goto hfs_rmdir_put; - - - if ((error = hfs_cat_delete(entry, victim, 1))) - goto hfs_rmdir_put; - - mark_inodes_deleted(victim, dentry); + struct inode *inode; + int res; + + inode = dentry->d_inode; + if (inode->i_size != 2) + return -ENOTEMPTY; + res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); + if (res) + return res; inode->i_nlink = 0; inode->i_ctime = CURRENT_TIME; + hfs_delete_inode(inode); mark_inode_dirty(inode); - update_dirs_minus(entry, 1); - -hfs_rmdir_put: - hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ - unlock_kernel(); - return error; + return 0; } /* @@ -376,55 +294,34 @@ hfs_rmdir_put: int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - struct hfs_cat_entry *old_parent; - struct hfs_cat_entry *new_parent; - struct hfs_cat_entry *victim = NULL; - struct hfs_cat_entry *deleted; - struct hfs_cat_key key; - int error; - - lock_kernel(); - old_parent = HFS_I(old_dir)->entry; - new_parent = HFS_I(new_dir)->entry; - if (build_key(&key, old_dir, old_dentry->d_name.name, - old_dentry->d_name.len) || - (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) { - unlock_kernel(); - return -EPERM; - } + int res; - if (!(victim = hfs_cat_get(old_parent->mdb, &key))) { - unlock_kernel(); - return -ENOENT; + /* Unlink destination if it already exists */ + if (new_dentry->d_inode) { + res = hfs_unlink(new_dir, new_dentry); + if (res) + return res; } - error = -EPERM; - if (build_key(&key, new_dir, new_dentry->d_name.name, - new_dentry->d_name.len)) - goto hfs_rename_put; - - if (!(error = hfs_cat_move(old_parent, new_parent, - victim, &key, &deleted))) { - int is_dir = (victim->type == HFS_CDR_DIR); - - /* drop the old dentries */ - mark_inodes_deleted(victim, old_dentry); - update_dirs_minus(old_parent, is_dir); - if (deleted) { - mark_inodes_deleted(deleted, new_dentry); - hfs_cat_put(deleted); - } else { - /* no existing inodes. just drop negative dentries */ - if (HFS_I(new_dir)->d_drop_op) - HFS_I(new_dir)->d_drop_op(new_dentry, - HFS_I(new_dir)->file_type); - update_dirs_plus(new_parent, is_dir); - } - - } - -hfs_rename_put: - hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ - unlock_kernel(); - return error; + res = hfs_cat_move(old_dentry->d_inode->i_ino, + old_dir, &old_dentry->d_name, + new_dir, &new_dentry->d_name); + return res; } + +struct file_operations hfs_dir_operations = { + .read = generic_read_dir, + .readdir = hfs_readdir, + .llseek = generic_file_llseek, + .release = hfs_dir_release, +}; + +struct inode_operations hfs_dir_inode_operations = { + .create = hfs_create, + .lookup = hfs_lookup, + .unlink = hfs_unlink, + .mkdir = hfs_mkdir, + .rmdir = hfs_rmdir, + .rename = hfs_rename, + .setattr = hfs_inode_setattr, +}; diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c deleted file mode 100644 index 62bbda0a6311..000000000000 --- a/fs/hfs/dir_cap.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the inode_operations and file_operations - * structures for HFS directories under the CAP scheme. - * - * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds - * - * The source code distribution of the Columbia AppleTalk Package for - * UNIX, version 6.0, (CAP) was used as a specification of the - * location and format of files used by CAP's Aufs. No code from CAP - * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in - * the sense of intellectual property law. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> -#include <linux/smp_lock.h> - -/*================ Forward declarations ================*/ - -static struct dentry *cap_lookup(struct inode *, struct dentry *, struct nameidata *); -static int cap_readdir(struct file *, void *, filldir_t); - -/*================ Global variables ================*/ - -#define DOT_LEN 1 -#define DOT_DOT_LEN 2 -#define DOT_RESOURCE_LEN 9 -#define DOT_FINDERINFO_LEN 11 -#define DOT_ROOTINFO_LEN 9 - -const struct hfs_name hfs_cap_reserved1[] = { - {DOT_LEN, "."}, - {DOT_DOT_LEN, ".."}, - {DOT_RESOURCE_LEN, ".resource"}, - {DOT_FINDERINFO_LEN, ".finderinfo"}, - {0, ""}, -}; - -const struct hfs_name hfs_cap_reserved2[] = { - {DOT_ROOTINFO_LEN, ".rootinfo"}, - {0, ""}, -}; - -#define DOT (&hfs_cap_reserved1[0]) -#define DOT_DOT (&hfs_cap_reserved1[1]) -#define DOT_RESOURCE (&hfs_cap_reserved1[2]) -#define DOT_FINDERINFO (&hfs_cap_reserved1[3]) -#define DOT_ROOTINFO (&hfs_cap_reserved2[0]) - -struct file_operations hfs_cap_dir_operations = { - .read = generic_read_dir, - .readdir = cap_readdir, - .fsync = file_fsync, -}; - -struct inode_operations hfs_cap_ndir_inode_operations = { - .create = hfs_create, - .lookup = cap_lookup, - .unlink = hfs_unlink, - .mkdir = hfs_mkdir, - .rmdir = hfs_rmdir, - .rename = hfs_rename, - .setattr = hfs_notify_change, -}; - -struct inode_operations hfs_cap_fdir_inode_operations = { - .lookup = cap_lookup, - .setattr = hfs_notify_change, -}; - -struct inode_operations hfs_cap_rdir_inode_operations = { - .create = hfs_create, - .lookup = cap_lookup, - .setattr = hfs_notify_change, -}; - -/*================ File-local functions ================*/ - -/* - * cap_lookup() - * - * This is the lookup() entry in the inode_operations structure for - * HFS directories in the CAP scheme. The purpose is to generate the - * inode corresponding to an entry in a directory, given the inode for - * the directory and the name (and its length) of the entry. - */ -static struct dentry *cap_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) -{ - ino_t dtype; - struct hfs_name cname; - struct hfs_cat_entry *entry; - struct hfs_cat_key key; - struct inode *inode = NULL; - - lock_kernel(); - - dentry->d_op = &hfs_dentry_operations; - entry = HFS_I(dir)->entry; - dtype = HFS_ITYPE(dir->i_ino); - - /* Perform name-mangling */ - hfs_nameout(dir, &cname, dentry->d_name.name, - dentry->d_name.len); - - /* no need to check for "." or ".." */ - - /* Check for epecial directories if in a normal directory. - Note that cap_dupdir() does an iput(dir). */ - if (dtype==HFS_CAP_NDIR) { - /* Check for ".resource", ".finderinfo" and ".rootinfo" */ - if (hfs_streq(cname.Name, cname.Len, - DOT_RESOURCE->Name, DOT_RESOURCE_LEN)) { - ++entry->count; /* __hfs_iget() eats one */ - inode = hfs_iget(entry, HFS_CAP_RDIR, dentry); - goto done; - } else if (hfs_streq(cname.Name, cname.Len, - DOT_FINDERINFO->Name, - DOT_FINDERINFO_LEN)) { - ++entry->count; /* __hfs_iget() eats one */ - inode = hfs_iget(entry, HFS_CAP_FDIR, dentry); - goto done; - } else if ((entry->cnid == htonl(HFS_ROOT_CNID)) && - hfs_streq(cname.Name, cname.Len, - DOT_ROOTINFO->Name, DOT_ROOTINFO_LEN)) { - ++entry->count; /* __hfs_iget() eats one */ - inode = hfs_iget(entry, HFS_CAP_FNDR, dentry); - goto done; - } - } - - /* Do an hfs_iget() on the mangled name. */ - hfs_cat_build_key(entry->cnid, &cname, &key); - inode = hfs_iget(hfs_cat_get(entry->mdb, &key), - HFS_I(dir)->file_type, dentry); - - /* Don't return a resource fork for a directory */ - if (inode && (dtype == HFS_CAP_RDIR) && - (HFS_I(inode)->entry->type == HFS_CDR_DIR)) { - iput(inode); /* this does an hfs_cat_put */ - inode = NULL; - } - -done: - unlock_kernel(); - d_add(dentry, inode); - return NULL; -} - -/* - * cap_readdir() - * - * This is the readdir() entry in the file_operations structure for - * HFS directories in the CAP scheme. The purpose is to enumerate the - * entries in a directory, given the inode of the directory and a - * (struct file *), the 'f_pos' field of which indicates the location - * in the directory. The (struct file *) is updated so that the next - * call with the same 'dir' and 'filp' arguments will produce the next - * directory entry. The entries are returned in 'dirent', which is - * "filled-in" by calling filldir(). This allows the same readdir() - * function be used for different dirent formats. We try to read in - * as many entries as we can before filldir() refuses to take any more. - * - * XXX: In the future it may be a good idea to consider not generating - * metadata files for covered directories since the data doesn't - * correspond to the mounted directory. However this requires an - * iget() for every directory which could be considered an excessive - * amount of overhead. Since the inode for a mount point is always - * in-core this is another argument for a call to get an inode if it - * is in-core or NULL if it is not. - */ -static int cap_readdir(struct file * filp, - void * dirent, filldir_t filldir) -{ - ino_t type; - int skip_dirs; - struct hfs_brec brec; - struct hfs_cat_entry *entry; - struct inode *dir = filp->f_dentry->d_inode; - - lock_kernel(); - - entry = HFS_I(dir)->entry; - type = HFS_ITYPE(dir->i_ino); - skip_dirs = (type == HFS_CAP_RDIR); - - if (filp->f_pos == 0) { - /* Entry 0 is for "." */ - if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino, DT_DIR)) { - goto out; - } - filp->f_pos = 1; - } - - if (filp->f_pos == 1) { - /* Entry 1 is for ".." */ - hfs_u32 cnid; - - if (type == HFS_CAP_NDIR) { - cnid = hfs_get_nl(entry->key.ParID); - } else { - cnid = entry->cnid; - } - - if (filldir(dirent, DOT_DOT->Name, - DOT_DOT_LEN, 1, ntohl(cnid), DT_DIR)) { - goto out; - } - filp->f_pos = 2; - } - - if (filp->f_pos < (dir->i_size - 3)) { - hfs_u32 cnid; - hfs_u8 type; - - if (hfs_cat_open(entry, &brec) || - hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) { - goto out; - } - while (filp->f_pos < (dir->i_size - 3)) { - if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) { - goto out; - } - if (!skip_dirs || (type != HFS_CDR_DIR)) { - ino_t ino; - unsigned int len; - unsigned char tmp_name[HFS_NAMEMAX]; - - ino = ntohl(cnid) | HFS_I(dir)->file_type; - len = hfs_namein(dir, tmp_name, - &((struct hfs_cat_key *)brec.key)->CName); - if (filldir(dirent, tmp_name, len, - filp->f_pos, ino, DT_UNKNOWN)) { - hfs_cat_close(entry, &brec); - goto out; - } - } - ++filp->f_pos; - } - hfs_cat_close(entry, &brec); - } - - if (filp->f_pos == (dir->i_size - 3)) { - if ((entry->cnid == htonl(HFS_ROOT_CNID)) && - (type == HFS_CAP_NDIR)) { - /* In root dir last-2 entry is for ".rootinfo" */ - if (filldir(dirent, DOT_ROOTINFO->Name, - DOT_ROOTINFO_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_CAP_FNDR, - DT_UNKNOWN)) { - goto out; - } - } - ++filp->f_pos; - } - - if (filp->f_pos == (dir->i_size - 2)) { - if (type == HFS_CAP_NDIR) { - /* In normal dirs last-1 entry is for ".finderinfo" */ - if (filldir(dirent, DOT_FINDERINFO->Name, - DOT_FINDERINFO_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_CAP_FDIR, - DT_UNKNOWN)) { - goto out; - } - } - ++filp->f_pos; - } - - if (filp->f_pos == (dir->i_size - 1)) { - if (type == HFS_CAP_NDIR) { - /* In normal dirs last entry is for ".resource" */ - if (filldir(dirent, DOT_RESOURCE->Name, - DOT_RESOURCE_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_CAP_RDIR, - DT_UNKNOWN)) { - goto out; - } - } - ++filp->f_pos; - } - -out: - unlock_kernel(); - return 0; -} - - -/* due to the dcache caching negative dentries for non-existent files, - * we need to drop those entries when a file silently gets created. - * as far as i can tell, the calls that need to do this are the file - * related calls (create, rename, and mknod). the directory calls - * should be immune. the relevant calls in dir.c call drop_dentry - * upon successful completion. */ -void hfs_cap_drop_dentry(struct dentry *dentry, const ino_t type) -{ - if (type == HFS_CAP_DATA) { /* given name */ - hfs_drop_special(dentry->d_parent, DOT_FINDERINFO, dentry); - hfs_drop_special(dentry->d_parent, DOT_RESOURCE, dentry); - } else { - struct dentry *de; - - /* given {.resource,.finderinfo}/name, look for name */ - if ((de = hfs_lookup_dentry(dentry->d_parent->d_parent, - dentry->d_name.name, dentry->d_name.len))) { - if (!de->d_inode) - d_drop(de); - dput(de); - } - - switch (type) { - case HFS_CAP_RSRC: /* given .resource/name */ - /* look for .finderinfo/name */ - hfs_drop_special(dentry->d_parent->d_parent, DOT_FINDERINFO, - dentry); - break; - case HFS_CAP_FNDR: /* given .finderinfo/name. i don't this - * happens. */ - /* look for .resource/name */ - hfs_drop_special(dentry->d_parent->d_parent, DOT_RESOURCE, - dentry); - break; - } - } -} diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c deleted file mode 100644 index ee2ccef70fe7..000000000000 --- a/fs/hfs/dir_dbl.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * linux/fs/hfs/dir_dbl.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the inode_operations and file_operations - * structures for HFS directories. - * - * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> -#include <linux/smp_lock.h> - -/*================ Forward declarations ================*/ - -static struct dentry *dbl_lookup(struct inode *, struct dentry *, struct nameidata *); -static int dbl_readdir(struct file *, void *, filldir_t); -static int dbl_create(struct inode *, struct dentry *, int, struct nameidata *); -static int dbl_mkdir(struct inode *, struct dentry *, int); -static int dbl_unlink(struct inode *, struct dentry *); -static int dbl_rmdir(struct inode *, struct dentry *); -static int dbl_rename(struct inode *, struct dentry *, - struct inode *, struct dentry *); - -/*================ Global variables ================*/ - -#define DOT_LEN 1 -#define DOT_DOT_LEN 2 -#define ROOTINFO_LEN 8 -#define PCNT_ROOTINFO_LEN 9 - -const struct hfs_name hfs_dbl_reserved1[] = { - {DOT_LEN, "."}, - {DOT_DOT_LEN, ".."}, - {0, ""}, -}; - -const struct hfs_name hfs_dbl_reserved2[] = { - {ROOTINFO_LEN, "RootInfo"}, - {PCNT_ROOTINFO_LEN, "%RootInfo"}, - {0, ""}, -}; - -#define DOT (&hfs_dbl_reserved1[0]) -#define DOT_DOT (&hfs_dbl_reserved1[1]) -#define ROOTINFO (&hfs_dbl_reserved2[0]) -#define PCNT_ROOTINFO (&hfs_dbl_reserved2[1]) - -struct file_operations hfs_dbl_dir_operations = { - .read = generic_read_dir, - .readdir = dbl_readdir, - .fsync = file_fsync, -}; - -struct inode_operations hfs_dbl_dir_inode_operations = { - .create = dbl_create, - .lookup = dbl_lookup, - .unlink = dbl_unlink, - .mkdir = dbl_mkdir, - .rmdir = dbl_rmdir, - .rename = dbl_rename, - .setattr = hfs_notify_change, -}; - - -/*================ File-local functions ================*/ - -/* - * is_hdr() - */ -static int is_hdr(struct inode *dir, const char *name, int len) -{ - int retval = 0; - - if (name[0] == '%') { - struct hfs_cat_entry *entry = HFS_I(dir)->entry; - struct hfs_cat_entry *victim; - struct hfs_name cname; - struct hfs_cat_key key; - - hfs_nameout(dir, &cname, name+1, len-1); - hfs_cat_build_key(entry->cnid, &cname, &key); - if ((victim = hfs_cat_get(entry->mdb, &key))) { - hfs_cat_put(victim); - retval = 1; - } - } - return retval; -} - -/* - * dbl_lookup() - * - * This is the lookup() entry in the inode_operations structure for - * HFS directories in the AppleDouble scheme. The purpose is to - * generate the inode corresponding to an entry in a directory, given - * the inode for the directory and the name (and its length) of the - * entry. - */ -static struct dentry *dbl_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) -{ - struct hfs_name cname; - struct hfs_cat_entry *entry; - struct hfs_cat_key key; - struct inode *inode = NULL; - - lock_kernel(); - dentry->d_op = &hfs_dentry_operations; - entry = HFS_I(dir)->entry; - - /* Perform name-mangling */ - hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len); - - /* no need to check for "." or ".." */ - - /* Check for "%RootInfo" if in the root directory. */ - if ((entry->cnid == htonl(HFS_ROOT_CNID)) && - hfs_streq(cname.Name, cname.Len, - PCNT_ROOTINFO->Name, PCNT_ROOTINFO_LEN)) { - ++entry->count; /* __hfs_iget() eats one */ - inode = hfs_iget(entry, HFS_DBL_HDR, dentry); - goto done; - } - - /* Do an hfs_iget() on the mangled name. */ - hfs_cat_build_key(entry->cnid, &cname, &key); - inode = hfs_iget(hfs_cat_get(entry->mdb, &key), HFS_DBL_NORM, dentry); - - /* Try as a header if not found and first character is '%' */ - if (!inode && (dentry->d_name.name[0] == '%')) { - hfs_nameout(dir, &cname, dentry->d_name.name+1, - dentry->d_name.len-1); - hfs_cat_build_key(entry->cnid, &cname, &key); - inode = hfs_iget(hfs_cat_get(entry->mdb, &key), - HFS_DBL_HDR, dentry); - } - -done: - unlock_kernel(); - d_add(dentry, inode); - return NULL; -} - -/* - * dbl_readdir() - * - * This is the readdir() entry in the file_operations structure for - * HFS directories in the AppleDouble scheme. The purpose is to - * enumerate the entries in a directory, given the inode of the - * directory and a (struct file *), the 'f_pos' field of which - * indicates the location in the directory. The (struct file *) is - * updated so that the next call with the same 'dir' and 'filp' - * arguments will produce the next directory entry. The entries are - * returned in 'dirent', which is "filled-in" by calling filldir(). - * This allows the same readdir() function be used for different - * formats. We try to read in as many entries as we can before - * filldir() refuses to take any more. - * - * XXX: In the future it may be a good idea to consider not generating - * metadata files for covered directories since the data doesn't - * correspond to the mounted directory. However this requires an - * iget() for every directory which could be considered an excessive - * amount of overhead. Since the inode for a mount point is always - * in-core this is another argument for a call to get an inode if it - * is in-core or NULL if it is not. - */ -static int dbl_readdir(struct file * filp, - void * dirent, filldir_t filldir) -{ - struct hfs_brec brec; - struct hfs_cat_entry *entry; - struct inode *dir = filp->f_dentry->d_inode; - - lock_kernel(); - - entry = HFS_I(dir)->entry; - - if (filp->f_pos == 0) { - /* Entry 0 is for "." */ - if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino, - DT_DIR)) { - goto out; - } - filp->f_pos = 1; - } - - if (filp->f_pos == 1) { - /* Entry 1 is for ".." */ - if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1, - hfs_get_hl(entry->key.ParID), DT_DIR)) { - goto out; - } - filp->f_pos = 2; - } - - if (filp->f_pos < (dir->i_size - 1)) { - hfs_u32 cnid; - hfs_u8 type; - - if (hfs_cat_open(entry, &brec) || - hfs_cat_next(entry, &brec, (filp->f_pos - 1) >> 1, - &cnid, &type)) { - goto out; - } - - while (filp->f_pos < (dir->i_size - 1)) { - unsigned char tmp_name[HFS_NAMEMAX + 1]; - ino_t ino; - int is_hdr = (filp->f_pos & 1); - unsigned int len; - - if (is_hdr) { - ino = ntohl(cnid) | HFS_DBL_HDR; - tmp_name[0] = '%'; - len = 1 + hfs_namein(dir, tmp_name + 1, - &((struct hfs_cat_key *)brec.key)->CName); - } else { - if (hfs_cat_next(entry, &brec, 1, - &cnid, &type)) { - goto out; - } - ino = ntohl(cnid); - len = hfs_namein(dir, tmp_name, - &((struct hfs_cat_key *)brec.key)->CName); - } - - if (filldir(dirent, tmp_name, len, filp->f_pos, ino, - DT_UNKNOWN)) { - hfs_cat_close(entry, &brec); - goto out; - } - ++filp->f_pos; - } - hfs_cat_close(entry, &brec); - } - - if (filp->f_pos == (dir->i_size - 1)) { - if (entry->cnid == htonl(HFS_ROOT_CNID)) { - /* In root dir last entry is for "%RootInfo" */ - if (filldir(dirent, PCNT_ROOTINFO->Name, - PCNT_ROOTINFO_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_DBL_HDR, - DT_UNKNOWN)) { - goto out; - } - } - ++filp->f_pos; - } - -out: - unlock_kernel(); - return 0; -} - -/* - * dbl_create() - * - * This is the create() entry in the inode_operations structure for - * AppleDouble directories. The purpose is to create a new file in - * a directory and return a corresponding inode, given the inode for - * the directory and the name (and its length) of the new file. - */ -static int dbl_create(struct inode * dir, struct dentry *dentry, - int mode, struct nameidata *nd) -{ - int error; - - lock_kernel(); - if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) { - error = -EEXIST; - } else { - error = hfs_create(dir, dentry, mode, nd); - } - unlock_kernel(); - return error; -} - -/* - * dbl_mkdir() - * - * This is the mkdir() entry in the inode_operations structure for - * AppleDouble directories. The purpose is to create a new directory - * in a directory, given the inode for the parent directory and the - * name (and its length) of the new directory. - */ -static int dbl_mkdir(struct inode * parent, struct dentry *dentry, - int mode) -{ - int error; - - lock_kernel(); - if (is_hdr(parent, dentry->d_name.name, dentry->d_name.len)) { - error = -EEXIST; - } else { - error = hfs_mkdir(parent, dentry, mode); - } - unlock_kernel(); - return error; -} - -/* - * dbl_unlink() - * - * This is the unlink() entry in the inode_operations structure for - * AppleDouble directories. The purpose is to delete an existing - * file, given the inode for the parent directory and the name - * (and its length) of the existing file. - */ -static int dbl_unlink(struct inode * dir, struct dentry *dentry) -{ - int error; - - lock_kernel(); - error = hfs_unlink(dir, dentry); - if ((error == -ENOENT) && is_hdr(dir, dentry->d_name.name, - dentry->d_name.len)) { - error = -EPERM; - } - unlock_kernel(); - return error; -} - -/* - * dbl_rmdir() - * - * This is the rmdir() entry in the inode_operations structure for - * AppleDouble directories. The purpose is to delete an existing - * directory, given the inode for the parent directory and the name - * (and its length) of the existing directory. - */ -static int dbl_rmdir(struct inode * parent, struct dentry *dentry) -{ - int error; - - lock_kernel(); - error = hfs_rmdir(parent, dentry); - if ((error == -ENOENT) && is_hdr(parent, dentry->d_name.name, - dentry->d_name.len)) { - error = -ENOTDIR; - } - unlock_kernel(); - return error; -} - -/* - * dbl_rename() - * - * This is the rename() entry in the inode_operations structure for - * AppleDouble directories. The purpose is to rename an existing - * file or directory, given the inode for the current directory and - * the name (and its length) of the existing file/directory and the - * inode for the new directory and the name (and its length) of the - * new file/directory. - * - * XXX: how do we handle must_be_dir? - */ -static int dbl_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int error; - - lock_kernel(); - if (is_hdr(new_dir, new_dentry->d_name.name, - new_dentry->d_name.len)) { - error = -EPERM; - } else { - error = hfs_rename(old_dir, old_dentry, - new_dir, new_dentry); - if ((error == -ENOENT) /*&& !must_be_dir*/ && - is_hdr(old_dir, old_dentry->d_name.name, - old_dentry->d_name.len)) { - error = -EPERM; - } - } - unlock_kernel(); - return error; -} - - -/* due to the dcache caching negative dentries for non-existent files, - * we need to drop those entries when a file silently gets created. - * as far as i can tell, the calls that need to do this are the file - * related calls (create, rename, and mknod). the directory calls - * should be immune. the relevant calls in dir.c call drop_dentry - * upon successful completion. */ -void hfs_dbl_drop_dentry(struct dentry *dentry, const ino_t type) -{ - unsigned char tmp_name[HFS_NAMEMAX + 1]; - struct dentry *de = NULL; - - switch (type) { - case HFS_DBL_HDR: - /* given %name, look for name. i don't think this happens. */ - de = hfs_lookup_dentry(dentry->d_parent, - dentry->d_name.name + 1, dentry->d_name.len - 1); - break; - case HFS_DBL_DATA: - /* given name, look for %name */ - tmp_name[0] = '%'; - strncpy(tmp_name + 1, dentry->d_name.name, HFS_NAMELEN - 1); - de = hfs_lookup_dentry(dentry->d_parent, - tmp_name, dentry->d_name.len + 1); - } - - if (de) { - if (!de->d_inode) - d_drop(de); - dput(de); - } -} diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c deleted file mode 100644 index 9688bcf7c145..000000000000 --- a/fs/hfs/dir_nat.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * linux/fs/hfs/dir_nat.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the inode_operations and file_operations - * structures for HFS directories. - * - * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds - * - * The source code distributions of Netatalk, versions 1.3.3b2 and - * 1.4b2, were used as a specification of the location and format of - * files used by Netatalk's afpd. No code from Netatalk appears in - * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the - * sense of intellectual property law. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> -#include <linux/smp_lock.h> - -/*================ Forward declarations ================*/ - -static struct dentry *nat_lookup(struct inode *, struct dentry *, struct nameidata *); -static int nat_readdir(struct file *, void *, filldir_t); -static int nat_rmdir(struct inode *, struct dentry *); -static int nat_hdr_unlink(struct inode *, struct dentry *); -static int nat_hdr_rename(struct inode *, struct dentry *, - struct inode *, struct dentry *); - -/*================ Global variables ================*/ - -#define DOT_LEN 1 -#define DOT_DOT_LEN 2 -#define DOT_APPLEDOUBLE_LEN 12 -#define DOT_PARENT_LEN 7 -#define ROOTINFO_LEN 8 - -const struct hfs_name hfs_nat_reserved1[] = { - {DOT_LEN, "."}, - {DOT_DOT_LEN, ".."}, - {DOT_APPLEDOUBLE_LEN, ".AppleDouble"}, - {DOT_PARENT_LEN, ".Parent"}, - {0, ""}, -}; - -const struct hfs_name hfs_nat_reserved2[] = { - {ROOTINFO_LEN, "RootInfo"}, -}; - -#define DOT (&hfs_nat_reserved1[0]) -#define DOT_DOT (&hfs_nat_reserved1[1]) -#define DOT_APPLEDOUBLE (&hfs_nat_reserved1[2]) -#define DOT_PARENT (&hfs_nat_reserved1[3]) -#define ROOTINFO (&hfs_nat_reserved2[0]) - -struct file_operations hfs_nat_dir_operations = { - .read = generic_read_dir, - .readdir = nat_readdir, - .fsync = file_fsync, -}; - -struct inode_operations hfs_nat_ndir_inode_operations = { - .create = hfs_create, - .lookup = nat_lookup, - .unlink = hfs_unlink, - .mkdir = hfs_mkdir, - .rmdir = nat_rmdir, - .rename = hfs_rename, - .setattr = hfs_notify_change, -}; - -struct inode_operations hfs_nat_hdir_inode_operations = { - .create = hfs_create, - .lookup = nat_lookup, - .unlink = nat_hdr_unlink, - .rename = nat_hdr_rename, - .setattr = hfs_notify_change, -}; - -/*================ File-local functions ================*/ - -/* - * nat_lookup() - * - * This is the lookup() entry in the inode_operations structure for - * HFS directories in the Netatalk scheme. The purpose is to generate - * the inode corresponding to an entry in a directory, given the inode - * for the directory and the name (and its length) of the entry. - */ -static struct dentry *nat_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) -{ - ino_t dtype; - struct hfs_name cname; - struct hfs_cat_entry *entry; - struct hfs_cat_key key; - struct inode *inode = NULL; - - lock_kernel(); - dentry->d_op = &hfs_dentry_operations; - entry = HFS_I(dir)->entry; - dtype = HFS_ITYPE(dir->i_ino); - - /* Perform name-mangling */ - hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len); - - /* no need to check for "." or ".." */ - - /* Check for ".AppleDouble" if in a normal directory, - and for ".Parent" in ".AppleDouble". */ - if (dtype==HFS_NAT_NDIR) { - /* Check for ".AppleDouble" */ - if (hfs_streq(cname.Name, cname.Len, - DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) { - ++entry->count; /* __hfs_iget() eats one */ - inode = hfs_iget(entry, HFS_NAT_HDIR, dentry); - goto done; - } - } else if (dtype==HFS_NAT_HDIR) { - if (hfs_streq(cname.Name, cname.Len, - DOT_PARENT->Name, DOT_PARENT_LEN)) { - ++entry->count; /* __hfs_iget() eats one */ - inode = hfs_iget(entry, HFS_NAT_HDR, dentry); - goto done; - } - - if ((entry->cnid == htonl(HFS_ROOT_CNID)) && - hfs_streq(cname.Name, cname.Len, - ROOTINFO->Name, ROOTINFO_LEN)) { - ++entry->count; /* __hfs_iget() eats one */ - inode = hfs_iget(entry, HFS_NAT_HDR, dentry); - goto done; - } - } - - /* Do an hfs_iget() on the mangled name. */ - hfs_cat_build_key(entry->cnid, &cname, &key); - inode = hfs_iget(hfs_cat_get(entry->mdb, &key), - HFS_I(dir)->file_type, dentry); - - /* Don't return a header file for a directory other than .Parent */ - if (inode && (dtype == HFS_NAT_HDIR) && - (HFS_I(inode)->entry != entry) && - (HFS_I(inode)->entry->type == HFS_CDR_DIR)) { - iput(inode); /* this does an hfs_cat_put */ - inode = NULL; - } - -done: - unlock_kernel(); - d_add(dentry, inode); - return NULL; -} - -/* - * nat_readdir() - * - * This is the readdir() entry in the file_operations structure for - * HFS directories in the netatalk scheme. The purpose is to - * enumerate the entries in a directory, given the inode of the - * directory and a struct file which indicates the location in the - * directory. The struct file is updated so that the next call with - * the same dir and filp will produce the next directory entry. The - * entries are returned in dirent, which is "filled-in" by calling - * filldir(). This allows the same readdir() function be used for - * different dirent formats. We try to read in as many entries as we - * can before filldir() refuses to take any more. - * - * Note that the Netatalk format doesn't have the problem with - * metadata for covered directories that exists in the other formats, - * since the metadata is contained within the directory. - */ -static int nat_readdir(struct file * filp, - void * dirent, filldir_t filldir) -{ - ino_t type; - int skip_dirs; - struct hfs_brec brec; - struct hfs_cat_entry *entry; - struct inode *dir = filp->f_dentry->d_inode; - - lock_kernel(); - - entry = HFS_I(dir)->entry; - type = HFS_ITYPE(dir->i_ino); - skip_dirs = (type == HFS_NAT_HDIR); - - if (filp->f_pos == 0) { - /* Entry 0 is for "." */ - if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino, - DT_DIR)) { - goto out; - } - filp->f_pos = 1; - } - - if (filp->f_pos == 1) { - /* Entry 1 is for ".." */ - hfs_u32 cnid; - - if (type == HFS_NAT_NDIR) { - cnid = hfs_get_nl(entry->key.ParID); - } else { - cnid = entry->cnid; - } - - if (filldir(dirent, DOT_DOT->Name, - DOT_DOT_LEN, 1, ntohl(cnid), DT_DIR)) { - goto out; - } - filp->f_pos = 2; - } - - if (filp->f_pos < (dir->i_size - 2)) { - hfs_u32 cnid; - hfs_u8 type; - - if (hfs_cat_open(entry, &brec) || - hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) { - goto out; - } - while (filp->f_pos < (dir->i_size - 2)) { - if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) { - goto out; - } - if (!skip_dirs || (type != HFS_CDR_DIR)) { - ino_t ino; - unsigned int len; - unsigned char tmp_name[HFS_NAMEMAX]; - - ino = ntohl(cnid) | HFS_I(dir)->file_type; - len = hfs_namein(dir, tmp_name, - &((struct hfs_cat_key *)brec.key)->CName); - if (filldir(dirent, tmp_name, len, - filp->f_pos, ino, DT_UNKNOWN)) { - hfs_cat_close(entry, &brec); - goto out; - } - } - ++filp->f_pos; - } - hfs_cat_close(entry, &brec); - } - - if (filp->f_pos == (dir->i_size - 2)) { - if (type == HFS_NAT_NDIR) { - /* In normal dirs entry 2 is for ".AppleDouble" */ - if (filldir(dirent, DOT_APPLEDOUBLE->Name, - DOT_APPLEDOUBLE_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_NAT_HDIR, - DT_UNKNOWN)) { - goto out; - } - } else if (type == HFS_NAT_HDIR) { - /* In .AppleDouble entry 2 is for ".Parent" */ - if (filldir(dirent, DOT_PARENT->Name, - DOT_PARENT_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_NAT_HDR, - DT_UNKNOWN)) { - goto out; - } - } - ++filp->f_pos; - } - - if (filp->f_pos == (dir->i_size - 1)) { - /* handle ROOT/.AppleDouble/RootInfo as the last entry. */ - if ((entry->cnid == htonl(HFS_ROOT_CNID)) && - (type == HFS_NAT_HDIR)) { - if (filldir(dirent, ROOTINFO->Name, - ROOTINFO_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_NAT_HDR, - DT_UNKNOWN)) { - goto out; - } - } - ++filp->f_pos; - } - -out: - unlock_kernel(); - return 0; -} - -/* due to the dcache caching negative dentries for non-existent files, - * we need to drop those entries when a file silently gets created. - * as far as i can tell, the calls that need to do this are the file - * related calls (create, rename, and mknod). the directory calls - * should be immune. the relevant calls in dir.c call drop_dentry - * upon successful completion. */ -void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type) -{ - struct dentry *de; - - switch (type) { - case HFS_NAT_HDR: /* given .AppleDouble/name */ - /* look for name */ - de = hfs_lookup_dentry(dentry->d_parent->d_parent, - dentry->d_name.name, dentry->d_name.len); - - if (de) { - if (!de->d_inode) - d_drop(de); - dput(de); - } - break; - case HFS_NAT_DATA: /* given name */ - /* look for .AppleDouble/name */ - hfs_drop_special(dentry->d_parent, DOT_APPLEDOUBLE, dentry); - break; - } - -} - -/* - * nat_rmdir() - * - * This is the rmdir() entry in the inode_operations structure for - * Netatalk directories. The purpose is to delete an existing - * directory, given the inode for the parent directory and the name - * (and its length) of the existing directory. - * - * We handle .AppleDouble and call hfs_rmdir() for all other cases. - */ -static int nat_rmdir(struct inode *parent, struct dentry *dentry) -{ - struct hfs_cat_entry *entry = HFS_I(parent)->entry; - struct hfs_name cname; - int error; - - lock_kernel(); - hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len); - if (hfs_streq(cname.Name, cname.Len, - DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) { - if (!HFS_SB(parent->i_sb)->s_afpd) { - /* Not in AFPD compatibility mode */ - error = -EPERM; - } else if (entry->u.dir.files || entry->u.dir.dirs) { - /* AFPD compatible, but the directory is not empty */ - error = -ENOTEMPTY; - } else { - /* AFPD compatible, so pretend to succeed */ - error = 0; - } - } else { - error = hfs_rmdir(parent, dentry); - } - unlock_kernel(); - return error; -} - -/* - * nat_hdr_unlink() - * - * This is the unlink() entry in the inode_operations structure for - * Netatalk .AppleDouble directories. The purpose is to delete an - * existing file, given the inode for the parent directory and the name - * (and its length) of the existing file. - * - * WE DON'T ACTUALLY DELETE HEADER THE FILE. - * In non-afpd-compatible mode: - * We return -EPERM. - * In afpd-compatible mode: - * We return success if the file exists or is .Parent. - * Otherwise we return -ENOENT. - */ -static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry) -{ - struct hfs_cat_entry *entry; - int error = 0; - - lock_kernel(); - entry = HFS_I(dir)->entry; - if (!HFS_SB(dir->i_sb)->s_afpd) { - /* Not in AFPD compatibility mode */ - error = -EPERM; - } else { - struct hfs_name cname; - - hfs_nameout(dir, &cname, dentry->d_name.name, - dentry->d_name.len); - if (!hfs_streq(cname.Name, cname.Len, - DOT_PARENT->Name, DOT_PARENT_LEN)) { - struct hfs_cat_entry *victim; - struct hfs_cat_key key; - - hfs_cat_build_key(entry->cnid, &cname, &key); - victim = hfs_cat_get(entry->mdb, &key); - - if (victim) { - /* pretend to succeed */ - hfs_cat_put(victim); - } else { - error = -ENOENT; - } - } - } - unlock_kernel(); - return error; -} - -/* - * nat_hdr_rename() - * - * This is the rename() entry in the inode_operations structure for - * Netatalk header directories. The purpose is to rename an existing - * file given the inode for the current directory and the name - * (and its length) of the existing file and the inode for the new - * directory and the name (and its length) of the new file/directory. - * - * WE NEVER MOVE ANYTHING. - * In non-afpd-compatible mode: - * We return -EPERM. - * In afpd-compatible mode: - * If the source header doesn't exist, we return -ENOENT. - * If the destination is not a header directory we return -EPERM. - * We return success if the destination is also a header directory - * and the header exists or is ".Parent". - */ -static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - struct hfs_cat_entry *entry; - int error = 0; - - lock_kernel(); - entry = HFS_I(old_dir)->entry; - if (!HFS_SB(old_dir->i_sb)->s_afpd) { - /* Not in AFPD compatibility mode */ - error = -EPERM; - } else { - struct hfs_name cname; - - hfs_nameout(old_dir, &cname, old_dentry->d_name.name, - old_dentry->d_name.len); - if (!hfs_streq(cname.Name, cname.Len, - DOT_PARENT->Name, DOT_PARENT_LEN)) { - struct hfs_cat_entry *victim; - struct hfs_cat_key key; - - hfs_cat_build_key(entry->cnid, &cname, &key); - victim = hfs_cat_get(entry->mdb, &key); - - if (victim) { - /* pretend to succeed */ - hfs_cat_put(victim); - } else { - error = -ENOENT; - } - } - - if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) { - error = -EPERM; - } - } - unlock_kernel(); - return error; -} diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c index 4b7d36f5f2fb..a9f4e23f715a 100644 --- a/fs/hfs/extent.c +++ b/fs/hfs/extent.c @@ -1,805 +1,526 @@ /* - * linux/fs/hfs/extent.c + * linux/fs/hfs/extent.c * * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * This file contains the functions related to the extents B-tree. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. */ -#include "hfs.h" +#include <linux/pagemap.h> -/*================ File-local data type ================*/ - -/* An extent record on disk*/ -struct hfs_raw_extent { - hfs_word_t block1; - hfs_word_t length1; - hfs_word_t block2; - hfs_word_t length2; - hfs_word_t block3; - hfs_word_t length3; -}; +#include "hfs_fs.h" +#include "btree.h" /*================ File-local functions ================*/ /* * build_key */ -static inline void build_key(struct hfs_ext_key *key, - const struct hfs_fork *fork, hfs_u16 block) +static void hfs_ext_build_key(hfs_btree_key *key, u32 cnid, u16 block, u8 type) { - key->KeyLen = 7; - key->FkType = fork->fork; - hfs_put_nl(fork->entry->cnid, key->FNum); - hfs_put_hs(block, key->FABN); -} - - -/* - * lock_bitmap() - * - * Get an exclusive lock on the B-tree bitmap. - */ -static inline void lock_bitmap(struct hfs_mdb *mdb) { - down(&mdb->bitmap_sem); + key->key_len = 7; + key->ext.FkType = type; + key->ext.FNum = cpu_to_be32(cnid); + key->ext.FABN = cpu_to_be16(block); } /* - * unlock_bitmap() + * hfs_ext_compare() * - * Relinquish an exclusive lock on the B-tree bitmap. - */ -static inline void unlock_bitmap(struct hfs_mdb *mdb) { - up(&mdb->bitmap_sem); -} + * Description: + * This is the comparison function used for the extents B-tree. In + * comparing extent B-tree entries, the file id is the most + * significant field (compared as unsigned ints); the fork type is + * the second most significant field (compared as unsigned chars); + * and the allocation block number field is the least significant + * (compared as unsigned ints). + * Input Variable(s): + * struct hfs_ext_key *key1: pointer to the first key to compare + * struct hfs_ext_key *key2: pointer to the second key to compare + * Output Variable(s): + * NONE + * Returns: + * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2 + * Preconditions: + * key1 and key2 point to "valid" (struct hfs_ext_key)s. + * Postconditions: + * This function has no side-effects */ +int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2) +{ + unsigned int tmp; + int retval; -/* - * dump_ext() - * - * prints the content of a extent for debugging purposes. - */ -#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL) -static void dump_ext(const char *msg, const struct hfs_extent *e) { - if (e) { - hfs_warn("%s (%d-%d) (%d-%d) (%d-%d)\n", msg, - e->start, - e->start + e->length[0] - 1, - e->start + e->length[0], - e->start + e->length[0] + e->length[1] - 1, - e->start + e->length[0] + e->length[1], - e->end); + tmp = be32_to_cpu(key1->ext.FNum) - be32_to_cpu(key2->ext.FNum); + if (tmp != 0) { + retval = (int)tmp; } else { - hfs_warn("%s NULL\n", msg); + tmp = (unsigned char)key1->ext.FkType - (unsigned char)key2->ext.FkType; + if (tmp != 0) { + retval = (int)tmp; + } else { + retval = (int)(be16_to_cpu(key1->ext.FABN) + - be16_to_cpu(key2->ext.FABN)); + } } -} -#else -#define dump_ext(A,B) {} -#endif - -/* - * read_extent() - * - * Initializes a (struct hfs_extent) from a (struct hfs_raw_extent) and - * the number of the starting block for the extent. - * - * Note that the callers must check that to,from != NULL - */ -static void read_extent(struct hfs_extent *to, - const struct hfs_raw_extent *from, - hfs_u16 start) -{ - to->start = start; - to->block[0] = hfs_get_hs(from->block1); - to->length[0] = hfs_get_hs(from->length1); - to->block[1] = hfs_get_hs(from->block2); - to->length[1] = hfs_get_hs(from->length2); - to->block[2] = hfs_get_hs(from->block3); - to->length[2] = hfs_get_hs(from->length3); - to->end = start + to->length[0] + to->length[1] + to->length[2] - 1; - to->next = to->prev = NULL; - to->count = 0; + return retval; } /* - * write_extent() - * - * Initializes a (struct hfs_raw_extent) from a (struct hfs_extent). + * hfs_ext_find_block * - * Note that the callers must check that to,from != NULL + * Find a block within an extent record */ -static void write_extent(struct hfs_raw_extent *to, - const struct hfs_extent *from) +static u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off) { - hfs_put_hs(from->block[0], to->block1); - hfs_put_hs(from->length[0], to->length1); - hfs_put_hs(from->block[1], to->block2); - hfs_put_hs(from->length[1], to->length2); - hfs_put_hs(from->block[2], to->block3); - hfs_put_hs(from->length[2], to->length3); -} + int i; + u16 count; -/* - * decode_extent() - * - * Given an extent record and allocation block offset into the file, - * return the number of the corresponding allocation block on disk, - * or -1 if the desired block is not mapped by the given extent. - * - * Note that callers must check that extent != NULL - */ -static int decode_extent(const struct hfs_extent * extent, int block) -{ - if (!extent || (block < extent->start) || (block > extent->end) || - (extent->end == (hfs_u16)(extent->start - 1))) { - return -1; - } - block -= extent->start; - if (block < extent->length[0]) { - return block + extent->block[0]; + for (i = 0; i < 3; ext++, i++) { + count = be16_to_cpu(ext->count); + if (off < count) + return be16_to_cpu(ext->block) + off; + off -= count; } - block -= extent->length[0]; - if (block < extent->length[1]) { - return block + extent->block[1]; - } - return block + extent->block[2] - extent->length[1]; + /* panic? */ + return 0; } -/* - * relse_ext() - * - * Reduce the reference count of an in-core extent record by one, - * removing it from memory if the count falls to zero. - */ -static void relse_ext(struct hfs_extent *ext) +static int hfs_ext_block_count(struct hfs_extent *ext) { - if (--ext->count || !ext->start) { - return; - } - ext->prev->next = ext->next; - if (ext->next) { - ext->next->prev = ext->prev; - } - HFS_DELETE(ext); + int i; + u16 count = 0; + + for (i = 0; i < 3; ext++, i++) + count += be16_to_cpu(ext->count); + return count; } -/* - * set_cache() - * - * Changes the 'cache' field of the fork. - */ -static inline void set_cache(struct hfs_fork *fork, struct hfs_extent *ext) +static u16 hfs_ext_lastblock(struct hfs_extent *ext) { - struct hfs_extent *tmp = fork->cache; + int i; - ++ext->count; - fork->cache = ext; - relse_ext(tmp); + ext += 2; + for (i = 0; i < 2; ext--, i++) + if (ext->count) + break; + return be16_to_cpu(ext->block) + be16_to_cpu(ext->count); } -/* - * find_ext() - * - * Given a pointer to a (struct hfs_file) and an allocation block - * number in the file, find the extent record containing that block. - * Returns a pointer to the extent record on success or NULL on failure. - * The 'cache' field of 'fil' also points to the extent so it has a - * reference count of at least 2. - * - * Callers must check that fil != NULL - */ -static struct hfs_extent * find_ext(struct hfs_fork *fork, int alloc_block) +static void __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) { - struct hfs_cat_entry *entry = fork->entry; - struct hfs_btree *tr= entry->mdb->ext_tree; - struct hfs_ext_key target, *key; - struct hfs_brec brec; - struct hfs_extent *ext, *ptr; - int tmp; - - if (alloc_block < 0) { - ext = &fork->first; - goto found; - } - - ext = fork->cache; - if (!ext || (alloc_block < ext->start)) { - ext = &fork->first; - } - while (ext->next && (alloc_block > ext->end)) { - ext = ext->next; - } - if ((alloc_block <= ext->end) && (alloc_block >= ext->start)) { - goto found; - } - - /* time to read more extents */ - if (!HFS_NEW(ext)) { - goto bail3; - } - - build_key(&target, fork, alloc_block); - - tmp = hfs_bfind(&brec, tr, HFS_BKEY(&target), HFS_BFIND_READ_LE); - if (tmp < 0) { - goto bail2; - } - - key = (struct hfs_ext_key *)brec.key; - if ((hfs_get_nl(key->FNum) != hfs_get_nl(target.FNum)) || - (key->FkType != fork->fork)) { - goto bail1; - } - - read_extent(ext, brec.data, hfs_get_hs(key->FABN)); - hfs_brec_relse(&brec, NULL); - - if ((alloc_block > ext->end) && (alloc_block < ext->start)) { - /* something strange happened */ - goto bail2; - } - - ptr = fork->cache; - if (!ptr || (alloc_block < ptr->start)) { - ptr = &fork->first; - } - while (ptr->next && (alloc_block > ptr->end)) { - ptr = ptr->next; - } - if (ext->start == ptr->start) { - /* somebody beat us to it. */ - HFS_DELETE(ext); - ext = ptr; - } else if (ext->start < ptr->start) { - /* insert just before ptr */ - ptr->prev->next = ext; - ext->prev = ptr->prev; - ext->next = ptr; - ptr->prev = ext; + int res; + + hfs_ext_build_key(fd->search_key, inode->i_ino, HFS_I(inode)->cached_start, + HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); + res = hfs_brec_find(fd); + if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) { + if (res != -ENOENT) + return; + hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec)); + HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); } else { - /* insert at end */ - ptr->next = ext; - ext->prev = ptr; + if (res) + return; + hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength); + HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY; } - found: - ++ext->count; /* for return value */ - set_cache(fork, ext); - return ext; - - bail1: - hfs_brec_relse(&brec, NULL); - bail2: - HFS_DELETE(ext); - bail3: - return NULL; } -/* - * delete_extent() - * - * Description: - * Deletes an extent record from a fork, reducing its physical length. - * Input Variable(s): - * struct hfs_fork *fork: the fork - * struct hfs_extent *ext: the current last extent for 'fork' - * Output Variable(s): - * NONE - * Returns: - * void - * Preconditions: - * 'fork' points to a valid (struct hfs_fork) - * 'ext' point to a valid (struct hfs_extent) which is the last in 'fork' - * and which is not also the first extent in 'fork'. - * Postconditions: - * The extent record has been removed if possible, and a warning has been - * printed otherwise. - */ -static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext) +void hfs_ext_write_extent(struct inode *inode) { - struct hfs_mdb *mdb = fork->entry->mdb; - struct hfs_ext_key key; - int error; - - if (fork->cache == ext) { - set_cache(fork, ext->prev); - } - ext->prev->next = NULL; - if (ext->count != 1) { - hfs_warn("hfs_truncate: extent has count %d.\n", ext->count); - } - - lock_bitmap(mdb); - error = hfs_clear_vbm_bits(mdb, ext->block[2], ext->length[2]); - if (error) { - hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); - } - error = hfs_clear_vbm_bits(mdb, ext->block[1], ext->length[1]); - if (error) { - hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); - } - error = hfs_clear_vbm_bits(mdb, ext->block[0], ext->length[0]); - if (error) { - hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); - } - unlock_bitmap(mdb); - - build_key(&key, fork, ext->start); + struct hfs_find_data fd; - error = hfs_bdelete(mdb->ext_tree, HFS_BKEY(&key)); - if (error) { - hfs_warn("hfs_truncate: error %d deleting an extent.\n", error); + if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) { + hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); + __hfs_ext_write_extent(inode, &fd); + hfs_find_exit(&fd); } - - HFS_DELETE(ext); } -/* - * new_extent() - * - * Description: - * Adds a new extent record to a fork, extending its physical length. - * Input Variable(s): - * struct hfs_fork *fork: the fork to extend - * struct hfs_extent *ext: the current last extent for 'fork' - * hfs_u16 ablock: the number of allocation blocks in 'fork'. - * hfs_u16 start: first allocation block to add to 'fork'. - * hfs_u16 len: the number of allocation blocks to add to 'fork'. - * hfs_u32 ablksz: number of sectors in an allocation block. - * Output Variable(s): - * NONE - * Returns: - * (struct hfs_extent *) the new extent or NULL - * Preconditions: - * 'fork' points to a valid (struct hfs_fork) - * 'ext' point to a valid (struct hfs_extent) which is the last in 'fork' - * 'ablock', 'start', 'len' and 'ablksz' are what they claim to be. - * Postconditions: - * If NULL is returned then no changes have been made to 'fork'. - * If the return value is non-NULL that it is the extent that has been - * added to 'fork' both in memory and on disk. The 'psize' field of - * 'fork' has been updated to reflect the new physical size. - */ -static struct hfs_extent *new_extent(struct hfs_fork *fork, - struct hfs_extent *ext, - hfs_u16 ablock, hfs_u16 start, - hfs_u16 len, hfs_u16 ablksz) +static inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent, + u32 cnid, u32 block, u8 type) { - struct hfs_raw_extent raw; - struct hfs_ext_key key; - int error; - - if (fork->entry->cnid == htonl(HFS_EXT_CNID)) { - /* Limit extents tree to the record in the MDB */ - return NULL; - } - - if (!HFS_NEW(ext->next)) { - return NULL; - } - ext->next->prev = ext; - ext->next->next = NULL; - ext = ext->next; - relse_ext(ext->prev); - - ext->start = ablock; - ext->block[0] = start; - ext->length[0] = len; - ext->block[1] = 0; - ext->length[1] = 0; - ext->block[2] = 0; - ext->length[2] = 0; - ext->end = ablock + len - 1; - ext->count = 1; - - write_extent(&raw, ext); - - build_key(&key, fork, ablock); - - error = hfs_binsert(fork->entry->mdb->ext_tree, - HFS_BKEY(&key), &raw, sizeof(raw)); - if (error) { - ext->prev->next = NULL; - HFS_DELETE(ext); - return NULL; - } - set_cache(fork, ext); - return ext; + int res; + + hfs_ext_build_key(fd->search_key, cnid, block, type); + fd->key->ext.FNum = 0; + res = hfs_brec_find(fd); + if (res && res != -ENOENT) + return res; + if (fd->key->ext.FNum != fd->search_key->ext.FNum || + fd->key->ext.FkType != fd->search_key->ext.FkType) + return -ENOENT; + if (fd->entrylength != sizeof(hfs_extent_rec)) + return -EIO; + hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfs_extent_rec)); + return 0; } -/* - * update_ext() - * - * Given a (struct hfs_fork) write an extent record back to disk. - */ -static void update_ext(struct hfs_fork *fork, struct hfs_extent *ext) +static inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block) { - struct hfs_ext_key target; - struct hfs_brec brec; + int res; - if (ext->start) { - build_key(&target, fork, ext->start); + if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) + __hfs_ext_write_extent(inode, fd); - if (!hfs_bfind(&brec, fork->entry->mdb->ext_tree, - HFS_BKEY(&target), HFS_BFIND_WRITE)) { - write_extent(brec.data, ext); - hfs_brec_relse(&brec, NULL); - } - } -} - -/* - * zero_blocks() - * - * Zeros-out 'num' allocation blocks beginning with 'start'. - */ -static int zero_blocks(struct hfs_mdb *mdb, int start, int num) { - hfs_buffer buf; - int end; - int j; - - start = mdb->fs_start + start * mdb->alloc_blksz; - end = start + num * mdb->alloc_blksz; - - for (j=start; j<end; ++j) { - if (hfs_buffer_ok(buf = hfs_buffer_get(mdb->sys_mdb, j, 0))) { - memset(hfs_buffer_data(buf), 0, HFS_SECTOR_SIZE); - hfs_buffer_dirty(buf); - hfs_buffer_put(buf); - } + res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino, + block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); + if (!res) { + HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN); + HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents); + } else { + HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0; + HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); } - return 0; + return res; } -/* - * shrink_fork() - * - * Try to remove enough allocation blocks from 'fork' - * so that it is 'ablocks' allocation blocks long. - */ -static void shrink_fork(struct hfs_fork *fork, int ablocks) +static int hfs_ext_read_extent(struct inode *inode, u16 block) { - struct hfs_mdb *mdb = fork->entry->mdb; - struct hfs_extent *ext; - int i, error, next, count; - hfs_u32 ablksz = mdb->alloc_blksz; - - next = (fork->psize / ablksz) - 1; - ext = find_ext(fork, next); - while (ext && ext->start && (ext->start >= ablocks)) { - next = ext->start - 1; - delete_extent(fork, ext); - ext = find_ext(fork, next); - } - if (!ext) { - fork->psize = (next + 1) * ablksz; - return; - } + struct hfs_find_data fd; + int res; - if ((count = next + 1 - ablocks) > 0) { - for (i=2; (i>=0) && !ext->length[i]; --i) {}; - lock_bitmap(mdb); - while (count && (ext->length[i] <= count)) { - ext->end -= ext->length[i]; - count -= ext->length[i]; - error = hfs_clear_vbm_bits(mdb, ext->block[i], - ext->length[i]); - if (error) { - hfs_warn("hfs_truncate: error %d freeing " - "blocks.\n", error); - } - ext->block[i] = ext->length[i] = 0; - --i; - } - if (count) { - ext->end -= count; - ext->length[i] -= count; - error = hfs_clear_vbm_bits(mdb, ext->block[i] + - ext->length[i], count); - if (error) { - hfs_warn("hfs_truncate: error %d freeing " - "blocks.\n", error); - } - } - unlock_bitmap(mdb); - update_ext(fork, ext); - } + if (block >= HFS_I(inode)->cached_start && + block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks) + return 0; - fork->psize = ablocks * ablksz; + hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); + res = __hfs_ext_cache_extent(&fd, inode, block); + hfs_find_exit(&fd); + return res; } -/* - * grow_fork() - * - * Try to add enough allocation blocks to 'fork' - * so that it is 'ablock' allocation blocks long. - */ -static int grow_fork(struct hfs_fork *fork, int ablocks) +static void hfs_dump_extent(struct hfs_extent *extent) { - struct hfs_cat_entry *entry = fork->entry; - struct hfs_mdb *mdb = entry->mdb; - struct hfs_extent *ext; - int i, start, err; - hfs_u16 need, len=0; - hfs_u32 ablksz = mdb->alloc_blksz; - hfs_u32 blocks, clumpablks; - - blocks = fork->psize; - need = ablocks - blocks/ablksz; - if (need < 1) { /* no need to grow the fork */ - return 0; - } - - /* round up to clumpsize */ - if (entry->u.file.clumpablks) { - clumpablks = entry->u.file.clumpablks; - } else { - clumpablks = mdb->clumpablks; - } - need = ((need + clumpablks - 1) / clumpablks) * clumpablks; + int i; - /* find last extent record and try to extend it */ - if (!(ext = find_ext(fork, blocks/ablksz - 1))) { - /* somehow we couldn't find the end of the file! */ - return -1; - } + dprint(DBG_EXTENT, " "); + for (i = 0; i < 3; i++) + dprint(DBG_EXTENT, " %u:%u", be16_to_cpu(extent[i].block), + be16_to_cpu(extent[i].count)); + dprint(DBG_EXTENT, "\n"); +} - /* determine which is the last used extent in the record */ - /* then try to allocate the blocks immediately following it */ - for (i=2; (i>=0) && !ext->length[i]; --i) {}; - if (i>=0) { - /* try to extend the last extent */ - start = ext->block[i] + ext->length[i]; - - err = 0; - lock_bitmap(mdb); - len = hfs_vbm_count_free(mdb, start); - if (!len) { - unlock_bitmap(mdb); - goto more_extents; - } - if (need < len) { - len = need; - } - err = hfs_set_vbm_bits(mdb, start, len); - unlock_bitmap(mdb); - if (err) { - relse_ext(ext); - return -1; - } - - zero_blocks(mdb, start, len); - - ext->length[i] += len; - ext->end += len; - blocks = (fork->psize += len * ablksz); - need -= len; - update_ext(fork, ext); +static int hfs_add_extent(struct hfs_extent *extent, u16 offset, + u16 alloc_block, u16 block_count) +{ + u16 count, start; + int i; + + hfs_dump_extent(extent); + for (i = 0; i < 3; extent++, i++) { + count = be16_to_cpu(extent->count); + if (offset == count) { + start = be16_to_cpu(extent->block); + if (alloc_block != start + count) { + if (++i >= 3) + return -ENOSPC; + extent++; + extent->block = cpu_to_be16(alloc_block); + } else + block_count += count; + extent->count = cpu_to_be16(block_count); + return 0; + } else if (offset < count) + break; + offset -= count; } + /* panic? */ + return -EIO; +} -more_extents: - /* add some more extents */ - while (need) { - len = need; - err = 0; - lock_bitmap(mdb); - start = hfs_vbm_search_free(mdb, &len); - if (need < len) { - len = need; - } - err = hfs_set_vbm_bits(mdb, start, len); - unlock_bitmap(mdb); - if (!len || err) { - relse_ext(ext); - return -1; - } - zero_blocks(mdb, start, len); - - /* determine which is the first free extent in the record */ - for (i=0; (i<3) && ext->length[i]; ++i) {}; - if (i < 3) { - ext->block[i] = start; - ext->length[i] = len; - ext->end += len; - update_ext(fork, ext); +int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent, + u16 offset, u16 block_nr) +{ + u16 count, start; + int i; + + hfs_dump_extent(extent); + for (i = 0; i < 3; extent++, i++) { + count = be16_to_cpu(extent->count); + if (offset == count) + goto found; + else if (offset < count) + break; + offset -= count; + } + /* panic? */ + return -EIO; +found: + for (;;) { + start = be16_to_cpu(extent->block); + if (count <= block_nr) { + hfs_clear_vbm_bits(sb, start, count); + extent->block = 0; + extent->count = 0; + block_nr -= count; } else { - if (!(ext = new_extent(fork, ext, blocks/ablksz, - start, len, ablksz))) { - lock_bitmap(mdb); - hfs_clear_vbm_bits(mdb, start, len); - unlock_bitmap(mdb); - return -1; - } + count -= block_nr; + hfs_clear_vbm_bits(sb, start + count, block_nr); + extent->count = cpu_to_be16(count); + block_nr = 0; } - blocks = (fork->psize += len * ablksz); - need -= len; + if (!block_nr || !i) + return 0; + i--; + extent--; + count = be16_to_cpu(extent->count); } - set_cache(fork, ext); - relse_ext(ext); - return 0; } -/*================ Global functions ================*/ - -/* - * hfs_ext_compare() - * - * Description: - * This is the comparison function used for the extents B-tree. In - * comparing extent B-tree entries, the file id is the most - * significant field (compared as unsigned ints); the fork type is - * the second most significant field (compared as unsigned chars); - * and the allocation block number field is the least significant - * (compared as unsigned ints). - * Input Variable(s): - * struct hfs_ext_key *key1: pointer to the first key to compare - * struct hfs_ext_key *key2: pointer to the second key to compare - * Output Variable(s): - * NONE - * Returns: - * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2 - * Preconditions: - * key1 and key2 point to "valid" (struct hfs_ext_key)s. - * Postconditions: - * This function has no side-effects */ -int hfs_ext_compare(const struct hfs_ext_key *key1, - const struct hfs_ext_key *key2) +int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type) { - unsigned int tmp; - int retval; - - tmp = hfs_get_hl(key1->FNum) - hfs_get_hl(key2->FNum); - if (tmp != 0) { - retval = (int)tmp; + struct hfs_find_data fd; + u32 total_blocks, blocks, start; + u32 cnid = be32_to_cpu(file->FlNum); + struct hfs_extent *extent; + int res, i; + + if (type == HFS_FK_DATA) { + total_blocks = file->PyLen; + extent = file->ExtRec; } else { - tmp = (unsigned char)key1->FkType - (unsigned char)key2->FkType; - if (tmp != 0) { - retval = (int)tmp; - } else { - retval = (int)(hfs_get_hs(key1->FABN) - - hfs_get_hs(key2->FABN)); - } + total_blocks = file->RPyLen; + extent = file->RExtRec; } - return retval; -} + total_blocks = be32_to_cpu(total_blocks) / HFS_SB(sb)->alloc_blksz; + if (!total_blocks) + return 0; -/* - * hfs_extent_adj() - * - * Given an hfs_fork shrink or grow the fork to hold the - * forks logical size. - */ -void hfs_extent_adj(struct hfs_fork *fork) -{ - if (fork) { - hfs_u32 blks, ablocks, ablksz; + blocks = 0; + for (i = 0; i < 3; extent++, i++) + blocks += be16_to_cpu(extent[i].count); - if (fork->lsize > HFS_FORK_MAX) { - fork->lsize = HFS_FORK_MAX; - } + res = hfs_free_extents(sb, extent, blocks, blocks); + if (res) + return res; + if (total_blocks == blocks) + return 0; - blks = (fork->lsize+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS; - ablksz = fork->entry->mdb->alloc_blksz; - ablocks = (blks + ablksz - 1) / ablksz; - - if (blks > fork->psize) { - grow_fork(fork, ablocks); - if (blks > fork->psize) { - fork->lsize = - fork->psize >> HFS_SECTOR_SIZE_BITS; - } - } else if (blks < fork->psize) { - shrink_fork(fork, ablocks); - } - } + hfs_find_init(HFS_SB(sb)->ext_tree, &fd); + do { + res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type); + if (res) + break; + start = be16_to_cpu(fd.key->ext.FABN); + hfs_free_extents(sb, extent, total_blocks - start, total_blocks); + hfs_brec_remove(&fd); + total_blocks = start; + } while (total_blocks > blocks); + hfs_find_exit(&fd); + + return res; } /* - * hfs_extent_map() - * - * Given an hfs_fork and a block number within the fork, return the - * number of the corresponding physical block on disk, or zero on - * error. + * hfs_get_block */ -int hfs_extent_map(struct hfs_fork *fork, int block, int create) +int hfs_get_block(struct inode *inode, sector_t block, + struct buffer_head *bh_result, int create) { - int ablksz, ablock, offset, tmp; - struct hfs_extent *ext; + struct super_block *sb; + u16 dblock, ablock; + int res; + + sb = inode->i_sb; + /* Convert inode block to disk allocation block */ + ablock = (u32)block / HFS_SB(sb)->fs_div; + + if (block >= inode->i_blocks) { + if (block > inode->i_blocks || !create) + return -EIO; + if (ablock >= HFS_I(inode)->alloc_blocks) { + res = hfs_extend_file(inode); + if (res) + return res; + } + } else + create = 0; - if (!fork || !fork->entry || !fork->entry->mdb) { - return 0; + if (ablock < HFS_I(inode)->first_blocks) { + dblock = hfs_ext_find_block(HFS_I(inode)->first_extents, ablock); + goto done; } -#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL) - hfs_warn("hfs_extent_map: ablock %d of file %d, fork %d\n", - block, fork->entry->cnid, fork->fork); -#endif - - if (block < 0) { - hfs_warn("hfs_extent_map: block < 0\n"); - return 0; - } - if (block > (HFS_FORK_MAX >> HFS_SECTOR_SIZE_BITS)) { - hfs_warn("hfs_extent_map: block(0x%08x) > big; cnid=%d " - "fork=%d\n", block, fork->entry->cnid, fork->fork); - return 0; - } - ablksz = fork->entry->mdb->alloc_blksz; - offset = fork->entry->mdb->fs_start + (block % ablksz); - ablock = block / ablksz; - - if (block >= fork->psize) { - if (!create || (grow_fork(fork, ablock + 1) < 0)) - return 0; + down(&HFS_I(inode)->extents_lock); + res = hfs_ext_read_extent(inode, ablock); + if (!res) + dblock = hfs_ext_find_block(HFS_I(inode)->cached_extents, + ablock - HFS_I(inode)->cached_start); + else { + up(&HFS_I(inode)->extents_lock); + return -EIO; } + up(&HFS_I(inode)->extents_lock); -#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL) - hfs_warn("(lblock %d offset %d)\n", ablock, offset); -#endif - - if ((ext = find_ext(fork, ablock))) { - dump_ext("trying new: ", ext); - tmp = decode_extent(ext, ablock); - relse_ext(ext); - if (tmp >= 0) { - return tmp*ablksz + offset; - } - } +done: + map_bh(bh_result, sb, HFS_SB(sb)->fs_start + + dblock * HFS_SB(sb)->fs_div + + (u32)block % HFS_SB(sb)->fs_div); + if (create) { + set_buffer_new(bh_result); + HFS_I(inode)->phys_size += sb->s_blocksize; + inode->i_blocks++; + mark_inode_dirty(inode); + } return 0; } -/* - * hfs_extent_out() - * - * Copy the first extent record from a (struct hfs_fork) to a (struct - * raw_extent), record (normally the one in the catalog entry). - */ -void hfs_extent_out(const struct hfs_fork *fork, hfs_byte_t dummy[12]) +int hfs_extend_file(struct inode *inode) { - struct hfs_raw_extent *ext = (struct hfs_raw_extent *)dummy; - - if (fork && ext) { - write_extent(ext, &fork->first); - dump_ext("extent out: ", &fork->first); - } + struct super_block *sb = inode->i_sb; + u32 start, len, goal; + int res; + + down(&HFS_I(inode)->extents_lock); + if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) + goal = hfs_ext_lastblock(HFS_I(inode)->first_extents); + else { + res = hfs_ext_read_extent(inode, HFS_I(inode)->alloc_blocks); + if (res) + goto out; + goal = hfs_ext_lastblock(HFS_I(inode)->cached_extents); + } + + len = HFS_I(inode)->clump_blocks; + start = hfs_vbm_search_free(sb, goal, &len); + if (!len) { + res = -ENOSPC; + goto out; + } + + dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); + if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) { + if (!HFS_I(inode)->first_blocks) { + dprint(DBG_EXTENT, "first extents\n"); + /* no extents yet */ + HFS_I(inode)->first_extents[0].block = cpu_to_be16(start); + HFS_I(inode)->first_extents[0].count = cpu_to_be16(len); + res = 0; + } else { + /* try to append to extents in inode */ + res = hfs_add_extent(HFS_I(inode)->first_extents, + HFS_I(inode)->alloc_blocks, + start, len); + if (res == -ENOSPC) + goto insert_extent; + } + if (!res) { + hfs_dump_extent(HFS_I(inode)->first_extents); + HFS_I(inode)->first_blocks += len; + } + } else { + res = hfs_add_extent(HFS_I(inode)->cached_extents, + HFS_I(inode)->alloc_blocks - + HFS_I(inode)->cached_start, + start, len); + if (!res) { + hfs_dump_extent(HFS_I(inode)->cached_extents); + HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY; + HFS_I(inode)->cached_blocks += len; + } else if (res == -ENOSPC) + goto insert_extent; + } +out: + up(&HFS_I(inode)->extents_lock); + if (!res) { + HFS_I(inode)->alloc_blocks += len; + mark_inode_dirty(inode); + if (inode->i_ino < HFS_FIRSTUSER_CNID) + set_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags); + set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); + sb->s_dirt = 1; + } + return res; + +insert_extent: + dprint(DBG_EXTENT, "insert new extent\n"); + hfs_ext_write_extent(inode); + + memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec)); + HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start); + HFS_I(inode)->cached_extents[0].count = cpu_to_be16(len); + hfs_dump_extent(HFS_I(inode)->cached_extents); + HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW; + HFS_I(inode)->cached_start = HFS_I(inode)->alloc_blocks; + HFS_I(inode)->cached_blocks = len; + + res = 0; + goto out; } -/* - * hfs_extent_in() - * - * Copy an raw_extent to the 'first' and 'cache' fields of an hfs_fork. - */ -void hfs_extent_in(struct hfs_fork *fork, const hfs_byte_t dummy[12]) +void hfs_file_truncate(struct inode *inode) { - const struct hfs_raw_extent *ext = - (const struct hfs_raw_extent *)dummy; - - if (fork && ext) { - read_extent(&fork->first, ext, 0); - fork->cache = &fork->first; - fork->first.count = 2; - dump_ext("extent in: ", &fork->first); + struct super_block *sb = inode->i_sb; + struct hfs_find_data fd; + u16 blk_cnt, alloc_cnt, start; + u32 size; + int res; + + dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino, + (long long)HFS_I(inode)->phys_size, inode->i_size); + if (inode->i_size > HFS_I(inode)->phys_size) { + struct address_space *mapping = inode->i_mapping; + struct page *page; + int res; + + size = inode->i_size - 1; + page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); + if (!page) + return; + size &= PAGE_CACHE_SIZE - 1; + size++; + res = mapping->a_ops->prepare_write(NULL, page, size, size); + if (!res) + res = mapping->a_ops->commit_write(NULL, page, size, size); + if (res) + inode->i_size = HFS_I(inode)->phys_size; + unlock_page(page); + page_cache_release(page); + mark_inode_dirty(inode); + return; } -} - -/* - * hfs_extent_free() - * - * Removes from memory all extents associated with 'fil'. - */ -void hfs_extent_free(struct hfs_fork *fork) -{ - if (fork) { - set_cache(fork, &fork->first); - - if (fork->first.next) { - hfs_warn("hfs_extent_free: extents in use!\n"); + size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1; + blk_cnt = size / HFS_SB(sb)->alloc_blksz; + alloc_cnt = HFS_I(inode)->alloc_blocks; + if (blk_cnt == alloc_cnt) + goto out; + + down(&HFS_I(inode)->extents_lock); + hfs_find_init(HFS_SB(sb)->ext_tree, &fd); + while (1) { + if (alloc_cnt == HFS_I(inode)->first_blocks) { + hfs_free_extents(sb, HFS_I(inode)->first_extents, + alloc_cnt, alloc_cnt - blk_cnt); + hfs_dump_extent(HFS_I(inode)->first_extents); + HFS_I(inode)->first_blocks = blk_cnt; + break; } - } + res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt); + if (res) + break; + start = HFS_I(inode)->cached_start; + hfs_free_extents(sb, HFS_I(inode)->cached_extents, + alloc_cnt - start, alloc_cnt - blk_cnt); + hfs_dump_extent(HFS_I(inode)->cached_extents); + if (blk_cnt > start) { + HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY; + break; + } + alloc_cnt = start; + HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0; + HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); + hfs_brec_remove(&fd); + } + hfs_find_exit(&fd); + up(&HFS_I(inode)->extents_lock); + + HFS_I(inode)->alloc_blocks = blk_cnt; +out: + HFS_I(inode)->phys_size = inode->i_size; + mark_inode_dirty(inode); + inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; } diff --git a/fs/hfs/file.c b/fs/hfs/file.c deleted file mode 100644 index 3d649c0690e8..000000000000 --- a/fs/hfs/file.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * linux/fs/hfs/file.c - * - * Copyright (C) 1995, 1996 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the file-related functions which are independent of - * which scheme is being used to represent forks. - * - * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> -#include <linux/buffer_head.h> -#include <linux/smp_lock.h> - -/*================ Forward declarations ================*/ - -static hfs_rwret_t hfs_file_read(struct file *, char __user *, hfs_rwarg_t, - loff_t *); -static hfs_rwret_t hfs_file_write(struct file *, const char __user *, - hfs_rwarg_t, loff_t *); -static void hfs_file_truncate(struct inode *); - -/*================ Global variables ================*/ - -struct file_operations hfs_file_operations = { - .llseek = generic_file_llseek, - .read = hfs_file_read, - .write = hfs_file_write, - .mmap = generic_file_mmap, - .fsync = file_fsync, -}; - -struct inode_operations hfs_file_inode_operations = { - .truncate = hfs_file_truncate, - .setattr = hfs_notify_change, -}; - -/*================ Variable-like macros ================*/ - -/* maximum number of blocks to try to read in at once */ -#define NBUF 32 - -/*================ File-local functions ================*/ - -/* - * hfs_getblk() - * - * Given an hfs_fork and a block number return the buffer_head for - * that block from the fork. If 'create' is non-zero then allocate - * the necessary block(s) to the fork. - */ -struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create) -{ - int tmp; - struct super_block *sb = fork->entry->mdb->sys_mdb; - - tmp = hfs_extent_map(fork, block, create); - - if (create) { - /* If writing the block, then we have exclusive access - to the file until we return, so it can't have moved. - */ - if (tmp) { - hfs_cat_mark_dirty(fork->entry); - return sb_getblk(sb, tmp); - } - return NULL; - } else { - /* If reading the block, then retry since the - location on disk could have changed while - we waited on the I/O in getblk to complete. - */ - do { - struct buffer_head *bh = sb_getblk(sb, tmp); - int tmp2 = hfs_extent_map(fork, block, 0); - - if (tmp2 == tmp) { - return bh; - } else { - /* The block moved or no longer exists. */ - brelse(bh); - tmp = tmp2; - } - } while (tmp != 0); - - /* The block no longer exists. */ - return NULL; - } -} - -/* - * hfs_get_block - * - * This is the hfs_get_block() field in the inode_operations structure for - * "regular" (non-header) files. The purpose is to translate an inode - * and a block number within the corresponding file into a physical - * block number. This function just calls hfs_extent_map() to do the - * real work and then stuffs the appropriate info into the buffer_head. - */ -int hfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) -{ - unsigned long phys; - - phys = hfs_extent_map(HFS_I(inode)->fork, iblock, create); - if (phys) { - if (create) - set_buffer_new(bh_result); - map_bh(bh_result, inode->i_sb, phys); - return 0; - } - - if (!create) - return 0; - - /* we tried to add stuff, but we couldn't. send back an out-of-space - * error. */ - return -ENOSPC; -} - - -/* - * hfs_file_read() - * - * This is the read field in the inode_operations structure for - * "regular" (non-header) files. The purpose is to transfer up to - * 'count' bytes from the file corresponding to 'inode', beginning at - * 'filp->offset' bytes into the file. The data is transferred to - * user-space at the address 'buf'. Returns the number of bytes - * successfully transferred. This function checks the arguments, does - * some setup and then calls hfs_do_read() to do the actual transfer. */ -static hfs_rwret_t hfs_file_read(struct file *filp, char __user *buf, - hfs_rwarg_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - hfs_s32 read, left, pos, size; - - if (!S_ISREG(inode->i_mode)) { - hfs_warn("hfs_file_read: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - pos = *ppos; - if (pos >= HFS_FORK_MAX) { - return 0; - } - size = inode->i_size; - if (pos > size) { - left = 0; - } else { - left = size - pos; - } - if (left > count) { - left = count; - } - if (left <= 0) { - return 0; - } - if ((read = hfs_do_read(inode, HFS_I(inode)->fork, pos, buf, left)) > 0) { - *ppos += read; - } - - return read; -} - -/* - * hfs_file_write() - * - * This is the write() entry in the file_operations structure for - * "regular" files. The purpose is to transfer up to 'count' bytes - * to the file corresponding to 'inode' beginning at offset - * 'file->f_pos' from user-space at the address 'buf'. The return - * value is the number of bytes actually transferred. - */ -static hfs_rwret_t hfs_file_write(struct file *filp, const char __user *buf, - hfs_rwarg_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct hfs_fork *fork = HFS_I(inode)->fork; - hfs_s32 written, pos; - - if (!S_ISREG(inode->i_mode)) { - hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode); - return -EINVAL; - } - - pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos; - - if (pos >= HFS_FORK_MAX) { - return 0; - } - if (count > HFS_FORK_MAX) { - count = HFS_FORK_MAX; - } - if ((written = hfs_do_write(inode, fork, pos, buf, count)) > 0) - pos += written; - - *ppos = pos; - if (*ppos > inode->i_size) { - inode->i_size = *ppos; - mark_inode_dirty(inode); - } - - return written; -} - -/* - * hfs_file_truncate() - * - * This is the truncate() entry in the file_operations structure for - * "regular" files. The purpose is to change the length of the file - * corresponding to the given inode. Changes can either lengthen or - * shorten the file. - */ -static void hfs_file_truncate(struct inode * inode) -{ - struct hfs_fork *fork; - - lock_kernel(); - fork = HFS_I(inode)->fork; - fork->lsize = inode->i_size; - hfs_extent_adj(fork); - hfs_cat_mark_dirty(HFS_I(inode)->entry); - - inode->i_size = fork->lsize; - inode->i_blocks = fork->psize; - mark_inode_dirty(inode); - unlock_kernel(); -} - -/* - * xlate_to_user() - * - * Like copy_to_user() while translating CR->NL. - */ -static inline void xlate_to_user(char __user *buf, const char *data, int count) -{ - char ch; - - while (count--) { - ch = *(data++); - put_user((ch == '\r') ? '\n' : ch, buf++); - } -} - -/* - * xlate_from_user() - * - * Like copy_from_user() while translating NL->CR; - */ -static inline -int xlate_from_user(char *data, const char __user *buf, int count) -{ - int i; - - i = copy_from_user(data, buf, count); - count -= i; - while (count--) { - if (*data == '\n') { - *data = '\r'; - } - ++data; - } - return i; -} - -/*================ Global functions ================*/ - -/* - * hfs_do_read() - * - * This function transfers actual data from disk to user-space memory, - * returning the number of bytes successfully transferred. 'fork' tells - * which file on the disk to read from. 'pos' gives the offset into - * the Linux file at which to begin the transfer. Note that this will - * differ from 'filp->offset' in the case of an AppleDouble header file - * due to the block of metadata at the beginning of the file, which has - * no corresponding place in the HFS file. 'count' tells how many - * bytes to transfer. 'buf' gives an address in user-space to transfer - * the data to. - * - * This is based on Linus's minix_file_read(). - * It has been changed to take into account that HFS files have no holes. - */ -hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork *fork, hfs_u32 pos, - char __user *buf, hfs_u32 count) -{ - hfs_s32 size, chars, offset, block, blocks, read = 0; - int bhrequest, uptodate; - int convert = HFS_I(inode)->convert; - struct buffer_head ** bhb, ** bhe; - struct buffer_head * bhreq[NBUF]; - struct buffer_head * buflist[NBUF]; - - /* split 'pos' in to block and (byte) offset components */ - block = pos >> HFS_SECTOR_SIZE_BITS; - offset = pos & (HFS_SECTOR_SIZE-1); - - /* compute the logical size of the fork in blocks */ - size = (fork->lsize + (HFS_SECTOR_SIZE-1)) >> HFS_SECTOR_SIZE_BITS; - - /* compute the number of physical blocks to be transferred */ - blocks = (count+offset+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS; - - bhb = bhe = buflist; - - /* We do this in a two stage process. We first try and - request as many blocks as we can, then we wait for the - first one to complete, and then we try and wrap up as many - as are actually done. - - This routine is optimized to make maximum use of the - various buffers and caches. */ - - do { - bhrequest = 0; - uptodate = 1; - while (blocks) { - --blocks; - *bhb = hfs_getblk(fork, block++, 0); - - if (!(*bhb)) { - /* Since there are no holes in HFS files - we must have encountered an error. - So, stop adding blocks to the queue. */ - blocks = 0; - break; - } - - if (!buffer_uptodate(*bhb)) { - uptodate = 0; - bhreq[bhrequest++] = *bhb; - } - - if (++bhb == &buflist[NBUF]) { - bhb = buflist; - } - - /* If the block we have on hand is uptodate, - go ahead and complete processing. */ - if (uptodate) { - break; - } - if (bhb == bhe) { - break; - } - } - - /* If the only block in the queue is bad then quit */ - if (!(*bhe)) { - break; - } - - /* Now request them all */ - if (bhrequest) { - ll_rw_block(READ, bhrequest, bhreq); - } - - do { /* Finish off all I/O that has actually completed */ - char *p; - - wait_on_buffer(*bhe); - - if (!buffer_uptodate(*bhe)) { - /* read error? */ - brelse(*bhe); - if (++bhe == &buflist[NBUF]) { - bhe = buflist; - } - count = 0; - break; - } - - if (count < HFS_SECTOR_SIZE - offset) { - chars = count; - } else { - chars = HFS_SECTOR_SIZE - offset; - } - p = (*bhe)->b_data + offset; - if (convert) { - xlate_to_user(buf, p, chars); - } else { - chars -= copy_to_user(buf, p, chars); - if (!chars) { - brelse(*bhe); - count = 0; - if (!read) - read = -EFAULT; - break; - } - } - brelse(*bhe); - count -= chars; - buf += chars; - read += chars; - offset = 0; - if (++bhe == &buflist[NBUF]) { - bhe = buflist; - } - } while (count && (bhe != bhb) && !buffer_locked(*bhe)); - } while (count); - - /* Release the read-ahead blocks */ - while (bhe != bhb) { - brelse(*bhe); - if (++bhe == &buflist[NBUF]) { - bhe = buflist; - } - } - if (!read) { - return -EIO; - } - return read; -} - -/* - * hfs_do_write() - * - * This function transfers actual data from user-space memory to disk, - * returning the number of bytes successfully transferred. 'fork' tells - * which file on the disk to write to. 'pos' gives the offset into - * the Linux file at which to begin the transfer. Note that this will - * differ from 'filp->offset' in the case of an AppleDouble header file - * due to the block of metadata at the beginning of the file, which has - * no corresponding place in the HFS file. 'count' tells how many - * bytes to transfer. 'buf' gives an address in user-space to transfer - * the data from. - * - * This is just a minor edit of Linus's minix_file_write(). - */ -hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork *fork, hfs_u32 pos, - const char __user *buf, hfs_u32 count) -{ - hfs_s32 written, c; - struct buffer_head * bh; - char * p; - int convert = HFS_I(inode)->convert; - - written = 0; - while (written < count) { - bh = hfs_getblk(fork, pos/HFS_SECTOR_SIZE, 1); - if (!bh) { - if (!written) { - written = -ENOSPC; - } - break; - } - c = HFS_SECTOR_SIZE - (pos % HFS_SECTOR_SIZE); - if (c > count - written) { - c = count - written; - } - if (c != HFS_SECTOR_SIZE && !buffer_uptodate(bh)) { - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - if (!written) { - written = -EIO; - } - break; - } - } - p = (pos % HFS_SECTOR_SIZE) + bh->b_data; - c -= convert ? xlate_from_user(p, buf, c) : - copy_from_user(p, buf, c); - if (!c) { - brelse(bh); - if (!written) - written = -EFAULT; - break; - } - pos += c; - written += c; - buf += c; - set_buffer_uptodate(bh); - mark_buffer_dirty(bh); - brelse(bh); - } - if (written > 0) { - struct hfs_cat_entry *entry = fork->entry; - - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - if (pos > fork->lsize) { - fork->lsize = pos; - } - entry->modify_date = hfs_u_to_mtime(get_seconds()); - hfs_cat_mark_dirty(entry); - } - return written; -} - -/* - * hfs_file_fix_mode() - * - * Fixes up the permissions on a file after changing the write-inhibit bit. - */ -void hfs_file_fix_mode(struct hfs_cat_entry *entry) -{ - struct dentry **de = entry->sys_entry; - int i; - - if (entry->u.file.flags & HFS_FIL_LOCK) { - for (i = 0; i < 4; ++i) { - if (de[i]) { - de[i]->d_inode->i_mode &= ~S_IWUGO; - } - } - } else { - for (i = 0; i < 4; ++i) { - if (de[i]) { - struct inode *inode = de[i]->d_inode; - inode->i_mode |= S_IWUGO; - inode->i_mode &= - ~HFS_SB(inode->i_sb)->s_umask; - } - } - } -} diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c deleted file mode 100644 index 500a016e0df8..000000000000 --- a/fs/hfs/file_cap.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * linux/fs/hfs/file_cap.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the file_ops and inode_ops for the metadata - * files under the CAP representation. - * - * The source code distribution of the Columbia AppleTalk Package for - * UNIX, version 6.0, (CAP) was used as a specification of the - * location and format of files used by CAP's Aufs. No code from CAP - * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in - * the sense of intellectual property law. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> -#include <linux/smp_lock.h> - -/*================ Forward declarations ================*/ -static loff_t cap_info_llseek(struct file *, loff_t, - int); -static hfs_rwret_t cap_info_read(struct file *, char __user *, - hfs_rwarg_t, loff_t *); -static hfs_rwret_t cap_info_write(struct file *, const char __user *, - hfs_rwarg_t, loff_t *); -/*================ Function-like macros ================*/ - -/* - * OVERLAPS() - * - * Determines if a given range overlaps the specified structure member - */ -#define OVERLAPS(START, END, TYPE, MEMB) \ - ((END > offsetof(TYPE, MEMB)) && \ - (START < offsetof(TYPE, MEMB) + sizeof(((TYPE *)0)->MEMB))) - -/*================ Global variables ================*/ - -struct file_operations hfs_cap_info_operations = { - .llseek = cap_info_llseek, - .read = cap_info_read, - .write = cap_info_write, - .fsync = file_fsync, -}; - -struct inode_operations hfs_cap_info_inode_operations = { - .setattr = hfs_notify_change_cap, -}; - -/*================ File-local functions ================*/ - -/* - * cap_build_meta() - * - * Build the metadata structure. - */ -static void cap_build_meta(struct hfs_cap_info *meta, - struct hfs_cat_entry *entry) -{ - memset(meta, 0, sizeof(*meta)); - memcpy(meta->fi_fndr, &entry->info, 32); - if ((entry->type == HFS_CDR_FIL) && - (entry->u.file.flags & HFS_FIL_LOCK)) { - /* Couple the locked bit of the file to the - AFP {write,rename,delete} inhibit bits. */ - hfs_put_hs(HFS_AFP_RDONLY, meta->fi_attr); - } - meta->fi_magic1 = HFS_CAP_MAGIC1; - meta->fi_version = HFS_CAP_VERSION; - meta->fi_magic = HFS_CAP_MAGIC; - meta->fi_bitmap = HFS_CAP_LONGNAME; - memcpy(meta->fi_macfilename, entry->key.CName.Name, - entry->key.CName.Len); - meta->fi_datemagic = HFS_CAP_DMAGIC; - meta->fi_datevalid = HFS_CAP_MDATE | HFS_CAP_CDATE; - hfs_put_nl(hfs_m_to_htime(entry->create_date), meta->fi_ctime); - hfs_put_nl(hfs_m_to_htime(entry->modify_date), meta->fi_mtime); - hfs_put_nl(get_seconds(), meta->fi_utime); -} - -static loff_t cap_info_llseek(struct file *file, loff_t offset, int origin) -{ - long long retval; - - lock_kernel(); - - switch (origin) { - case 2: - offset += file->f_dentry->d_inode->i_size; - break; - case 1: - offset += file->f_pos; - } - retval = -EINVAL; - if (offset>=0 && offset<=HFS_FORK_MAX) { - if (offset != file->f_pos) { - file->f_pos = offset; - } - retval = offset; - } - unlock_kernel(); - return retval; -} - -/* - * cap_info_read() - * - * This is the read() entry in the file_operations structure for CAP - * metadata files. The purpose is to transfer up to 'count' bytes - * from the file corresponding to 'inode' beginning at offset - * 'file->f_pos' to user-space at the address 'buf'. The return value - * is the number of bytes actually transferred. - */ -static hfs_rwret_t cap_info_read(struct file *filp, char __user *buf, - hfs_rwarg_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - hfs_s32 left, size, read = 0; - hfs_u32 pos; - - if (!S_ISREG(inode->i_mode)) { - hfs_warn("hfs_cap_info_read: mode = %07o\n", inode->i_mode); - return -EINVAL; - } - - pos = *ppos; - if (pos > HFS_FORK_MAX) { - return 0; - } - size = inode->i_size; - if (pos > size) { - left = 0; - } else { - left = size - pos; - } - if (left > count) { - left = count; - } - if (left <= 0) { - return 0; - } - - if (pos < sizeof(struct hfs_cap_info)) { - int memcount = sizeof(struct hfs_cap_info) - pos; - struct hfs_cap_info meta; - - if (memcount > left) { - memcount = left; - } - cap_build_meta(&meta, entry); - memcount -= copy_to_user(buf, ((char *)&meta) + pos, memcount); - left -= memcount; - read += memcount; - pos += memcount; - buf += memcount; - } - - if (left > 0) { - clear_user(buf, left); - pos += left; - } - - if (read) { - inode->i_atime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - } - - return read; -} - -/* - * cap_info_write() - * - * This is the write() entry in the file_operations structure for CAP - * metadata files. The purpose is to transfer up to 'count' bytes - * to the file corresponding to 'inode' beginning at offset - * '*ppos' from user-space at the address 'buf'. - * The return value is the number of bytes actually transferred. - */ -static hfs_rwret_t cap_info_write(struct file *filp, const char __user *buf, - hfs_rwarg_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - hfs_u32 pos; - - if (!S_ISREG(inode->i_mode)) { - hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode); - return -EINVAL; - } - if (count <= 0) { - return 0; - } - - pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos; - - if (pos > HFS_FORK_MAX) { - return 0; - } - - *ppos += count; - if (*ppos > HFS_FORK_MAX) { - *ppos = HFS_FORK_MAX; - count = HFS_FORK_MAX - pos; - } - - if (*ppos > inode->i_size) - inode->i_size = *ppos; - - /* Only deal with the part we store in memory */ - if (pos < sizeof(struct hfs_cap_info)) { - int end, mem_count; - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - struct hfs_cap_info meta; - - mem_count = sizeof(struct hfs_cap_info) - pos; - if (mem_count > count) { - mem_count = count; - } - end = pos + mem_count; - - cap_build_meta(&meta, entry); - mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count); - - /* Update finder attributes if changed */ - if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) { - memcpy(&entry->info, meta.fi_fndr, 32); - hfs_cat_mark_dirty(entry); - } - - /* Update file flags if changed */ - if (OVERLAPS(pos, end, struct hfs_cap_info, fi_attr) && - (entry->type == HFS_CDR_FIL)) { - int locked = hfs_get_ns(&meta.fi_attr) & - htons(HFS_AFP_WRI); - hfs_u8 new_flags; - - if (locked) { - new_flags = entry->u.file.flags | HFS_FIL_LOCK; - } else { - new_flags = entry->u.file.flags & ~HFS_FIL_LOCK; - } - - if (new_flags != entry->u.file.flags) { - entry->u.file.flags = new_flags; - hfs_cat_mark_dirty(entry); - hfs_file_fix_mode(entry); - } - } - - /* Update CrDat if changed */ - if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) { - entry->create_date = - hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime)); - hfs_cat_mark_dirty(entry); - } - - /* Update MdDat if changed */ - if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) { - entry->modify_date = - hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime)); - hfs_cat_mark_dirty(entry); - } - } - - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); - return count; -} diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c deleted file mode 100644 index b39484939f9b..000000000000 --- a/fs/hfs/file_hdr.c +++ /dev/null @@ -1,1033 +0,0 @@ -/* - * linux/fs/hfs/file_hdr.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the file_ops and inode_ops for the metadata - * files under the AppleDouble and Netatalk representations. - * - * The source code distributions of Netatalk, versions 1.3.3b2 and - * 1.4b2, were used as a specification of the location and format of - * files used by Netatalk's afpd. No code from Netatalk appears in - * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the - * sense of intellectual property law. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - * - * XXX: Note the reason that there is not bmap() for AppleDouble - * header files is that dynamic nature of their structure make it - * very difficult to safely mmap them. Maybe in the distant future - * I'll get bored enough to implement it. - */ - -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> -#include <linux/smp_lock.h> - -/* prodos types */ -#define PRODOSI_FTYPE_DIR 0x0F -#define PRODOSI_FTYPE_TEXT 0x04 -#define PRODOSI_FTYPE_8BIT 0xFF -#define PRODOSI_FTYPE_16BIT 0xB3 - -#define PRODOSI_AUXTYPE_DIR 0x0200 - -/*================ Forward declarations ================*/ -static loff_t hdr_llseek(struct file *, loff_t, int); -static hfs_rwret_t hdr_read(struct file *, char __user *, - hfs_rwarg_t, loff_t *); -static hfs_rwret_t hdr_write(struct file *, const char __user *, - hfs_rwarg_t, loff_t *); -/*================ Global variables ================*/ - -struct file_operations hfs_hdr_operations = { - .llseek = hdr_llseek, - .read = hdr_read, - .write = hdr_write, - .fsync = file_fsync, -}; - -struct inode_operations hfs_hdr_inode_operations = { - .setattr = hfs_notify_change_hdr, -}; - -const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = { - .magic = __constant_htonl(HFS_DBL_MAGIC), /* magic */ - .version = __constant_htonl(HFS_HDR_VERSION_2), /* version */ - .entries = 6, /* entries */ - { /* descr[] */ - {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, - {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16}, - {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32}, - {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4}, - {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4}, - {HFS_HDR_RSRC, HFS_DBL_HDR_LEN, ~0} - }, - { /* order[] */ - (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[0], - (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[1], - (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[2], - (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[3], - (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[4], - (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[5] - } -}; - -const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout = { - .magic = __constant_htonl(HFS_DBL_MAGIC), /* magic */ - .version = __constant_htonl(HFS_HDR_VERSION_2), /* version */ - .entries = 5, /* entries */ - { /* descr[] */ - {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, - {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16}, - {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32}, - {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4}, - {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4} - }, - { /* order[] */ - (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[0], - (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[1], - (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[2], - (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[3], - (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[4] - } -}; - -const struct hfs_hdr_layout hfs_nat2_hdr_layout = { - .magic = __constant_htonl(HFS_DBL_MAGIC), /* magic */ - .version = __constant_htonl(HFS_HDR_VERSION_2), /* version */ - .entries = 9, /* entries */ - { /* descr[] */ - {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, - {HFS_HDR_COMNT, offsetof(struct hfs_dbl_hdr, comment), 0}, - {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16}, - {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32}, - {HFS_HDR_AFPI, offsetof(struct hfs_dbl_hdr, fileinfo), 4}, - {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4}, - {HFS_HDR_SNAME, offsetof(struct hfs_dbl_hdr, short_name), ~0}, - {HFS_HDR_PRODOSI, offsetof(struct hfs_dbl_hdr, prodosi), 8}, - {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0} - }, - { /* order[] */ - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[5], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[6], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[7], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[8] - } -}; - -const struct hfs_hdr_layout hfs_nat_hdr_layout = { - .magic = __constant_htonl(HFS_DBL_MAGIC), /* magic */ - .version = __constant_htonl(HFS_HDR_VERSION_1), /* version */ - .entries = 5, /* entries */ - { /* descr[] */ - {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, - {HFS_HDR_COMNT, offsetof(struct hfs_dbl_hdr, comment), 0}, - {HFS_HDR_OLDI, offsetof(struct hfs_dbl_hdr, create_time), 16}, - {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32}, - {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0}, - }, - { /* order[] */ - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4] - } -}; - -/*================ File-local variables ================*/ - -static const char fstype[16] = - {'M','a','c','i','n','t','o','s','h',' ',' ',' ',' ',' ',' ',' '}; - -/*================ File-local data types ================*/ - -struct hdr_hdr { - hfs_lword_t magic; - hfs_lword_t version; - hfs_byte_t filler[16]; - hfs_word_t entries; - hfs_byte_t descrs[12*HFS_HDR_MAX]; -} __attribute__((packed)); - -/*================ File-local functions ================*/ - -/* - * dlength() - */ -static int dlength(const struct hfs_hdr_descr *descr, - const struct hfs_cat_entry *entry) -{ - hfs_u32 length = descr->length; - - /* handle auto-sized entries */ - if (length == ~0) { - switch (descr->id) { - case HFS_HDR_DATA: - if (entry->type == HFS_CDR_FIL) { - length = entry->u.file.data_fork.lsize; - } else { - length = 0; - } - break; - - case HFS_HDR_RSRC: - if (entry->type == HFS_CDR_FIL) { - length = entry->u.file.rsrc_fork.lsize; - } else { - length = 0; - } - break; - - case HFS_HDR_FNAME: - length = entry->key.CName.Len; - break; - - case HFS_HDR_SNAME: - default: - length = 0; - } - } - return length; -} - -/* - * hdr_build_meta() - */ -static void hdr_build_meta(struct hdr_hdr *meta, - const struct hfs_hdr_layout *layout, - const struct hfs_cat_entry *entry) -{ - const struct hfs_hdr_descr *descr; - hfs_byte_t *ptr; - int lcv; - - hfs_put_nl(layout->magic, meta->magic); - hfs_put_nl(layout->version, meta->version); - if (layout->version == htonl(HFS_HDR_VERSION_1)) { - memcpy(meta->filler, fstype, 16); - } else { - memset(meta->filler, 0, 16); - } - hfs_put_hs(layout->entries, meta->entries); - memset(meta->descrs, 0, sizeof(meta->descrs)); - for (lcv = 0, descr = layout->descr, ptr = meta->descrs; - lcv < layout->entries; ++lcv, ++descr, ptr += 12) { - hfs_put_hl(descr->id, ptr); - hfs_put_hl(descr->offset, ptr + 4); - hfs_put_hl(dlength(descr, entry), ptr + 8); - } -} - -/* - * dup_layout () - */ -static struct hfs_hdr_layout *dup_layout(const struct hfs_hdr_layout *old) -{ - struct hfs_hdr_layout *new; - int lcv; - - if (HFS_NEW(new)) { - memcpy(new, old, sizeof(*new)); - for (lcv = 0; lcv < new->entries; ++lcv) { - new->order[lcv] = (struct hfs_hdr_descr *) - ((char *)new->order[lcv] + ((char *)new - (char *)old)); - } - } - return new; -} - -/* - * init_layout() - */ -static inline void init_layout(struct hfs_hdr_layout *layout, - const hfs_byte_t *descrs) -{ - struct hfs_hdr_descr **base, **p, **q, *tmp; - int lcv, entries = layout->entries; - - for (lcv = 0; lcv < entries; ++lcv, descrs += 12) { - layout->order[lcv] = &layout->descr[lcv]; - layout->descr[lcv].id = hfs_get_hl(descrs); - layout->descr[lcv].offset = hfs_get_hl(descrs + 4); - layout->descr[lcv].length = hfs_get_hl(descrs + 8); - } - for (lcv = layout->entries; lcv < HFS_HDR_MAX; ++lcv) { - layout->order[lcv] = NULL; - layout->descr[lcv].id = 0; - layout->descr[lcv].offset = 0; - layout->descr[lcv].length = 0; - } - - /* Sort the 'order' array using an insertion sort */ - base = &layout->order[0]; - for (p = (base+1); p < (base+entries); ++p) { - q=p; - while ((*q)->offset < (*(q-1))->offset) { - tmp = *q; - *q = *(q-1); - *(--q) = tmp; - if (q == base) break; - } - } -} - -/* - * adjust_forks() - */ -static inline void adjust_forks(struct hfs_cat_entry *entry, - const struct hfs_hdr_layout *layout) -{ - int lcv; - - for (lcv = 0; lcv < layout->entries; ++lcv) { - const struct hfs_hdr_descr *descr = &layout->descr[lcv]; - - if ((descr->id == HFS_HDR_DATA) && - (descr->length != entry->u.file.data_fork.lsize)) { - entry->u.file.data_fork.lsize = descr->length; - hfs_extent_adj(&entry->u.file.data_fork); - } else if ((descr->id == HFS_HDR_RSRC) && - (descr->length != entry->u.file.rsrc_fork.lsize)) { - entry->u.file.rsrc_fork.lsize = descr->length; - hfs_extent_adj(&entry->u.file.rsrc_fork); - } - } -} - -/* - * get_dates() - */ -static void get_dates(const struct hfs_cat_entry *entry, - const struct inode *inode, hfs_u32 dates[3]) -{ - dates[0] = hfs_m_to_htime(entry->create_date); - dates[1] = hfs_m_to_htime(entry->modify_date); - dates[2] = hfs_m_to_htime(entry->backup_date); -} - -/* - * set_dates() - */ -static void set_dates(struct hfs_cat_entry *entry, struct inode *inode, - const hfs_u32 *dates) -{ - hfs_u32 tmp; - - tmp = hfs_h_to_mtime(dates[0]); - if (entry->create_date != tmp) { - entry->create_date = tmp; - hfs_cat_mark_dirty(entry); - } - tmp = hfs_h_to_mtime(dates[1]); - if (entry->modify_date != tmp) { - entry->modify_date = tmp; - inode->i_ctime.tv_sec = inode->i_atime.tv_sec = inode->i_mtime.tv_sec = - hfs_h_to_utime(dates[1]); - inode->i_ctime.tv_nsec = 0; - inode->i_mtime.tv_nsec = 0; - inode->i_atime.tv_nsec = 0; - hfs_cat_mark_dirty(entry); - } - tmp = hfs_h_to_mtime(dates[2]); - if (entry->backup_date != tmp) { - entry->backup_date = tmp; - hfs_cat_mark_dirty(entry); - } -} - -loff_t hdr_llseek(struct file *file, loff_t offset, int origin) -{ - long long retval; - - lock_kernel(); - - switch (origin) { - case 2: - offset += file->f_dentry->d_inode->i_size; - break; - case 1: - offset += file->f_pos; - } - retval = -EINVAL; - if (offset>=0 && offset<file->f_dentry->d_inode->i_size) { - if (offset != file->f_pos) { - file->f_pos = offset; - } - retval = offset; - } - unlock_kernel(); - return retval; -} - -/* - * hdr_read() - * - * This is the read field in the inode_operations structure for - * header files. The purpose is to transfer up to 'count' bytes - * from the file corresponding to 'inode', beginning at - * 'filp->offset' bytes into the file. The data is transferred to - * user-space at the address 'buf'. Returns the number of bytes - * successfully transferred. - */ -/* XXX: what about the entry count changing on us? */ -static hfs_rwret_t hdr_read(struct file *filp, char __user *buf, - hfs_rwarg_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - const struct hfs_hdr_layout *layout; - off_t start, length, offset; - off_t pos = *ppos; - int left, lcv, read = 0; - - if (!S_ISREG(inode->i_mode)) { - hfs_warn("hfs_hdr_read: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - - if (HFS_I(inode)->layout) { - layout = HFS_I(inode)->layout; - } else { - layout = HFS_I(inode)->default_layout; - } - - /* Adjust count to fit within the bounds of the file */ - if ((pos >= inode->i_size) || (count <= 0)) { - return 0; - } else if (count > inode->i_size - pos) { - count = inode->i_size - pos; - } - - /* Handle the fixed-location portion */ - length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 + - sizeof(hfs_u16) + layout->entries * (3 * sizeof(hfs_u32)); - if (pos < length) { - struct hdr_hdr meta; - - left = length - pos; - if (left > count) { - left = count; - } - - hdr_build_meta(&meta, layout, entry); - left -= copy_to_user(buf, ((char *)&meta) + pos, left); - count -= left; - read += left; - pos += left; - buf += left; - } - if (!count) { - goto done; - } - - /* Handle the actual data */ - for (lcv = 0; count && (lcv < layout->entries); ++lcv) { - const struct hfs_hdr_descr *descr = layout->order[lcv]; - struct hfs_fork *fork; - char tmp[16], *p; - off_t limit; - - /* stop reading if we run out of descriptors early */ - if (!descr) { - break; - } - - /* find start and length of this entry */ - start = descr->offset; - length = dlength(descr, entry); - - /* Skip to next entry if this one is empty or isn't needed */ - if (!length || (pos >= start + length)) { - continue; - } - - /* Pad with zeros to the start of this entry if needed */ - if (pos < start) { - left = start - pos; - if (left > count) { - left = count; - } - clear_user(buf, left); - count -= left; - read += left; - pos += left; - buf += left; - } - if (!count) { - goto done; - } - - /* locate and/or construct the data for this entry */ - fork = NULL; - p = NULL; - switch (descr->id) { - case HFS_HDR_DATA: - fork = &entry->u.file.data_fork; - limit = fork->lsize; - break; - - case HFS_HDR_RSRC: - fork = &entry->u.file.rsrc_fork; - limit = fork->lsize; - break; - - case HFS_HDR_FNAME: - p = entry->key.CName.Name; - limit = entry->key.CName.Len; - break; - - case HFS_HDR_OLDI: - case HFS_HDR_DATES: - get_dates(entry, inode, (hfs_u32 *)tmp); - if (descr->id == HFS_HDR_DATES) { - /* XXX: access date. hfsplus actually - has this. */ - memcpy(tmp + 12, tmp + 4, 4); - } else if ((entry->type == HFS_CDR_FIL) && - (entry->u.file.flags & HFS_FIL_LOCK)) { - hfs_put_hl(HFS_AFP_RDONLY, tmp + 12); - } else { - hfs_put_nl(0, tmp + 12); - } - p = tmp; - limit = 16; - break; - - case HFS_HDR_FINFO: - p = (char *)&entry->info; - limit = 32; - break; - - case HFS_HDR_AFPI: - /* XXX: this needs to do more mac->afp mappings */ - hfs_put_ns(0, tmp); - if ((entry->type == HFS_CDR_FIL) && - (entry->u.file.flags & HFS_FIL_LOCK)) { - hfs_put_hs(HFS_AFP_RDONLY, tmp + 2); - } else { - hfs_put_ns(0, tmp + 2); - } - p = tmp; - limit = 4; - break; - - case HFS_HDR_PRODOSI: - /* XXX: this needs to do mac->prodos translations */ - memset(tmp, 0, 8); -#if 0 - hfs_put_ns(0, tmp); /* access */ - hfs_put_ns(0, tmp); /* type */ - hfs_put_nl(0, tmp); /* aux type */ -#endif - p = tmp; - limit = 8; - break; - - case HFS_HDR_MACI: - hfs_put_ns(0, tmp); - if (entry->type == HFS_CDR_FIL) { - hfs_put_hs(entry->u.file.flags, tmp + 2); - } else { - hfs_put_ns(entry->u.dir.flags, tmp + 2); - } - p = tmp; - limit = 4; - break; - - case HFS_HDR_DID: - /* if it's rootinfo, stick the next available did in - * the did slot. */ - limit = 4; - if (entry->cnid == htonl(HFS_ROOT_CNID)) { - struct hfs_mdb *mdb = entry->mdb; - const struct hfs_name *reserved = - HFS_SB(mdb->sys_mdb)->s_reserved2; - - while (reserved->Len) { - if (hfs_streq(reserved->Name, - reserved->Len, - entry->key.CName.Name, - entry->key.CName.Len)) { - hfs_put_hl(mdb->next_id, tmp); - p = tmp; - goto hfs_did_done; - } - reserved++; - } - } - p = (char *) &entry->cnid; -hfs_did_done: - break; - - case HFS_HDR_SNAME: - default: - limit = 0; - } - - /* limit the transfer to the available data - of to the stated length of the entry. */ - if (length > limit) { - length = limit; - } - offset = pos - start; - left = length - offset; - if (left > count) { - left = count; - } - if (left <= 0) { - continue; - } - - /* transfer the data */ - if (p) { - left -= copy_to_user(buf, p + offset, left); - } else if (fork) { - left = hfs_do_read(inode, fork, offset, buf, left); - if (left > 0) { - } else if (!read) { - return left; - } else { - goto done; - } - } - count -= left; - read += left; - pos += left; - buf += left; - } - - /* Pad the file out with zeros */ - if (count) { - clear_user(buf, count); - read += count; - pos += count; - } - -done: - if (read) { - inode->i_atime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - } - return read; -} - -/* - * hdr_write() - * - * This is the write() entry in the file_operations structure for - * header files. The purpose is to transfer up to 'count' bytes - * to the file corresponding to 'inode' beginning at offset - * '*ppos' from user-space at the address 'buf'. - * The return value is the number of bytes actually transferred. - */ -static hfs_rwret_t hdr_write(struct file *filp, const char __user *buf, - hfs_rwarg_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - struct hfs_hdr_layout *layout; - off_t start, length, offset; - int left, lcv, written = 0; - struct hdr_hdr meta; - int built_meta = 0; - off_t pos; - - if (!S_ISREG(inode->i_mode)) { - hfs_warn("hfs_hdr_write: mode = %07o\n", inode->i_mode); - return -EINVAL; - } - if (count <= 0) { - return 0; - } - - pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos; - - if (!HFS_I(inode)->layout) { - HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout); - } - layout = HFS_I(inode)->layout; - - /* Handle the 'magic', 'version', 'filler' and 'entries' fields */ - length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 + sizeof(hfs_u16); - if (pos < length) { - hdr_build_meta(&meta, layout, entry); - built_meta = 1; - - left = length - pos; - if (left > count) { - left = count; - } - - left -= copy_from_user(((char *)&meta) + pos, buf, left); - layout->magic = hfs_get_nl(meta.magic); - layout->version = hfs_get_nl(meta.version); - layout->entries = hfs_get_hs(meta.entries); - if (layout->entries > HFS_HDR_MAX) { - /* XXX: should allocate slots dynamically */ - hfs_warn("hfs_hdr_write: TRUNCATING TO %d " - "DESCRIPTORS\n", HFS_HDR_MAX); - layout->entries = HFS_HDR_MAX; - } - - count -= left; - written += left; - pos += left; - buf += left; - } - if (!count) { - goto done; - } - - /* We know for certain how many entries we have, so process them */ - length += layout->entries * 3 * sizeof(hfs_u32); - if (pos < length) { - if (!built_meta) { - hdr_build_meta(&meta, layout, entry); - } - - left = length - pos; - if (left > count) { - left = count; - } - - left -= copy_from_user(((char *)&meta) + pos, buf, left); - init_layout(layout, meta.descrs); - - count -= left; - written += left; - pos += left; - buf += left; - - /* Handle possible size changes for the forks */ - if (entry->type == HFS_CDR_FIL) { - adjust_forks(entry, layout); - hfs_cat_mark_dirty(entry); - } - } - - /* Handle the actual data */ - for (lcv = 0; count && (lcv < layout->entries); ++lcv) { - struct hfs_hdr_descr *descr = layout->order[lcv]; - struct hfs_fork *fork; - char tmp[16], *p; - off_t limit; - - /* stop writing if we run out of descriptors early */ - if (!descr) { - break; - } - - /* find start and length of this entry */ - start = descr->offset; - if ((descr->id == HFS_HDR_DATA) || - (descr->id == HFS_HDR_RSRC)) { - if (entry->type == HFS_CDR_FIL) { - length = 0x7fffffff - start; - } else { - continue; - } - } else { - length = dlength(descr, entry); - } - - /* Trim length to avoid overlap with the next entry */ - if (layout->order[lcv+1] && - ((start + length) > layout->order[lcv+1]->offset)) { - length = layout->order[lcv+1]->offset - start; - } - - /* Skip to next entry if this one is empty or isn't needed */ - if (!length || (pos >= start + length)) { - continue; - } - - /* Skip any padding that may exist between entries */ - if (pos < start) { - left = start - pos; - if (left > count) { - left = count; - } - count -= left; - written += left; - pos += left; - buf += left; - } - if (!count) { - goto done; - } - - /* locate and/or construct the data for this entry */ - fork = NULL; - p = NULL; - switch (descr->id) { - case HFS_HDR_DATA: -#if 0 -/* Can't yet write to the data fork via a header file, since there is the - * possibility to write via the data file, and the only locking is at the - * inode level. - */ - fork = &entry->u.file.data_fork; - limit = length; -#else - limit = 0; -#endif - break; - - case HFS_HDR_RSRC: - fork = &entry->u.file.rsrc_fork; - limit = length; - break; - - case HFS_HDR_OLDI: - case HFS_HDR_DATES: - get_dates(entry, inode, (hfs_u32 *)tmp); - if (descr->id == HFS_HDR_DATES) { - memcpy(tmp + 12, tmp + 4, 4); - } else if ((entry->type == HFS_CDR_FIL) && - (entry->u.file.flags & HFS_FIL_LOCK)) { - hfs_put_hl(HFS_AFP_RDONLY, tmp + 12); - } else { - hfs_put_nl(0, tmp + 12); - } - p = tmp; - limit = 16; - break; - - case HFS_HDR_FINFO: - p = (char *)&entry->info; - limit = 32; - break; - - case HFS_HDR_AFPI: - hfs_put_ns(0, tmp); - if ((entry->type == HFS_CDR_FIL) && - (entry->u.file.flags & HFS_FIL_LOCK)) { - hfs_put_hs(HFS_AFP_RDONLY, tmp + 2); - } else { - hfs_put_ns(0, tmp + 2); - } - p = tmp; - limit = 4; - break; - - case HFS_HDR_PRODOSI: - /* XXX: this needs to do mac->prodos translations */ - memset(tmp, 0, 8); -#if 0 - hfs_put_ns(0, tmp); /* access */ - hfs_put_ns(0, tmp); /* type */ - hfs_put_nl(0, tmp); /* aux type */ -#endif - p = tmp; - limit = 8; - break; - - case HFS_HDR_MACI: - hfs_put_ns(0, tmp); - if (entry->type == HFS_CDR_FIL) { - hfs_put_hs(entry->u.file.flags, tmp + 2); - } else { - hfs_put_ns(entry->u.dir.flags, tmp + 2); - } - p = tmp; - limit = 4; - break; - - case HFS_HDR_FNAME: /* Can't rename a file this way */ - case HFS_HDR_DID: /* can't specify a did this way */ - default: - limit = 0; - } - - /* limit the transfer to the available data - of to the stated length of the entry. */ - if (length > limit) { - length = limit; - } - offset = pos - start; - left = length - offset; - if (left > count) { - left = count; - } - if (left <= 0) { - continue; - } - - /* transfer the data from user space */ - if (p) { - left -= copy_from_user(p + offset, buf, left); - } else if (fork) { - left = hfs_do_write(inode, fork, offset, buf, left); - } - - /* process the data */ - switch (descr->id) { - case HFS_HDR_OLDI: - set_dates(entry, inode, (hfs_u32 *)tmp); - if (entry->type == HFS_CDR_FIL) { - hfs_u8 new_flags = entry->u.file.flags; - - if (hfs_get_nl(tmp+12) & htonl(HFS_AFP_WRI)) { - new_flags |= HFS_FIL_LOCK; - } else { - new_flags &= ~HFS_FIL_LOCK; - } - - if (new_flags != entry->u.file.flags) { - entry->u.file.flags = new_flags; - hfs_cat_mark_dirty(entry); - hfs_file_fix_mode(entry); - } - } - break; - - case HFS_HDR_DATES: - set_dates(entry, inode, (hfs_u32 *)tmp); - break; - - case HFS_HDR_FINFO: - hfs_cat_mark_dirty(entry); - break; - - case HFS_HDR_MACI: - if (entry->type == HFS_CDR_DIR) { - hfs_u16 new_flags = hfs_get_ns(tmp + 2); - - if (entry->u.dir.flags != new_flags) { - entry->u.dir.flags = new_flags; - hfs_cat_mark_dirty(entry); - } - } else { - hfs_u8 new_flags = tmp[3]; - hfs_u8 changed = entry->u.file.flags^new_flags; - - if (changed) { - entry->u.file.flags = new_flags; - hfs_cat_mark_dirty(entry); - if (changed & HFS_FIL_LOCK) { - hfs_file_fix_mode(entry); - } - } - } - break; - - case HFS_HDR_DATA: - case HFS_HDR_RSRC: - if (left <= 0) { - if (!written) { - return left; - } else { - goto done; - } - } else if (fork->lsize > descr->length) { - descr->length = fork->lsize; - } - break; - - case HFS_HDR_FNAME: /* Can't rename a file this way */ - case HFS_HDR_DID: /* Can't specify a did this way */ - case HFS_HDR_PRODOSI: /* not implemented yet */ - case HFS_HDR_AFPI: /* ditto */ - default: - break; - } - - count -= left; - written += left; - pos += left; - buf += left; - } - - /* Skip any padding at the end */ - if (count) { - written += count; - pos += count; - } - -done: - *ppos = pos; - if (written > 0) { - if (pos > inode->i_size) - inode->i_size = pos; - inode->i_mtime = inode->i_atime = CURRENT_TIME; - mark_inode_dirty(inode); - } - return written; -} - -/* - * hdr_truncate() - * - * This is the truncate field in the inode_operations structure for - * header files. The purpose is to allocate or release blocks as needed - * to satisfy a change in file length. - */ -void hdr_truncate(struct inode *inode, size_t size) -{ - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - struct hfs_hdr_layout *layout; - int lcv, last; - - inode->i_size = size; - if (!HFS_I(inode)->layout) { - HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout); - } - layout = HFS_I(inode)->layout; - - last = layout->entries - 1; - for (lcv = 0; lcv <= last; ++lcv) { - struct hfs_hdr_descr *descr = layout->order[lcv]; - struct hfs_fork *fork; - hfs_u32 offset; - - if (!descr) { - break; - } - - if (descr->id == HFS_HDR_RSRC) { - fork = &entry->u.file.rsrc_fork; -#if 0 -/* Can't yet truncate the data fork via a header file, since there is the - * possibility to truncate via the data file, and the only locking is at - * the inode level. - */ - } else if (descr->id == HFS_HDR_DATA) { - fork = &entry->u.file.data_fork; -#endif - } else { - continue; - } - - offset = descr->offset; - - if ((lcv != last) && ((offset + descr->length) <= size)) { - continue; - } - - if (offset < size) { - descr->length = size - offset; - } else { - descr->length = 0; - } - if (fork->lsize != descr->length) { - fork->lsize = descr->length; - hfs_extent_adj(fork); - hfs_cat_mark_dirty(entry); - } - } -} diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h index adf912cbec0c..73ac40810d15 100644 --- a/fs/hfs/hfs.h +++ b/fs/hfs/hfs.h @@ -1,21 +1,14 @@ -/* - * linux/fs/hfs/hfs.h +/* + * linux/fs/hfs/hfs.h * * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. - * - * "XXX" in a comment is a note to myself to consider changing something. */ #ifndef _HFS_H #define _HFS_H -#include <linux/hfs_sysdep.h> - -#define HFS_NEW(X) ((X) = hfs_malloc(sizeof(*(X)))) -#define HFS_DELETE(X) do { hfs_free((X), sizeof(*(X))); (X) = NULL; } \ - while (0) - /* offsets to various blocks */ #define HFS_DD_BLK 0 /* Driver Descriptor block */ #define HFS_PMAP_BLK 1 /* First block of partition map */ @@ -28,46 +21,32 @@ #define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */ #define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */ -/* magic numbers for various internal structures */ -#define HFS_FILE_MAGIC 0x4801 -#define HFS_DIR_MAGIC 0x4802 -#define HFS_MDB_MAGIC 0x4803 -#define HFS_EXT_MAGIC 0x4804 /* XXX currently unused */ -#define HFS_BREC_MAGIC 0x4811 /* XXX currently unused */ -#define HFS_BTREE_MAGIC 0x4812 -#define HFS_BNODE_MAGIC 0x4813 - /* various FIXED size parameters */ #define HFS_SECTOR_SIZE 512 /* size of an HFS sector */ #define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */ #define HFS_NAMELEN 31 /* maximum length of an HFS filename */ -#define HFS_NAMEMAX (3*31) /* max size of ENCODED filename */ -#define HFS_BM_MAXBLOCKS (16) /* max number of bitmap blocks */ -#define HFS_BM_BPB (8*HFS_SECTOR_SIZE) /* number of bits per bitmap block */ #define HFS_MAX_VALENCE 32767U -#define HFS_FORK_MAX (0x7FFFFFFF) /* Meanings of the drAtrb field of the MDB, * Reference: _Inside Macintosh: Files_ p. 2-61 */ -#define HFS_SB_ATTRIB_HLOCK 0x0080 -#define HFS_SB_ATTRIB_CLEAN 0x0100 -#define HFS_SB_ATTRIB_SPARED 0x0200 -#define HFS_SB_ATTRIB_SLOCK 0x8000 - -/* 2**16 - 1 */ -#define HFS_USHRT_MAX 65535 +#define HFS_SB_ATTRIB_HLOCK (1 << 7) +#define HFS_SB_ATTRIB_UNMNT (1 << 8) +#define HFS_SB_ATTRIB_SPARED (1 << 9) +#define HFS_SB_ATTRIB_INCNSTNT (1 << 11) +#define HFS_SB_ATTRIB_SLOCK (1 << 15) /* Some special File ID numbers */ -#define HFS_POR_CNID 1 /* Parent Of the Root */ -#define HFS_ROOT_CNID 2 /* ROOT directory */ -#define HFS_EXT_CNID 3 /* EXTents B-tree */ -#define HFS_CAT_CNID 4 /* CATalog B-tree */ -#define HFS_BAD_CNID 5 /* BAD blocks file */ -#define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */ -#define HFS_START_CNID 7 /* STARTup file (HFS+) */ -#define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */ -#define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */ +#define HFS_POR_CNID 1 /* Parent Of the Root */ +#define HFS_ROOT_CNID 2 /* ROOT directory */ +#define HFS_EXT_CNID 3 /* EXTents B-tree */ +#define HFS_CAT_CNID 4 /* CATalog B-tree */ +#define HFS_BAD_CNID 5 /* BAD blocks file */ +#define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */ +#define HFS_START_CNID 7 /* STARTup file (HFS+) */ +#define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */ +#define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */ +#define HFS_FIRSTUSER_CNID 16 /* values for hfs_cat_rec.cdrType */ #define HFS_CDR_DIR 0x01 /* folder (directory) */ @@ -85,7 +64,6 @@ #define HFS_FIL_DOPEN 0x04 /* data fork open */ #define HFS_FIL_ROPEN 0x08 /* resource fork open */ #define HFS_FIL_DIR 0x10 /* directory (always clear) */ -#define HFS_FIL_RSRV1 0x20 /* reserved */ #define HFS_FIL_NOCOPY 0x40 /* copy-protected file */ #define HFS_FIL_USED 0x80 /* open */ @@ -96,449 +74,214 @@ #define HFS_DIR_MOUNTED 0x08 /* mounted */ #define HFS_DIR_DIR 0x10 /* directory (always set) */ #define HFS_DIR_EXPFOLDER 0x20 /* share point */ -#define HFS_DIR_RSRV1 0x40 /* reserved */ -#define HFS_DIR_RSRV2 0x80 /* reserved */ - -/* Access types used when requesting access to a B-node */ -#define HFS_LOCK_NONE 0x0000 /* Illegal */ -#define HFS_LOCK_READ 0x0001 /* read-only access */ -#define HFS_LOCK_RESRV 0x0002 /* might potentially modify */ -#define HFS_LOCK_WRITE 0x0003 /* will modify now (exclusive access) */ -#define HFS_LOCK_MASK 0x000f - -/* Flags field of the hfs_path_elem */ -#define HFS_BPATH_FIRST 0x0100 -#define HFS_BPATH_OVERFLOW 0x0200 -#define HFS_BPATH_UNDERFLOW 0x0400 -#define HFS_BPATH_MASK 0x0f00 - -/* Flags for hfs_bfind() */ -#define HFS_BFIND_EXACT 0x0010 -#define HFS_BFIND_LOCK 0x0020 - -/* Modes for hfs_bfind() */ -#define HFS_BFIND_WRITE (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BFIND_LOCK) -#define HFS_BFIND_READ_EQ (HFS_LOCK_READ|HFS_BFIND_EXACT) -#define HFS_BFIND_READ_LE (HFS_LOCK_READ) -#define HFS_BFIND_INSERT (HFS_LOCK_RESRV|HFS_BPATH_FIRST|HFS_BPATH_OVERFLOW) -#define HFS_BFIND_DELETE \ - (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BPATH_FIRST|HFS_BPATH_UNDERFLOW) + +/* bits hfs_finfo.fdFlags */ +#define HFS_FLG_INITED 0x0100 +#define HFS_FLG_LOCKED 0x1000 +#define HFS_FLG_INVISIBLE 0x4000 /*======== HFS structures as they appear on the disk ========*/ +#define __packed __attribute__ ((packed)) + /* Pascal-style string of up to 31 characters */ struct hfs_name { - hfs_byte_t Len; - hfs_byte_t Name[31]; -} __attribute__((packed)); - -typedef struct { - hfs_word_t v; - hfs_word_t h; -} hfs_point_t; - -typedef struct { - hfs_word_t top; - hfs_word_t left; - hfs_word_t bottom; - hfs_word_t right; -} hfs_rect_t; - -typedef struct { - hfs_lword_t fdType; - hfs_lword_t fdCreator; - hfs_word_t fdFlags; - hfs_point_t fdLocation; - hfs_word_t fdFldr; -} __attribute__((packed)) hfs_finfo_t; - -typedef struct { - hfs_word_t fdIconID; - hfs_byte_t fdUnused[8]; - hfs_word_t fdComment; - hfs_lword_t fdPutAway; -} __attribute__((packed)) hfs_fxinfo_t; - -typedef struct { - hfs_rect_t frRect; - hfs_word_t frFlags; - hfs_point_t frLocation; - hfs_word_t frView; -} __attribute__((packed)) hfs_dinfo_t; - -typedef struct { - hfs_point_t frScroll; - hfs_lword_t frOpenChain; - hfs_word_t frUnused; - hfs_word_t frComment; - hfs_lword_t frPutAway; -} __attribute__((packed)) hfs_dxinfo_t; + u8 len; + u8 name[HFS_NAMELEN]; +} __packed; + +struct hfs_point { + u16 v; + u16 h; +} __packed; + +struct hfs_rect { + u16 top; + u16 left; + u16 bottom; + u16 right; +} __packed; + +struct hfs_finfo { + u32 fdType; + u32 fdCreator; + u16 fdFlags; + struct hfs_point fdLocation; + u16 fdFldr; +} __packed; + +struct hfs_fxinfo { + u16 fdIconID; + u8 fdUnused[8]; + u16 fdComment; + u32 fdPutAway; +} __packed; + +struct hfs_dinfo { + struct hfs_rect frRect; + u16 frFlags; + struct hfs_point frLocation; + u16 frView; +} __packed; + +struct hfs_dxinfo { + struct hfs_point frScroll; + u32 frOpenChain; + u16 frUnused; + u16 frComment; + u32 frPutAway; +} __packed; union hfs_finder_info { struct { - hfs_finfo_t finfo; - hfs_fxinfo_t fxinfo; + struct hfs_finfo finfo; + struct hfs_fxinfo fxinfo; } file; struct { - hfs_dinfo_t dinfo; - hfs_dxinfo_t dxinfo; + struct hfs_dinfo dinfo; + struct hfs_dxinfo dxinfo; } dir; -}; - -/* A btree record key on disk */ -struct hfs_bkey { - hfs_byte_t KeyLen; /* number of bytes in the key */ - hfs_byte_t value[1]; /* (KeyLen) bytes of key */ -} __attribute__((packed)); +} __packed; /* Cast to a pointer to a generic bkey */ #define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X))) /* The key used in the catalog b-tree: */ struct hfs_cat_key { - hfs_byte_t KeyLen; /* number of bytes in the key */ - hfs_byte_t Resrv1; /* padding */ - hfs_lword_t ParID; /* CNID of the parent dir */ + u8 key_len; /* number of bytes in the key */ + u8 reserved; /* padding */ + u32 ParID; /* CNID of the parent dir */ struct hfs_name CName; /* The filename of the entry */ -} __attribute__((packed)); +} __packed; /* The key used in the extents b-tree: */ struct hfs_ext_key { - hfs_byte_t KeyLen; /* number of bytes in the key */ - hfs_byte_t FkType; /* HFS_FK_{DATA,RSRC} */ - hfs_lword_t FNum; /* The File ID of the file */ - hfs_word_t FABN; /* allocation blocks number*/ -} __attribute__((packed)); + u8 key_len; /* number of bytes in the key */ + u8 FkType; /* HFS_FK_{DATA,RSRC} */ + u32 FNum; /* The File ID of the file */ + u16 FABN; /* allocation blocks number*/ +} __packed; -/*======== Data structures kept in memory ========*/ +typedef union hfs_btree_key { + u8 key_len; /* number of bytes in the key */ + struct hfs_cat_key cat; + struct hfs_ext_key ext; +} hfs_btree_key; -/* - * struct hfs_mdb - * - * The fields from the MDB of an HFS filesystem - */ -struct hfs_mdb { - int magic; /* A magic number */ - unsigned char vname[28]; /* The volume name */ - hfs_sysmdb sys_mdb; /* superblock */ - hfs_buffer buf; /* The hfs_buffer - holding the real - superblock (aka VIB - or MDB) */ - hfs_buffer alt_buf; /* The hfs_buffer holding - the alternate superblock */ - hfs_buffer bitmap[16]; /* The hfs_buffer holding the - allocation bitmap */ - struct hfs_btree * ext_tree; /* Information about - the extents b-tree */ - struct hfs_btree * cat_tree; /* Information about - the catalog b-tree */ - hfs_u32 file_count; /* The number of - regular files in - the filesystem */ - hfs_u32 dir_count; /* The number of - directories in the - filesystem */ - hfs_u32 next_id; /* The next available - file id number */ - hfs_u32 clumpablks; /* The number of allocation - blocks to try to add when - extending a file */ - hfs_u32 write_count; /* The number of MDB - writes (a sort of - version number) */ - hfs_u32 fs_start; /* The first 512-byte - block represented - in the bitmap */ - hfs_u32 create_date; /* In network byte-order */ - hfs_u32 modify_date; /* In network byte-order */ - hfs_u32 backup_date; /* In network byte-order */ - hfs_u16 root_files; /* The number of - regular - (non-directory) - files in the root - directory */ - hfs_u16 root_dirs; /* The number of - directories in the - root directory */ - hfs_u16 fs_ablocks; /* The number of - allocation blocks - in the filesystem */ - hfs_u16 free_ablocks; /* The number of unused - allocation blocks - in the filesystem */ - hfs_u32 alloc_blksz; /* The number of - 512-byte blocks per - "allocation block" */ - hfs_u16 attrib; /* Attribute word */ - struct semaphore bitmap_sem; - struct list_head entry_dirty; -}; +typedef union hfs_btree_key btree_key; -/* - * struct hfs_extent - * - * The offset to allocation block mapping for a given file is - * contained in a series of these structures. Each (struct - * hfs_extent) records up to three runs of contiguous allocation - * blocks. An allocation block is a contiguous group of physical - * blocks. - */ struct hfs_extent { - int magic; /* A magic number */ - unsigned short start; /* Where in the file this record - begins (in allocation blocks) */ - unsigned short end; /* Where in the file this record - ends (in allocation blocks) */ - unsigned short block[3]; /* The allocation block on disk which - begins this extent */ - unsigned short length[3]; /* The number of allocation blocks - in this extent */ - struct hfs_extent *next; /* Next extent record for this file */ - struct hfs_extent *prev; /* Previous extent record for this file */ - int count; /* Number of times it is used */ -}; - -/* - * struct hfs_dir - * - * This structure holds information specific - * to a directory in an HFS filesystem. - */ -struct hfs_dir { - int magic; /* A magic number */ - hfs_u16 flags; - hfs_u16 dirs; /* Number of directories in this one */ - hfs_u16 files; /* Number of files in this directory */ - struct rw_semaphore sem; -}; - -/* - * struct hfs_fork - * - * This structure holds the information - * specific to a single fork of a file. - */ -struct hfs_fork { - struct hfs_cat_entry *entry; /* The file this fork is part of */ - struct hfs_extent first; /* The first extent record for - this fork */ - struct hfs_extent *cache; /* The most-recently accessed - extent record for this fork */ - hfs_u32 lsize; /* The logical size in bytes */ - hfs_u32 psize; /* The phys size (512-byte blocks) */ - hfs_u8 fork; /* Which fork is this? */ -}; - -/* - * struct hfs_file - * - * This structure holds information specific - * to a file in an HFS filesystem. - */ -struct hfs_file { - int magic; - struct hfs_fork data_fork; - struct hfs_fork rsrc_fork; - hfs_u16 clumpablks; - hfs_u8 flags; -}; - -/* - * struct hfs_file - * - * This structure holds information about a - * file or directory in an HFS filesystem. - * - * 'wait' must remain 1st and 'hash' 2nd since we do some pointer arithmetic. - */ -struct hfs_cat_entry { - hfs_wait_queue wait; - struct list_head hash; - struct list_head list; - struct hfs_mdb *mdb; - hfs_sysentry sys_entry; - struct hfs_cat_key key; - union hfs_finder_info info; - hfs_u32 cnid; /* In network byte-order */ - hfs_u32 create_date; /* In network byte-order */ - hfs_u32 modify_date; /* In network byte-order */ - hfs_u32 backup_date; /* In network byte-order */ - unsigned short count; - unsigned long state; - hfs_u8 type; - union { - struct hfs_dir dir; - struct hfs_file file; - } u; + u16 block; + u16 count; }; +typedef struct hfs_extent hfs_extent_rec[3]; + +/* The catalog record for a file */ +struct hfs_cat_file { + s8 type; /* The type of entry */ + u8 reserved; + u8 Flags; /* Flags such as read-only */ + s8 Typ; /* file version number = 0 */ + struct hfs_finfo UsrWds; /* data used by the Finder */ + u32 FlNum; /* The CNID */ + u16 StBlk; /* obsolete */ + u32 LgLen; /* The logical EOF of the data fork*/ + u32 PyLen; /* The physical EOF of the data fork */ + u16 RStBlk; /* obsolete */ + u32 RLgLen; /* The logical EOF of the rsrc fork */ + u32 RPyLen; /* The physical EOF of the rsrc fork */ + u32 CrDat; /* The creation date */ + u32 MdDat; /* The modified date */ + u32 BkDat; /* The last backup date */ + struct hfs_fxinfo FndrInfo; /* more data for the Finder */ + u16 ClpSize; /* number of bytes to allocate + when extending files */ + hfs_extent_rec ExtRec; /* first extent record + for the data fork */ + hfs_extent_rec RExtRec; /* first extent record + for the resource fork */ + u32 Resrv; /* reserved by Apple */ +} __packed; + +/* the catalog record for a directory */ +struct hfs_cat_dir { + s8 type; /* The type of entry */ + u8 reserved; + u16 Flags; /* flags */ + u16 Val; /* Valence: number of files and + dirs in the directory */ + u32 DirID; /* The CNID */ + u32 CrDat; /* The creation date */ + u32 MdDat; /* The modification date */ + u32 BkDat; /* The last backup date */ + struct hfs_dinfo UsrInfo; /* data used by the Finder */ + struct hfs_dxinfo FndrInfo; /* more data used by Finder */ + u8 Resrv[16]; /* reserved by Apple */ +} __packed; + +/* the catalog record for a thread */ +struct hfs_cat_thread { + s8 type; /* The type of entry */ + u8 reserved[9]; /* reserved by Apple */ + u32 ParID; /* CNID of parent directory */ + struct hfs_name CName; /* The name of this entry */ +} __packed; + +/* A catalog tree record */ +typedef union hfs_cat_rec { + s8 type; /* The type of entry */ + struct hfs_cat_file file; + struct hfs_cat_dir dir; + struct hfs_cat_thread thread; +} hfs_cat_rec; -/* hfs entry state bits */ -#define HFS_DIRTY 1 -#define HFS_KEYDIRTY 2 -#define HFS_LOCK 4 -#define HFS_DELETED 8 - -/* - * struct hfs_bnode_ref - * - * A pointer to a (struct hfs_bnode) and the type of lock held on it. - */ -struct hfs_bnode_ref { - struct hfs_bnode *bn; - int lock_type; -}; +struct hfs_mdb { + u16 drSigWord; /* Signature word indicating fs type */ + u32 drCrDate; /* fs creation date/time */ + u32 drLsMod; /* fs modification date/time */ + u16 drAtrb; /* fs attributes */ + u16 drNmFls; /* number of files in root directory */ + u16 drVBMSt; /* location (in 512-byte blocks) + of the volume bitmap */ + u16 drAllocPtr; /* location (in allocation blocks) + to begin next allocation search */ + u16 drNmAlBlks; /* number of allocation blocks */ + u32 drAlBlkSiz; /* bytes in an allocation block */ + u32 drClpSiz; /* clumpsize, the number of bytes to + allocate when extending a file */ + u16 drAlBlSt; /* location (in 512-byte blocks) + of the first allocation block */ + u32 drNxtCNID; /* CNID to assign to the next + file or directory created */ + u16 drFreeBks; /* number of free allocation blocks */ + u8 drVN[28]; /* the volume label */ + u32 drVolBkUp; /* fs backup date/time */ + u16 drVSeqNum; /* backup sequence number */ + u32 drWrCnt; /* fs write count */ + u32 drXTClpSiz; /* clumpsize for the extents B-tree */ + u32 drCTClpSiz; /* clumpsize for the catalog B-tree */ + u16 drNmRtDirs; /* number of directories in + the root directory */ + u32 drFilCnt; /* number of files in the fs */ + u32 drDirCnt; /* number of directories in the fs */ + u8 drFndrInfo[32]; /* data used by the Finder */ + u16 drEmbedSigWord; /* embedded volume signature */ + u32 drEmbedExtent; /* starting block number (xdrStABN) + and number of allocation blocks + (xdrNumABlks) occupied by embedded + volume */ + u32 drXTFlSize; /* bytes in the extents B-tree */ + hfs_extent_rec drXTExtRec; /* extents B-tree's first 3 extents */ + u32 drCTFlSize; /* bytes in the catalog B-tree */ + hfs_extent_rec drCTExtRec; /* catalog B-tree's first 3 extents */ +} __packed; -/* - * struct hfs_belem - * - * An element of the path from the root of a B-tree to a leaf. - * Includes the reference to a (struct hfs_bnode), the index of - * the appropriate record in that node, and some flags. - */ -struct hfs_belem { - struct hfs_bnode_ref bnr; - int record; - int flags; -}; +/*======== Data structures kept in memory ========*/ -/* - * struct hfs_brec - * - * The structure returned by hfs_bfind() to describe the requested record. - */ -struct hfs_brec { - int keep_flags; - struct hfs_btree *tree; - struct hfs_belem *top; - struct hfs_belem *bottom; - struct hfs_belem elem[9]; - struct hfs_bkey *key; - void *data; /* The actual data */ +struct hfs_readdir_data { + struct list_head list; + struct file *file; + struct hfs_cat_key key; }; -/*================ Function prototypes ================*/ - -/* bdelete.c */ -extern int hfs_bdelete(struct hfs_btree *, const struct hfs_bkey *); - -/* bfind.c */ -extern void hfs_brec_relse(struct hfs_brec *, struct hfs_belem *); -extern int hfs_bsucc(struct hfs_brec *, int); -extern int hfs_bfind(struct hfs_brec *, struct hfs_btree *, - const struct hfs_bkey *, int); - -/* binsert.c */ -extern int hfs_binsert(struct hfs_btree *, const struct hfs_bkey *, - const void *, hfs_u16); - -/* bitmap.c */ -extern hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *, hfs_u16); -extern hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *, hfs_u16 *); -extern int hfs_set_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16); -extern int hfs_clear_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16); - -/* bitops.c */ -extern hfs_u32 hfs_find_zero_bit(const hfs_u32 *, hfs_u32, hfs_u32); -extern hfs_u32 hfs_count_zero_bits(const hfs_u32 *, hfs_u32, hfs_u32); - -/* btree.c */ -extern struct hfs_btree *hfs_btree_init(struct hfs_mdb *, ino_t, - hfs_byte_t *, hfs_u32, hfs_u32); -extern void hfs_btree_free(struct hfs_btree *); -extern void hfs_btree_commit(struct hfs_btree *, hfs_byte_t *, hfs_lword_t); - -/* catalog.c */ -extern void hfs_cat_init(void); -extern void hfs_cat_put(struct hfs_cat_entry *); -extern void hfs_cat_mark_dirty(struct hfs_cat_entry *); -extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *, - const struct hfs_cat_key *); - -extern void hfs_cat_invalidate(struct hfs_mdb *); -extern void hfs_cat_commit(struct hfs_mdb *); -extern void hfs_cat_free(void); - -extern int hfs_cat_compare(const struct hfs_cat_key *, - const struct hfs_cat_key *); -extern void hfs_cat_build_key(hfs_u32, const struct hfs_name *, - struct hfs_cat_key *); -extern struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *); - -extern int hfs_cat_open(struct hfs_cat_entry *, struct hfs_brec *); -extern int hfs_cat_next(struct hfs_cat_entry *, struct hfs_brec *, - hfs_u16, hfs_u32 *, hfs_u8 *); -extern void hfs_cat_close(struct hfs_cat_entry *, struct hfs_brec *); - -extern int hfs_cat_create(struct hfs_cat_entry *, struct hfs_cat_key *, - hfs_u8, hfs_u32, hfs_u32, struct hfs_cat_entry **); -extern int hfs_cat_mkdir(struct hfs_cat_entry *, struct hfs_cat_key *, - struct hfs_cat_entry **); -extern int hfs_cat_delete(struct hfs_cat_entry *, struct hfs_cat_entry *, int); -extern int hfs_cat_move(struct hfs_cat_entry *, struct hfs_cat_entry *, - struct hfs_cat_entry *, struct hfs_cat_key *, - struct hfs_cat_entry **); - -/* extent.c */ -extern int hfs_ext_compare(const struct hfs_ext_key *, - const struct hfs_ext_key *); -extern void hfs_extent_in(struct hfs_fork *, const hfs_byte_t *); -extern void hfs_extent_out(const struct hfs_fork *, hfs_byte_t *); -extern int hfs_extent_map(struct hfs_fork *, int, int); -extern void hfs_extent_adj(struct hfs_fork *); -extern void hfs_extent_free(struct hfs_fork *); - -/* file.c */ -extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int); - -/* mdb.c */ -extern struct hfs_mdb *hfs_mdb_get(hfs_sysmdb, int, hfs_s32); -extern void hfs_mdb_commit(struct hfs_mdb *, int); -extern void hfs_mdb_put(struct hfs_mdb *, int); - -/* part_tbl.c */ -extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *); - -/* string.c */ -extern unsigned int hfs_strhash(const unsigned char *, unsigned int); -extern int hfs_strcmp(const unsigned char *, unsigned int, - const unsigned char *, unsigned int); -extern int hfs_streq(const unsigned char *, unsigned int, - const unsigned char *, unsigned int); -extern void hfs_tolower(unsigned char *, int); - -static __inline__ struct dentry -*hfs_lookup_dentry(struct dentry *base, const char *name, const int len) -{ - struct qstr this; - - this.name = name; - this.len = len; - this.hash = hfs_strhash(name, len); - - return d_lookup(base, &this); -} - -/* drop a dentry for one of the special directories. - * it's in the form of base/name/dentry. */ -static __inline__ void hfs_drop_special(struct dentry *base, - const struct hfs_name *name, - struct dentry *dentry) -{ - struct dentry *dparent, *de; - - dparent = hfs_lookup_dentry(base, name->Name, name->Len); - if (dparent) { - de = hfs_lookup_dentry(dparent, dentry->d_name.name, - dentry->d_name.len); - if (de) { - if (!de->d_inode) - d_drop(de); - dput(de); - } - dput(dparent); - } -} - -extern struct dentry_operations hfs_dentry_operations; #endif diff --git a/fs/hfs/hfs_btree.h b/fs/hfs/hfs_btree.h deleted file mode 100644 index b4bc1069ecfd..000000000000 --- a/fs/hfs/hfs_btree.h +++ /dev/null @@ -1,289 +0,0 @@ -/* - * linux/fs/hfs/hfs_btree.h - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the declarations of the private B-tree - * structures and functions. - * - * "XXX" in a comment is a note to myself to consider changing something. - */ - -#ifndef _HFS_BTREE_H -#define _HFS_BTREE_H - -#include "hfs.h" - -/*================ Variable-like macros ================*/ - -/* The stickiness of a (struct hfs_bnode) */ -#define HFS_NOT_STICKY 0 -#define HFS_STICKY 1 - -/* The number of hash buckets in a B-tree's bnode cache */ -#define HFS_CACHELEN 17 /* primes are best? */ - -/* - * Legal values for the 'ndType' field of a (struct NodeDescriptor) - * - * Reference: _Inside Macintosh: Files_ p. 2-65 - */ -#define ndIndxNode 0x00 /* An internal (index) node */ -#define ndHdrNode 0x01 /* The tree header node (node 0) */ -#define ndMapNode 0x02 /* Holds part of the bitmap of used nodes */ -#define ndLeafNode 0xFF /* A leaf (ndNHeight==1) node */ - -/* - * Legal values for the bthAtrb field of a (struct BTHdrRec) - * - * Reference: TN 1150 - */ -#define bthBadClose 0x00000001 /* b-tree not closed properly. not - used by hfsplus. */ -#define bthBigKeys 0x00000002 /* key length is u16 instead of u8. - used by hfsplus. */ -#define bthVarIndxKeys 0x00000004 /* variable key length instead of - max key length. use din catalog - b-tree but not in extents - b-tree (hfsplus). */ - -/*================ Function-like macros ================*/ - -/* Access the cache slot which should contain the desired node */ -#define bhash(tree, node) ((tree)->cache[(node) % HFS_CACHELEN]) - -/* round up to multiple of sizeof(hfs_u16) */ -#define ROUND(X) ((X + sizeof(hfs_u16) - 1) & ~(sizeof(hfs_u16)-1)) - -/* Refer to the (base-1) array of offsets in a bnode */ -#define RECTBL(X,N) \ - (((hfs_u16 *)(hfs_buffer_data((X)->buf)+HFS_SECTOR_SIZE))-(N)) - -/*================ Private data types ================*/ - -/* - * struct BTHdrRec - * - * The B-tree header record - * - * This data structure is stored in the first node (512-byte block) of - * each B-tree file. It contains important information about the - * B-tree. Most fields vary over the life of the tree and are - * indicated by a 'V' in the comments. The other fields are fixed for - * the life of the tree and are indicated by a 'F'. - * - * Reference: _Inside Macintosh: Files_ pp. 2-68 through 2-69 */ -struct BTHdrRec { - hfs_word_t bthDepth; /* (V) The number of levels in this B-tree */ - hfs_lword_t bthRoot; /* (V) The node number of the root node */ - hfs_lword_t bthNRecs; /* (V) The number of leaf records */ - hfs_lword_t bthFNode; /* (V) The number of the first leaf node */ - hfs_lword_t bthLNode; /* (V) The number of the last leaf node */ - hfs_word_t bthNodeSize; /* (F) The number of bytes in a node (=512) */ - hfs_word_t bthKeyLen; /* (F) The length of a key in an index node */ - hfs_lword_t bthNNodes; /* (V) The total number of nodes */ - hfs_lword_t bthFree; /* (V) The number of unused nodes */ - hfs_word_t bthResv1; /* reserved */ - hfs_lword_t bthClpSiz; /* (F) clump size. not usually used. */ - hfs_byte_t bthType; /* (F) BTree type */ - hfs_byte_t bthResv2; /* reserved */ - hfs_lword_t bthAtrb; /* (F) attributes */ - hfs_lword_t bthResv3[16]; /* Reserved */ -} __attribute__((packed)); - -/* - * struct NodeDescriptor - * - * The B-tree node descriptor. - * - * This structure begins each node in the B-tree file. It contains - * important information about the node's contents. 'V' and 'F' in - * the comments indicate fields that are variable or fixed over the - * life of a node, where the 'life' of a node is defined as the period - * between leaving and reentering the free pool. - * - * Reference: _Inside Macintosh: Files_ p. 2-64 - */ -struct NodeDescriptor { - hfs_lword_t ndFLink; /* (V) Number of the next node at this level */ - hfs_lword_t ndBLink; /* (V) Number of the prev node at this level */ - hfs_byte_t ndType; /* (F) The type of node */ - hfs_byte_t ndNHeight; /* (F) The level of this node (leaves=1) */ - hfs_word_t ndNRecs; /* (V) The number of records in this node */ - hfs_word_t ndResv2; /* Reserved */ -} __attribute__((packed)); - -/* - * typedef hfs_cmpfn - * - * The type 'hfs_cmpfn' is a comparison function taking 2 keys and - * returning a positive, negative or zero integer according to the - * ordering of the two keys (just like strcmp() does for strings). - */ -typedef int (*hfs_cmpfn)(const void *, const void *); - -/* - * struct hfs_bnode - * - * An in-core B-tree node - * - * This structure holds information from the NodeDescriptor in native - * byte-order, a pointer to the buffer which contains the actual - * node and fields necessary for locking access to the node during - * updates. The use of the locking fields is explained with the - * locking functions. - */ -struct hfs_bnode { - int magic; /* Magic number to guard against - wild pointers */ - hfs_buffer buf; /* The buffer containing the - actual node */ - struct hfs_btree *tree; /* The tree to which this node - belongs */ - struct hfs_bnode *prev; /* Next node in this hash bucket */ - struct hfs_bnode *next; /* Previous node in this hash - bucket */ - int sticky; /* Boolean: non-zero means keep - this node in-core (set for - root and head) */ - hfs_u32 node; /* Node number */ - hfs_u16 nodeSize; /* node size */ - hfs_u16 keyLen; /* key length */ - /* locking related fields: */ - hfs_wait_queue wqueue; /* Wait queue for write access */ - hfs_wait_queue rqueue; /* Wait queue for read or reserve - access */ - int count; /* Number of processes accessing - this node */ - int resrv; /* Boolean, true means a process - had placed a 'reservation' on - this node */ - int lock; /* Boolean, true means some - process has exclusive access, - so KEEP OUT */ - /* fields from the NodeDescriptor in native byte-order: */ - hfs_u32 ndFLink; - hfs_u32 ndBLink; - hfs_u16 ndNRecs; - hfs_u8 ndType; - hfs_u8 ndNHeight; -}; - -/* - * struct hfs_btree - * - * An in-core B-tree. - * - * This structure holds information from the BTHdrRec, MDB - * (superblock) and other information needed to work with the B-tree. - */ -struct hfs_btree { - int magic; /* Magic number to - guard against wild - pointers */ - hfs_cmpfn compare; /* Comparison function - for this tree */ - struct hfs_bnode head; /* in-core copy of node 0 */ - struct hfs_bnode *root; /* Pointer to the in-core - copy of the root node */ - hfs_sysmdb sys_mdb; /* The "device" holding - the filesystem */ - int reserved; /* bnodes claimed but - not yet used */ - struct hfs_bnode /* The bnode cache */ - *cache[HFS_CACHELEN]; - struct hfs_cat_entry entry; /* Fake catalog entry */ - struct semaphore sem; - int dirt; - int keySize; - /* Fields from the BTHdrRec in native byte-order: */ - hfs_u32 bthRoot; - hfs_u32 bthNRecs; - hfs_u32 bthFNode; - hfs_u32 bthLNode; - hfs_u32 bthNNodes; - hfs_u32 bthFree; - hfs_u16 bthKeyLen; - hfs_u16 bthDepth; -}; - -/*================ Global functions ================*/ - -/* Convert a (struct hfs_bnode *) and an index to the value of the - n-th offset in the bnode (N >= 1) to the offset */ -extern inline hfs_u16 bnode_offset(const struct hfs_bnode *bnode, int n) -{ return hfs_get_hs(RECTBL(bnode,n)); } - -/* Convert a (struct hfs_bnode *) and an index to the size of the - n-th record in the bnode (N >= 1) */ -extern inline hfs_u16 bnode_rsize(const struct hfs_bnode *bnode, int n) -{ return bnode_offset(bnode, n+1) - bnode_offset(bnode, n); } - -/* Convert a (struct hfs_bnode *) to the offset of the empty part */ -extern inline hfs_u16 bnode_end(const struct hfs_bnode *bnode) -{ return bnode_offset(bnode, bnode->ndNRecs + 1); } - -/* Convert a (struct hfs_bnode *) to the number of free bytes it contains */ -extern inline hfs_u16 bnode_freespace(const struct hfs_bnode *bnode) -{ return HFS_SECTOR_SIZE - bnode_end(bnode) - - (bnode->ndNRecs + 1)*sizeof(hfs_u16); } - -/* Convert a (struct hfs_bnode *) X and an index N to - the address of the record N in the bnode (N >= 1) */ -extern inline void *bnode_datastart(const struct hfs_bnode *bnode) -{ return (void *)(hfs_buffer_data(bnode->buf)+sizeof(struct NodeDescriptor)); } - -/* Convert a (struct hfs_bnode *) to the address of the empty part */ -extern inline void *bnode_dataend(const struct hfs_bnode *bnode) -{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_end(bnode)); } - -/* Convert various pointers to address of record's key */ -extern inline void *bnode_key(const struct hfs_bnode *bnode, int n) -{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_offset(bnode, n)); } -extern inline void *belem_key(const struct hfs_belem *elem) -{ return bnode_key(elem->bnr.bn, elem->record); } -extern inline void *brec_key(const struct hfs_brec *brec) -{ return belem_key(brec->bottom); } - -/* Convert various pointers to the address of a record */ -extern inline void *bkey_record(const struct hfs_bkey *key) -{ return (void *)key + ROUND(key->KeyLen + 1); } -extern inline void *bnode_record(const struct hfs_bnode *bnode, int n) -{ return bkey_record(bnode_key(bnode, n)); } -extern inline void *belem_record(const struct hfs_belem *elem) -{ return bkey_record(belem_key(elem)); } -extern inline void *brec_record(const struct hfs_brec *brec) -{ return bkey_record(brec_key(brec)); } - -/*================ Function Prototypes ================*/ - -/* balloc.c */ -extern int hfs_bnode_bitop(struct hfs_btree *, hfs_u32, int); -extern struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *); -extern int hfs_bnode_free(struct hfs_bnode_ref *); -extern void hfs_btree_extend(struct hfs_btree *); - -/* bins_del.c */ -extern void hfs_bnode_update_key(struct hfs_brec *, struct hfs_belem *, - struct hfs_bnode *, int); -extern void hfs_bnode_shift_right(struct hfs_bnode *, struct hfs_bnode *, int); -extern void hfs_bnode_shift_left(struct hfs_bnode *, struct hfs_bnode *, int); -extern int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec); - -/* bnode.c */ -extern void hfs_bnode_read(struct hfs_bnode *, struct hfs_btree *, - hfs_u32, int); -extern void hfs_bnode_relse(struct hfs_bnode_ref *); -extern struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *, hfs_u32, int); -extern void hfs_bnode_lock(struct hfs_bnode_ref *, int); -extern void hfs_bnode_delete(struct hfs_bnode *); -extern void hfs_bnode_commit(struct hfs_bnode *); - -/* brec.c */ -extern void hfs_brec_lock(struct hfs_brec *, struct hfs_belem *); -extern struct hfs_belem *hfs_brec_init(struct hfs_brec *, struct hfs_btree *, - int); -extern struct hfs_belem *hfs_brec_next(struct hfs_brec *); - -#endif diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h new file mode 100644 index 000000000000..33876db2230e --- /dev/null +++ b/fs/hfs/hfs_fs.h @@ -0,0 +1,291 @@ +/* + * linux/fs/hfs/hfs_fs.h + * + * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * This file may be distributed under the terms of the GNU General Public License. + */ + +#ifndef _LINUX_HFS_FS_H +#define _LINUX_HFS_FS_H + +#include <linux/version.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/buffer_head.h> +#include <linux/fs.h> + +#include <asm/byteorder.h> +#include <asm/uaccess.h> + +#include "hfs.h" + +#define DBG_BNODE_REFS 0x00000001 +#define DBG_BNODE_MOD 0x00000002 +#define DBG_CAT_MOD 0x00000004 +#define DBG_INODE 0x00000008 +#define DBG_SUPER 0x00000010 +#define DBG_EXTENT 0x00000020 +#define DBG_BITMAP 0x00000040 + +//#define DBG_MASK (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD|DBG_CAT_MOD|DBG_BITMAP) +//#define DBG_MASK (DBG_BNODE_MOD|DBG_CAT_MOD|DBG_INODE) +//#define DBG_MASK (DBG_CAT_MOD|DBG_BNODE_REFS|DBG_INODE|DBG_EXTENT) +#define DBG_MASK (0) + +#define dprint(flg, fmt, args...) \ + if (flg & DBG_MASK) printk(fmt , ## args) + +#define hfs_warn(format, args...) printk(KERN_WARNING format , ## args) +#define hfs_error(format, args...) printk(KERN_ERR format , ## args) + +/* + * struct hfs_inode_info + * + * The HFS-specific part of a Linux (struct inode) + */ +struct hfs_inode_info { + atomic_t opencnt; + + unsigned int flags; + + /* to deal with localtime ugliness */ + int tz_secondswest; + + struct hfs_cat_key cat_key; + + struct list_head open_dir_list; + struct inode *rsrc_inode; + + struct semaphore extents_lock; + + u16 alloc_blocks, clump_blocks; + /* Allocation extents from catlog record or volume header */ + hfs_extent_rec first_extents; + u16 first_blocks; + hfs_extent_rec cached_extents; + u16 cached_start, cached_blocks; + + loff_t phys_size; + struct inode vfs_inode; +}; + +#define HFS_FLG_RSRC 0x0001 +#define HFS_FLG_EXT_DIRTY 0x0002 +#define HFS_FLG_EXT_NEW 0x0004 + +#define HFS_IS_RSRC(inode) (HFS_I(inode)->flags & HFS_FLG_RSRC) + +/* + * struct hfs_sb_info + * + * The HFS-specific part of a Linux (struct super_block) + */ +struct hfs_sb_info { + struct buffer_head *mdb_bh; /* The hfs_buffer + holding the real + superblock (aka VIB + or MDB) */ + struct hfs_mdb *mdb; + struct buffer_head *alt_mdb_bh; /* The hfs_buffer holding + the alternate superblock */ + struct hfs_mdb *alt_mdb; + u32 *bitmap; /* The page holding the + allocation bitmap */ + struct hfs_btree *ext_tree; /* Information about + the extents b-tree */ + struct hfs_btree *cat_tree; /* Information about + the catalog b-tree */ + u32 file_count; /* The number of + regular files in + the filesystem */ + u32 folder_count; /* The number of + directories in the + filesystem */ + u32 next_id; /* The next available + file id number */ + u32 clumpablks; /* The number of allocation + blocks to try to add when + extending a file */ + u32 fs_start; /* The first 512-byte + block represented + in the bitmap */ + u32 part_start; + u16 root_files; /* The number of + regular + (non-directory) + files in the root + directory */ + u16 root_dirs; /* The number of + directories in the + root directory */ + u16 fs_ablocks; /* The number of + allocation blocks + in the filesystem */ + u16 free_ablocks; /* the number of unused + allocation blocks + in the filesystem */ + u32 alloc_blksz; /* The size of an + "allocation block" */ + int s_quiet; /* Silent failure when + changing owner or mode? */ + u32 s_type; /* Type for new files */ + u32 s_creator; /* Creator for new files */ + umode_t s_file_umask; /* The umask applied to the + permissions on all files */ + umode_t s_dir_umask; /* The umask applied to the + permissions on all dirs */ + uid_t s_uid; /* The uid of all files */ + gid_t s_gid; /* The gid of all files */ + + int session, part; + + struct semaphore bitmap_lock; + + unsigned long flags; + + u16 blockoffset; + + int fs_div; + + struct hlist_head rsrc_inodes; +}; + +#define HFS_FLG_BITMAP_DIRTY 0 +#define HFS_FLG_MDB_DIRTY 1 +#define HFS_FLG_ALT_MDB_DIRTY 2 + +/* bitmap.c */ +extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *); +extern int hfs_clear_vbm_bits(struct super_block *, u16, u16); + +/* catalog.c */ +extern int hfs_cat_keycmp(const btree_key *, const btree_key *); +struct hfs_find_data; +extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *); +extern int hfs_cat_create(u32, struct inode *, struct qstr *, struct inode *); +extern int hfs_cat_delete(u32, struct inode *, struct qstr *); +extern int hfs_cat_move(u32, struct inode *, struct qstr *, + struct inode *, struct qstr *); +extern void hfs_cat_build_key(btree_key *, u32, struct qstr *); + +/* dir.c */ +extern struct file_operations hfs_dir_operations; +extern struct inode_operations hfs_dir_inode_operations; + +extern int hfs_mkdir(struct inode *, struct dentry *, int); +extern int hfs_unlink(struct inode *, struct dentry *); +extern int hfs_rmdir(struct inode *, struct dentry *); +extern int hfs_rename(struct inode *, struct dentry *, + struct inode *, struct dentry *); + +/* extent.c */ +extern int hfs_ext_keycmp(const btree_key *, const btree_key *); +extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int); +extern void hfs_ext_write_extent(struct inode *); +extern int hfs_extend_file(struct inode *); +extern void hfs_file_truncate(struct inode *); + +/* file.c */ +extern struct inode_operations hfs_file_inode_operations; +extern struct file_operations hfs_file_operations; + +extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int); + +/* inode.c */ +extern struct address_space_operations hfs_aops; +extern struct address_space_operations hfs_btree_aops; + +extern struct inode *hfs_new_inode(struct inode *, struct qstr *, int); +extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, u32 *, u32 *); +extern void hfs_write_inode(struct inode *, int); +extern int hfs_inode_setattr(struct dentry *, struct iattr *); +extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext, + u32 log_size, u32 phys_size, u32 clump_size); +extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *); +extern void hfs_clear_inode(struct inode *); +extern void hfs_delete_inode(struct inode *); + +/* mdb.c */ +extern int hfs_mdb_get(struct super_block *); +extern void hfs_mdb_commit(struct super_block *); +extern void hfs_mdb_close(struct super_block *); +extern void hfs_mdb_put(struct super_block *); + +/* part_tbl.c */ +extern int hfs_part_find(struct super_block *, sector_t *, sector_t *); + +/* string.c */ +extern struct dentry_operations hfs_dentry_operations; + +extern int hfs_hash_dentry(struct dentry *, struct qstr *); +extern int hfs_strcmp(const unsigned char *, unsigned int, + const unsigned char *, unsigned int); +extern int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); + +/* super.c */ +extern struct super_block *hfs_read_super(struct super_block *,void *,int); + +/* trans.c */ +extern void hfs_triv2mac(struct hfs_name *, struct qstr *); +extern int hfs_mac2triv(char *, const struct hfs_name *); + +extern struct timezone sys_tz; + +/* + * There are two time systems. Both are based on seconds since + * a particular time/date. + * Unix: unsigned lil-endian since 00:00 GMT, Jan. 1, 1970 + * mac: unsigned big-endian since 00:00 GMT, Jan. 1, 1904 + * + */ +#define __hfs_u_to_mtime(sec) cpu_to_be32(sec + 2082844800U - sys_tz.tz_minuteswest * 60) +#define __hfs_m_to_utime(sec) (be32_to_cpu(sec) - 2082844800U + sys_tz.tz_minuteswest * 60) + +#define HFS_I(inode) (list_entry(inode, struct hfs_inode_info, vfs_inode)) +#define HFS_SB(sb) ((struct hfs_sb_info *)(sb)->s_fs_info) + +#define hfs_m_to_utime(time) (struct timespec){ .tv_sec = __hfs_m_to_utime(time) } +#define hfs_u_to_mtime(time) __hfs_u_to_mtime((time).tv_sec) +#define hfs_mtime() __hfs_u_to_mtime(get_seconds()) + +static inline const char *hfs_mdb_name(struct super_block *sb) +{ + return sb->s_id; +} + +static inline void hfs_bitmap_dirty(struct super_block *sb) +{ + set_bit(HFS_FLG_BITMAP_DIRTY, &HFS_SB(sb)->flags); + sb->s_dirt = 1; +} + +static inline void hfs_buffer_sync(struct buffer_head *bh) +{ + while (buffer_locked(bh)) { + wait_on_buffer(bh); + } + if (buffer_dirty(bh)) { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + } +} + +#define sb_bread512(sb, sec, data) ({ \ + struct buffer_head *__bh; \ + sector_t __block; \ + loff_t __start; \ + int __offset; \ + \ + __start = (loff_t)(sec) << HFS_SECTOR_SIZE_BITS;\ + __block = __start >> (sb)->s_blocksize_bits; \ + __offset = __start & ((sb)->s_blocksize - 1); \ + __bh = sb_bread((sb), __block); \ + if (likely(__bh != NULL)) \ + data = (void *)(__bh->b_data + __offset);\ + else \ + data = NULL; \ + __bh; \ +}) + +#endif diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 398de286c3e7..e6a2ea549dba 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -1,259 +1,342 @@ /* - * linux/fs/hfs/inode.c + * linux/fs/hfs/inode.c * * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * This file contains inode-related functions which do not depend on * which scheme is being used to represent forks. * * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. */ -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> #include <linux/pagemap.h> -#include <linux/smp_lock.h> +#include <linux/version.h> +#include <linux/mpage.h> + +#include "hfs_fs.h" +#include "btree.h" /*================ Variable-like macros ================*/ #define HFS_VALID_MODE_BITS (S_IFREG | S_IFDIR | S_IRWXUGO) -/*================ File-local functions ================*/ +static int hfs_writepage(struct page *page, struct writeback_control *wbc) +{ + return block_write_full_page(page, hfs_get_block, wbc); +} -/* - * init_file_inode() - * - * Given an HFS catalog entry initialize an inode for a file. - */ -static void init_file_inode(struct inode *inode, hfs_u8 fork) +static int hfs_readpage(struct file *file, struct page *page) { - struct hfs_fork *fk; - struct hfs_cat_entry *entry = HFS_I(inode)->entry; + return block_read_full_page(page, hfs_get_block); +} - if (fork == HFS_FK_DATA) { - inode->i_mode = S_IRWXUGO | S_IFREG; - } else { - inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; - } +static int hfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page, from, to, hfs_get_block, + &HFS_I(page->mapping->host)->phys_size); +} - if (fork == HFS_FK_DATA) { -#if 0 /* XXX: disable crlf translations for now */ - hfs_u32 type = hfs_get_nl(entry->info.file.finfo.fdType); - - HFS_I(inode)->convert = - ((HFS_SB(inode->i_sb)->s_conv == 't') || - ((HFS_SB(inode->i_sb)->s_conv == 'a') && - ((type == htonl(0x54455854)) || /* "TEXT" */ - (type == htonl(0x7474726f))))); /* "ttro" */ -#else - HFS_I(inode)->convert = 0; -#endif - fk = &entry->u.file.data_fork; +static sector_t hfs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping, block, hfs_get_block); +} + +int hfs_releasepage(struct page *page, int mask) +{ + struct inode *inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + struct hfs_btree *tree; + struct hfs_bnode *node; + u32 nidx; + int i, res = 1; + + switch (inode->i_ino) { + case HFS_EXT_CNID: + tree = HFS_SB(sb)->ext_tree; + break; + case HFS_CAT_CNID: + tree = HFS_SB(sb)->cat_tree; + break; + default: + BUG(); + return 0; + } + if (tree->node_size >= PAGE_CACHE_SIZE) { + nidx = page->index >> (tree->node_size_shift - PAGE_CACHE_SHIFT); + spin_lock(&tree->hash_lock); + node = hfs_bnode_findhash(tree, nidx); + if (!node) + ; + else if (atomic_read(&node->refcnt)) + res = 0; + else for (i = 0; i < tree->pages_per_bnode; i++) { + if (PageActive(node->page[i])) { + res = 0; + break; + } + } + if (res && node) { + hfs_bnode_unhash(node); + hfs_bnode_free(node); + } + spin_unlock(&tree->hash_lock); } else { - fk = &entry->u.file.rsrc_fork; - HFS_I(inode)->convert = 0; + nidx = page->index << (PAGE_CACHE_SHIFT - tree->node_size_shift); + i = 1 << (PAGE_CACHE_SHIFT - tree->node_size_shift); + spin_lock(&tree->hash_lock); + do { + node = hfs_bnode_findhash(tree, nidx++); + if (!node) + continue; + if (atomic_read(&node->refcnt)) { + res = 0; + break; + } + hfs_bnode_unhash(node); + hfs_bnode_free(node); + } while (--i); + spin_unlock(&tree->hash_lock); } - HFS_I(inode)->fork = fk; - inode->i_size = fk->lsize; - inode->i_blocks = fk->psize; - inode->i_nlink = 1; + //printk("releasepage: %lu,%x = %d\n", page->index, mask, res); + return res; } -/*================ Global functions ================*/ +static int hfs_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, + struct buffer_head *bh_result, int create) +{ + int ret; -/* - * hfs_put_inode() - * - * This is the put_inode() entry in the super_operations for HFS - * filesystems. The purpose is to perform any filesystem-dependent - * cleanup necessary when the use-count of an inode falls to zero. - */ -void hfs_put_inode(struct inode * inode) + ret = hfs_get_block(inode, iblock, bh_result, create); + if (!ret) + bh_result->b_size = (1 << inode->i_blkbits); + return ret; +} + +static int hfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, + loff_t offset, unsigned long nr_segs) { - struct hfs_cat_entry *entry = HFS_I(inode)->entry; + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_dentry->d_inode->i_mapping->host; - lock_kernel(); - hfs_cat_put(entry); - if (atomic_read(&inode->i_count) == 1) { - struct hfs_hdr_layout *tmp = HFS_I(inode)->layout; + return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, hfs_get_blocks, NULL); +} - if (tmp) { - HFS_I(inode)->layout = NULL; - HFS_DELETE(tmp); - } - } - unlock_kernel(); +static int hfs_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + return mpage_writepages(mapping, wbc, hfs_get_block); } +struct address_space_operations hfs_btree_aops = { + .readpage = hfs_readpage, + .writepage = hfs_writepage, + .sync_page = block_sync_page, + .prepare_write = hfs_prepare_write, + .commit_write = generic_commit_write, + .bmap = hfs_bmap, + .releasepage = hfs_releasepage, +}; + +struct address_space_operations hfs_aops = { + .readpage = hfs_readpage, + .writepage = hfs_writepage, + .sync_page = block_sync_page, + .prepare_write = hfs_prepare_write, + .commit_write = generic_commit_write, + .bmap = hfs_bmap, + .direct_IO = hfs_direct_IO, + .writepages = hfs_writepages, +}; + /* - * hfs_notify_change() - * - * Based very closely on fs/msdos/inode.c by Werner Almesberger - * - * This is the notify_change() field in the super_operations structure - * for HFS file systems. The purpose is to take that changes made to - * an inode and apply then in a filesystem-dependent manner. In this - * case the process has a few of tasks to do: - * 1) prevent changes to the i_uid and i_gid fields. - * 2) map file permissions to the closest allowable permissions - * 3) Since multiple Linux files can share the same on-disk inode under - * HFS (for instance the data and resource forks of a file) a change - * to permissions must be applied to all other in-core inodes which - * correspond to the same HFS file. + * hfs_new_inode */ -enum {HFS_NORM, HFS_HDR, HFS_CAP}; - -static int __hfs_notify_change(struct dentry *dentry, struct iattr * attr, int kind) +struct inode *hfs_new_inode(struct inode *dir, struct qstr *name, int mode) { - struct inode *inode = dentry->d_inode; - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - struct dentry **de = entry->sys_entry; - struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); - int error=0, i; - - lock_kernel(); + struct super_block *sb = dir->i_sb; + struct inode *inode = new_inode(sb); + if (!inode) + return NULL; - error = inode_change_ok(inode, attr); /* basic permission checks */ - if (error) { - /* Let netatalk's afpd think chmod() always succeeds */ - if (hsb->s_afpd && - (attr->ia_valid == (ATTR_MODE | ATTR_CTIME))) { - error = 0; - } - goto out; + init_MUTEX(&HFS_I(inode)->extents_lock); + INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list); + hfs_cat_build_key((btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name); + inode->i_ino = HFS_SB(sb)->next_id++; + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_nlink = 1; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + HFS_I(inode)->flags = 0; + HFS_I(inode)->rsrc_inode = NULL; + if (S_ISDIR(inode->i_mode)) { + inode->i_size = 2; + HFS_SB(sb)->folder_count++; + if (dir->i_ino == HFS_ROOT_CNID) + HFS_SB(sb)->root_dirs++; + inode->i_op = &hfs_dir_inode_operations; + inode->i_fop = &hfs_dir_operations; + } else if (S_ISREG(inode->i_mode)) { + HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks; + HFS_SB(sb)->file_count++; + if (dir->i_ino == HFS_ROOT_CNID) + HFS_SB(sb)->root_files++; + inode->i_op = &hfs_file_inode_operations; + inode->i_fop = &hfs_file_operations; + inode->i_mapping->a_ops = &hfs_aops; + HFS_I(inode)->phys_size = 0; + HFS_I(inode)->alloc_blocks = 0; + HFS_I(inode)->first_blocks = 0; + HFS_I(inode)->cached_start = 0; + HFS_I(inode)->cached_blocks = 0; + memset(HFS_I(inode)->first_extents, 0, sizeof(hfs_extent_rec)); + memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec)); } + insert_inode_hash(inode); + mark_inode_dirty(inode); + set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); + sb->s_dirt = 1; - /* no uig/gid changes and limit which mode bits can be set */ - if (((attr->ia_valid & ATTR_UID) && - (attr->ia_uid != hsb->s_uid)) || - ((attr->ia_valid & ATTR_GID) && - (attr->ia_gid != hsb->s_gid)) || - ((attr->ia_valid & ATTR_MODE) && - (((entry->type == HFS_CDR_DIR) && - (attr->ia_mode != inode->i_mode))|| - (attr->ia_mode & ~HFS_VALID_MODE_BITS)))) { - if( hsb->s_quiet ) { - error = 0; - goto out; - } - } - - if (entry->type == HFS_CDR_DIR) { - attr->ia_valid &= ~ATTR_MODE; - } else if (attr->ia_valid & ATTR_MODE) { - /* Only the 'w' bits can ever change and only all together. */ - if (attr->ia_mode & S_IWUSR) { - attr->ia_mode = inode->i_mode | S_IWUGO; - } else { - attr->ia_mode = inode->i_mode & ~S_IWUGO; - } - attr->ia_mode &= ~hsb->s_umask; - } - /* - * Normal files handle size change in normal way. - * Oddballs are served here. - */ - if (attr->ia_valid & ATTR_SIZE) { - if (kind == HFS_CAP) { - inode->i_size = attr->ia_size; - if (inode->i_size > HFS_FORK_MAX) - inode->i_size = HFS_FORK_MAX; - mark_inode_dirty(inode); - attr->ia_valid &= ~ATTR_SIZE; - } else if (kind == HFS_HDR) { - hdr_truncate(inode, attr->ia_size); - attr->ia_valid &= ~ATTR_SIZE; - } - } - error = inode_setattr(inode, attr); - if (error) - goto out; - - /* We wouldn't want to mess with the sizes of the other fork */ - attr->ia_valid &= ~ATTR_SIZE; - - /* We must change all in-core inodes corresponding to this file. */ - for (i = 0; i < 4; ++i) { - if (de[i] && (de[i] != dentry)) { - inode_setattr(de[i]->d_inode, attr); - } - } + return inode; +} - /* Change the catalog entry if needed */ - if (attr->ia_valid & ATTR_MTIME) { - entry->modify_date = hfs_u_to_mtime(inode->i_mtime.tv_sec); - hfs_cat_mark_dirty(entry); +void hfs_delete_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + + dprint(DBG_INODE, "delete_inode: %lu\n", inode->i_ino); + if (S_ISDIR(inode->i_mode)) { + HFS_SB(sb)->folder_count--; + if (HFS_I(inode)->cat_key.ParID == be32_to_cpu(HFS_ROOT_CNID)) + HFS_SB(sb)->root_dirs--; + set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); + sb->s_dirt = 1; + return; } - if (attr->ia_valid & ATTR_MODE) { - hfs_u8 new_flags; - - if (inode->i_mode & S_IWUSR) { - new_flags = entry->u.file.flags & ~HFS_FIL_LOCK; - } else { - new_flags = entry->u.file.flags | HFS_FIL_LOCK; - } - - if (new_flags != entry->u.file.flags) { - entry->u.file.flags = new_flags; - hfs_cat_mark_dirty(entry); + HFS_SB(sb)->file_count--; + if (HFS_I(inode)->cat_key.ParID == be32_to_cpu(HFS_ROOT_CNID)) + HFS_SB(sb)->root_files--; + if (S_ISREG(inode->i_mode)) { + if (!inode->i_nlink) { + inode->i_size = 0; + hfs_file_truncate(inode); } } - /* size changes handled in hfs_extent_adj() */ - -out: - unlock_kernel(); - return error; + set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags); + sb->s_dirt = 1; } -int hfs_notify_change(struct dentry *dentry, struct iattr * attr) +void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext, + u32 log_size, u32 phys_size, u32 clump_size) { - return __hfs_notify_change(dentry, attr, HFS_NORM); + struct super_block *sb = inode->i_sb; + u16 count; + int i; + + memcpy(HFS_I(inode)->first_extents, ext, sizeof(hfs_extent_rec)); + for (count = 0, i = 0; i < 3; i++) + count += be16_to_cpu(ext[i].count); + HFS_I(inode)->first_blocks = count; + + log_size = be32_to_cpu(log_size); + inode->i_size = HFS_I(inode)->phys_size = log_size; + inode->i_blocks = (log_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + HFS_I(inode)->alloc_blocks = be32_to_cpu(phys_size) / + HFS_SB(sb)->alloc_blksz; + HFS_I(inode)->clump_blocks = clump_size / HFS_SB(sb)->alloc_blksz; + if (!HFS_I(inode)->clump_blocks) + HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks; } -int hfs_notify_change_cap(struct dentry *dentry, struct iattr * attr) -{ - return __hfs_notify_change(dentry, attr, HFS_CAP); -} +struct hfs_iget_data { + struct hfs_cat_key *key; + hfs_cat_rec *rec; +}; -int hfs_notify_change_hdr(struct dentry *dentry, struct iattr * attr) +int hfs_test_inode(struct inode *inode, void *data) { - return __hfs_notify_change(dentry, attr, HFS_HDR); + struct hfs_iget_data *idata = data; + hfs_cat_rec *rec; + + rec = idata->rec; + switch (rec->type) { + case HFS_CDR_DIR: + return inode->i_ino == be32_to_cpu(rec->dir.DirID); + case HFS_CDR_FIL: + return inode->i_ino == be32_to_cpu(rec->file.FlNum); + default: + BUG(); + return 1; + } } -static int hfs_writepage(struct page *page, struct writeback_control *wbc) -{ - return block_write_full_page(page,hfs_get_block, wbc); -} -static int hfs_readpage(struct file *file, struct page *page) -{ - return block_read_full_page(page,hfs_get_block); -} -static int hfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) -{ - return cont_prepare_write(page,from,to,hfs_get_block, - &HFS_I(page->mapping->host)->mmu_private); -} -static sector_t hfs_bmap(struct address_space *mapping, sector_t block) +/* + * hfs_read_inode + */ +int hfs_read_inode(struct inode *inode, void *data) { - return generic_block_bmap(mapping,block,hfs_get_block); + struct hfs_iget_data *idata = data; + struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); + hfs_cat_rec *rec; + + HFS_I(inode)->flags = 0; + HFS_I(inode)->rsrc_inode = NULL; + init_MUTEX(&HFS_I(inode)->extents_lock); + INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list); + + /* Initialize the inode */ + inode->i_uid = hsb->s_uid; + inode->i_gid = hsb->s_gid; + inode->i_nlink = 1; + + if (idata->key) + HFS_I(inode)->cat_key = *idata->key; + else + HFS_I(inode)->flags |= HFS_FLG_RSRC; + HFS_I(inode)->tz_secondswest = sys_tz.tz_minuteswest * 60; + + rec = idata->rec; + switch (rec->type) { + case HFS_CDR_FIL: + if (!HFS_IS_RSRC(inode)) { + hfs_inode_read_fork(inode, rec->file.ExtRec, rec->file.LgLen, + rec->file.PyLen, be16_to_cpu(rec->file.ClpSize)); + } else { + hfs_inode_read_fork(inode, rec->file.RExtRec, rec->file.RLgLen, + rec->file.RPyLen, be16_to_cpu(rec->file.ClpSize)); + } + + inode->i_ino = be32_to_cpu(rec->file.FlNum); + inode->i_mode = S_IRUGO | S_IXUGO; + if (!(rec->file.Flags & HFS_FIL_LOCK)) + inode->i_mode |= S_IWUGO; + inode->i_mode &= hsb->s_file_umask; + inode->i_mode |= S_IFREG; + inode->i_ctime = inode->i_atime = inode->i_mtime = + hfs_m_to_utime(rec->file.MdDat); + inode->i_op = &hfs_file_inode_operations; + inode->i_fop = &hfs_file_operations; + inode->i_mapping->a_ops = &hfs_aops; + HFS_I(inode)->phys_size = inode->i_size; + break; + case HFS_CDR_DIR: + inode->i_ino = be32_to_cpu(rec->dir.DirID); + inode->i_blocks = 0; + inode->i_size = be16_to_cpu(rec->dir.Val) + 2; + inode->i_mode = S_IFDIR | (S_IRWXUGO & hsb->s_dir_umask); + inode->i_ctime = inode->i_atime = inode->i_mtime = + hfs_m_to_utime(rec->file.MdDat); + inode->i_op = &hfs_dir_inode_operations; + inode->i_fop = &hfs_dir_operations; + break; + default: + make_bad_inode(inode); + } + return 0; } -struct address_space_operations hfs_aops = { - .readpage = hfs_readpage, - .writepage = hfs_writepage, - .sync_page = block_sync_page, - .prepare_write = hfs_prepare_write, - .commit_write = generic_commit_write, - .bmap = hfs_bmap -}; /* * __hfs_iget() @@ -263,253 +346,277 @@ struct address_space_operations hfs_aops = { * inode for that file/directory or NULL. Note that 'type' indicates * whether we want the actual file or directory, or the corresponding * metadata (AppleDouble header file or CAP metadata file). - * - * In an ideal world we could call iget() and would not need this - * function. However, since there is no way to even know the inode - * number until we've found the file/directory in the catalog B-tree - * that simply won't happen. - * - * The main idea here is to look in the catalog B-tree to get the - * vital info about the file or directory (including the file id which - * becomes the inode number) and then to call iget() and return the - * inode if it is complete. If it is not then we use the catalog - * entry to fill in the missing info, by calling the appropriate - * 'fillin' function. Note that these fillin functions are - * essentially hfs_*_read_inode() functions, but since there is no way - * to pass the catalog entry through iget() to such a read_inode() - * function, we have to call them after iget() returns an incomplete - * inode to us. This is pretty much the same problem faced in the NFS - * code, and pretty much the same solution. The SMB filesystem deals - * with this in a different way: by using the address of the - * kmalloc()'d space which holds the data as the inode number. - * - * XXX: Both this function and NFS's corresponding nfs_fhget() would - * benefit from a way to pass an additional (void *) through iget() to - * the VFS read_inode() function. - * - * this will hfs_cat_put() the entry if it fails. */ -struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type, - struct dentry *dentry) +struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key, hfs_cat_rec *rec) { - struct dentry **sys_entry; - struct super_block *sb; + struct hfs_iget_data data = { key, rec }; struct inode *inode; - - if (!entry) { + u32 cnid; + + switch (rec->type) { + case HFS_CDR_DIR: + cnid = be32_to_cpu(rec->dir.DirID); + break; + case HFS_CDR_FIL: + cnid = be32_to_cpu(rec->file.FlNum); + break; + default: return NULL; } + inode = iget5_locked(sb, cnid, hfs_test_inode, hfs_read_inode, &data); + if (inode && (inode->i_state & I_NEW)) + unlock_new_inode(inode); + return inode; +} + +void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext, + u32 *log_size, u32 *phys_size) +{ + memcpy(ext, HFS_I(inode)->first_extents, sizeof(hfs_extent_rec)); + + if (log_size) + *log_size = cpu_to_be32(inode->i_size); + if (phys_size) + *phys_size = cpu_to_be32(HFS_I(inode)->alloc_blocks * + HFS_SB(inode->i_sb)->alloc_blksz); +} + +void hfs_write_inode(struct inode *inode, int unused) +{ + struct hfs_find_data fd; + hfs_cat_rec rec; + + dprint(DBG_INODE, "hfs_write_inode: %lu\n", inode->i_ino); + hfs_ext_write_extent(inode); + + if (inode->i_ino < HFS_FIRSTUSER_CNID) { + switch (inode->i_ino) { + case HFS_ROOT_CNID: + break; + case HFS_EXT_CNID: + hfs_btree_write(HFS_SB(inode->i_sb)->ext_tree); + return; + case HFS_CAT_CNID: + hfs_btree_write(HFS_SB(inode->i_sb)->cat_tree); + return; + default: + BUG(); + return; + } + } - /* If there are several processes all calling __iget() for - the same inode then they will all get the same one back. - The first one to return from __iget() will notice that the - i_mode field of the inode is blank and KNOW that it is - the first to return. Therefore, it will set the appropriate - 'sys_entry' field in the entry and initialize the inode. - All the initialization must be done without sleeping, - or else other processes could end up using a partially - initialized inode. */ - - sb = entry->mdb->sys_mdb; - sys_entry = &entry->sys_entry[HFS_ITYPE_TO_INT(type)]; - - if (!(inode = iget(sb, ntohl(entry->cnid) | type))) { - hfs_cat_put(entry); - return NULL; + if (HFS_IS_RSRC(inode)) { + mark_inode_dirty(HFS_I(inode)->rsrc_inode); + return; } - if (!inode->i_mode || (*sys_entry == NULL)) { - /* Initialize the inode */ - struct hfs_sb_info *hsb = HFS_SB(sb); - - inode->i_ctime.tv_sec = inode->i_atime.tv_sec = inode->i_mtime.tv_sec = - hfs_m_to_utime(entry->modify_date); - inode->i_ctime.tv_nsec = 0; - inode->i_mtime.tv_nsec = 0; - inode->i_atime.tv_nsec = 0; - inode->i_blksize = HFS_SECTOR_SIZE; - inode->i_uid = hsb->s_uid; - inode->i_gid = hsb->s_gid; - - HFS_I(inode)->mmu_private = 0; - HFS_I(inode)->fork = NULL; - HFS_I(inode)->convert = 0; - HFS_I(inode)->file_type = 0; - HFS_I(inode)->dir_size = 0; - HFS_I(inode)->default_layout = NULL; - HFS_I(inode)->layout = NULL; - HFS_I(inode)->magic = HFS_INO_MAGIC; - HFS_I(inode)->entry = entry; - HFS_I(inode)->tz_secondswest = hfs_to_utc(0); - - hsb->s_ifill(inode, type, hsb->s_version); - if (!hsb->s_afpd && (entry->type == HFS_CDR_FIL) && - (entry->u.file.flags & HFS_FIL_LOCK)) { - inode->i_mode &= ~S_IWUGO; + if (!inode->i_nlink) + return; + + if (hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd)) + /* panic? */ + return; + + fd.search_key->cat = HFS_I(inode)->cat_key; + if (hfs_brec_find(&fd)) + /* panic? */ + goto out; + + if (S_ISDIR(inode->i_mode)) { + if (fd.entrylength < sizeof(struct hfs_cat_dir)) + /* panic? */; + hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, + sizeof(struct hfs_cat_dir)); + if (rec.type != HFS_CDR_DIR || + be32_to_cpu(rec.dir.DirID) != inode->i_ino) { } - inode->i_mode &= ~hsb->s_umask; - if (!inode->i_mode) { - iput(inode); /* does an hfs_cat_put */ - inode = NULL; - } else - *sys_entry = dentry; /* cache dentry */ + rec.dir.MdDat = hfs_u_to_mtime(inode->i_mtime); + rec.dir.Val = cpu_to_be16(inode->i_size - 2); - } + hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, + sizeof(struct hfs_cat_dir)); + } else { + if (fd.entrylength < sizeof(struct hfs_cat_file)) + /* panic? */; + hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, + sizeof(struct hfs_cat_file)); + if (rec.type != HFS_CDR_FIL || + be32_to_cpu(rec.file.FlNum) != inode->i_ino) { + } - return inode; + if (inode->i_mode & S_IWUSR) + rec.file.Flags &= ~HFS_FIL_LOCK; + else + rec.file.Flags |= HFS_FIL_LOCK; + hfs_inode_write_fork(inode, rec.file.ExtRec, &rec.file.LgLen, &rec.file.PyLen); + if (HFS_I(inode)->rsrc_inode) + hfs_inode_write_fork(HFS_I(inode)->rsrc_inode, rec.file.RExtRec, + &rec.file.RLgLen, &rec.file.RPyLen); + rec.file.MdDat = hfs_u_to_mtime(inode->i_mtime); + + hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, + sizeof(struct hfs_cat_file)); + } +out: + hfs_find_exit(&fd); } -/*================ Scheme-specific functions ================*/ - -/* - * hfs_cap_ifill() - * - * This function serves the same purpose as a read_inode() function does - * in other filesystems. It is called by __hfs_iget() to fill in - * the missing fields of an uninitialized inode under the CAP scheme. - */ -void hfs_cap_ifill(struct inode * inode, ino_t type, const int version) +static struct dentry *hfs_file_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) { - struct hfs_cat_entry *entry = HFS_I(inode)->entry; + struct inode *inode = NULL; + hfs_cat_rec rec; + struct hfs_find_data fd; + int res; - HFS_I(inode)->d_drop_op = hfs_cap_drop_dentry; - if (type == HFS_CAP_FNDR) { - inode->i_size = sizeof(struct hfs_cap_info); - inode->i_blocks = 0; - inode->i_nlink = 1; - inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; - inode->i_op = &hfs_cap_info_inode_operations; - inode->i_fop = &hfs_cap_info_operations; - } else if (entry->type == HFS_CDR_FIL) { - init_file_inode(inode, (type == HFS_CAP_DATA) ? - HFS_FK_DATA : HFS_FK_RSRC); - inode->i_op = &hfs_file_inode_operations; - inode->i_fop = &hfs_file_operations; - inode->i_mapping->a_ops = &hfs_aops; - HFS_I(inode)->mmu_private = inode->i_size; - } else { /* Directory */ - struct hfs_dir *hdir = &entry->u.dir; + if (HFS_IS_RSRC(dir) || strcmp(dentry->d_name.name, "rsrc")) + goto out; - inode->i_blocks = 0; - inode->i_size = hdir->files + hdir->dirs + 5; - HFS_I(inode)->dir_size = 1; - if (type == HFS_CAP_NDIR) { - inode->i_mode = S_IRWXUGO | S_IFDIR; - inode->i_nlink = hdir->dirs + 4; - inode->i_op = &hfs_cap_ndir_inode_operations; - inode->i_fop = &hfs_cap_dir_operations; - HFS_I(inode)->file_type = HFS_CAP_NORM; - } else if (type == HFS_CAP_FDIR) { - inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; - inode->i_nlink = 2; - inode->i_op = &hfs_cap_fdir_inode_operations; - inode->i_fop = &hfs_cap_dir_operations; - HFS_I(inode)->file_type = HFS_CAP_FNDR; - } else if (type == HFS_CAP_RDIR) { - inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; - inode->i_nlink = 2; - inode->i_op = &hfs_cap_rdir_inode_operations; - inode->i_fop = &hfs_cap_dir_operations; - HFS_I(inode)->file_type = HFS_CAP_RSRC; - } + inode = HFS_I(dir)->rsrc_inode; + if (inode) + goto out; + + inode = new_inode(dir->i_sb); + if (!inode) + return ERR_PTR(-ENOMEM); + + hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd); + fd.search_key->cat = HFS_I(dir)->cat_key; + res = hfs_brec_read(&fd, &rec, sizeof(rec)); + if (!res) { + struct hfs_iget_data idata = { NULL, &rec }; + hfs_read_inode(inode, &idata); } + hfs_find_exit(&fd); + if (res) { + iput(inode); + return ERR_PTR(res); + } + HFS_I(inode)->rsrc_inode = dir; + HFS_I(dir)->rsrc_inode = inode; + igrab(dir); + hlist_add_head(&inode->i_hash, &HFS_SB(dir->i_sb)->rsrc_inodes); + mark_inode_dirty(inode); +out: + d_add(dentry, inode); + return NULL; } -/* - * hfs_dbl_ifill() - * - * This function serves the same purpose as a read_inode() function does - * in other filesystems. It is called by __hfs_iget() to fill in - * the missing fields of an uninitialized inode under the AppleDouble - * scheme. - */ -void hfs_dbl_ifill(struct inode * inode, ino_t type, const int version) +void hfs_clear_inode(struct inode *inode) { - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - - HFS_I(inode)->d_drop_op = hfs_dbl_drop_dentry; - if (type == HFS_DBL_HDR) { - if (entry->type == HFS_CDR_FIL) { - init_file_inode(inode, HFS_FK_RSRC); - inode->i_size += HFS_DBL_HDR_LEN; - HFS_I(inode)->default_layout = &hfs_dbl_fil_hdr_layout; - } else { - inode->i_size = HFS_DBL_HDR_LEN; - inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; - inode->i_nlink = 1; - HFS_I(inode)->default_layout = &hfs_dbl_dir_hdr_layout; - } - inode->i_op = &hfs_hdr_inode_operations; - inode->i_fop = &hfs_hdr_operations; - } else if (entry->type == HFS_CDR_FIL) { - init_file_inode(inode, HFS_FK_DATA); - inode->i_op = &hfs_file_inode_operations; - inode->i_fop = &hfs_file_operations; - inode->i_mapping->a_ops = &hfs_aops; - HFS_I(inode)->mmu_private = inode->i_size; - } else { /* Directory */ - struct hfs_dir *hdir = &entry->u.dir; + if (HFS_IS_RSRC(inode) && HFS_I(inode)->rsrc_inode) { + HFS_I(HFS_I(inode)->rsrc_inode)->rsrc_inode = NULL; + iput(HFS_I(inode)->rsrc_inode); + } +} - inode->i_blocks = 0; - inode->i_nlink = hdir->dirs + 2; - inode->i_size = 3 + 2 * (hdir->dirs + hdir->files); - inode->i_mode = S_IRWXUGO | S_IFDIR; - inode->i_op = &hfs_dbl_dir_inode_operations; - inode->i_fop = &hfs_dbl_dir_operations; - HFS_I(inode)->file_type = HFS_DBL_NORM; - HFS_I(inode)->dir_size = 2; +static int hfs_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + if (S_ISREG(inode->i_mode) && mask & MAY_EXEC) + return 0; + return vfs_permission(inode, mask); +} + +static int hfs_file_open(struct inode *inode, struct file *file) +{ + if (HFS_IS_RSRC(inode)) + inode = HFS_I(inode)->rsrc_inode; + if (atomic_read(&file->f_count) != 1) + return 0; + atomic_inc(&HFS_I(inode)->opencnt); + return 0; +} + +static int hfs_file_release(struct inode *inode, struct file *file) +{ + //struct super_block *sb = inode->i_sb; + + if (HFS_IS_RSRC(inode)) + inode = HFS_I(inode)->rsrc_inode; + if (atomic_read(&file->f_count) != 0) + return 0; + if (atomic_dec_and_test(&HFS_I(inode)->opencnt)) { + down(&inode->i_sem); + hfs_file_truncate(inode); + //if (inode->i_flags & S_DEAD) { + // hfs_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); + // hfs_delete_inode(inode); + //} + up(&inode->i_sem); } + return 0; } -/* - * hfs_nat_ifill() +/* + * hfs_notify_change() + * + * Based very closely on fs/msdos/inode.c by Werner Almesberger * - * This function serves the same purpose as a read_inode() function does - * in other filesystems. It is called by __hfs_iget() to fill in - * the missing fields of an uninitialized inode under the Netatalk - * scheme. + * This is the notify_change() field in the super_operations structure + * for HFS file systems. The purpose is to take that changes made to + * an inode and apply then in a filesystem-dependent manner. In this + * case the process has a few of tasks to do: + * 1) prevent changes to the i_uid and i_gid fields. + * 2) map file permissions to the closest allowable permissions + * 3) Since multiple Linux files can share the same on-disk inode under + * HFS (for instance the data and resource forks of a file) a change + * to permissions must be applied to all other in-core inodes which + * correspond to the same HFS file. */ -void hfs_nat_ifill(struct inode * inode, ino_t type, const int version) + +int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr) { - struct hfs_cat_entry *entry = HFS_I(inode)->entry; + struct inode *inode = dentry->d_inode; + struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); + int error; - HFS_I(inode)->d_drop_op = hfs_nat_drop_dentry; - if (type == HFS_NAT_HDR) { - if (entry->type == HFS_CDR_FIL) { - init_file_inode(inode, HFS_FK_RSRC); - inode->i_size += HFS_NAT_HDR_LEN; - } else { - inode->i_size = HFS_NAT_HDR_LEN; - inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; - inode->i_nlink = 1; - } - inode->i_op = &hfs_hdr_inode_operations; - inode->i_fop = &hfs_hdr_operations; - HFS_I(inode)->default_layout = (version == 2) ? - &hfs_nat2_hdr_layout : &hfs_nat_hdr_layout; - } else if (entry->type == HFS_CDR_FIL) { - init_file_inode(inode, HFS_FK_DATA); - inode->i_op = &hfs_file_inode_operations; - inode->i_fop = &hfs_file_operations; - inode->i_mapping->a_ops = &hfs_aops; - HFS_I(inode)->mmu_private = inode->i_size; - } else { /* Directory */ - struct hfs_dir *hdir = &entry->u.dir; + error = inode_change_ok(inode, attr); /* basic permission checks */ + if (error) + return error; - inode->i_blocks = 0; - inode->i_size = hdir->files + hdir->dirs + 4; - inode->i_mode = S_IRWXUGO | S_IFDIR; - HFS_I(inode)->dir_size = 1; - if (type == HFS_NAT_NDIR) { - inode->i_nlink = hdir->dirs + 3; - inode->i_op = &hfs_nat_ndir_inode_operations; - HFS_I(inode)->file_type = HFS_NAT_NORM; - } else if (type == HFS_NAT_HDIR) { - inode->i_nlink = 2; - inode->i_op = &hfs_nat_hdir_inode_operations; - HFS_I(inode)->file_type = HFS_NAT_HDR; - } - inode->i_fop = &hfs_nat_dir_operations; + /* no uig/gid changes and limit which mode bits can be set */ + if (((attr->ia_valid & ATTR_UID) && + (attr->ia_uid != hsb->s_uid)) || + ((attr->ia_valid & ATTR_GID) && + (attr->ia_gid != hsb->s_gid)) || + ((attr->ia_valid & ATTR_MODE) && + ((S_ISDIR(inode->i_mode) && + (attr->ia_mode != inode->i_mode)) || + (attr->ia_mode & ~HFS_VALID_MODE_BITS)))) { + return hsb->s_quiet ? 0 : error; } + + if (attr->ia_valid & ATTR_MODE) { + /* Only the 'w' bits can ever change and only all together. */ + if (attr->ia_mode & S_IWUSR) + attr->ia_mode = inode->i_mode | S_IWUGO; + else + attr->ia_mode = inode->i_mode & ~S_IWUGO; + attr->ia_mode &= S_ISDIR(inode->i_mode) ? ~hsb->s_dir_umask: ~hsb->s_file_umask; + } + error = inode_setattr(inode, attr); + if (error) + return error; + + return 0; } + + +struct file_operations hfs_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .fsync = file_fsync, + .open = hfs_file_open, + .release = hfs_file_release, +}; + +struct inode_operations hfs_file_inode_operations = { + .lookup = hfs_file_lookup, + .truncate = hfs_file_truncate, + .setattr = hfs_inode_setattr, + .permission = hfs_permission, +}; diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c index b3f8477f58cb..cedb9ab184c3 100644 --- a/fs/hfs/mdb.c +++ b/fs/hfs/mdb.c @@ -1,28 +1,22 @@ /* - * linux/fs/hfs/mdb.c + * linux/fs/hfs/mdb.c * * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * This file contains functions for reading/writing the MDB. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - * - * The code in this file initializes some structures which contain - * pointers by calling memset(&foo, 0, sizeof(foo)). - * This produces the desired behavior only due to the non-ANSI - * assumption that the machine representation of NULL is all zeros. */ -#include "hfs.h" +#include <linux/cdrom.h> +#include <linux/genhd.h> + +#include "hfs_fs.h" +#include "btree.h" /*================ File-local data types ================*/ -/* +/* * The HFS Master Directory Block (MDB). * * Also known as the Volume Information Block (VIB), this structure is @@ -32,48 +26,35 @@ * * modified for HFS Extended */ -struct raw_mdb { - hfs_word_t drSigWord; /* Signature word indicating fs type */ - hfs_lword_t drCrDate; /* fs creation date/time */ - hfs_lword_t drLsMod; /* fs modification date/time */ - hfs_word_t drAtrb; /* fs attributes */ - hfs_word_t drNmFls; /* number of files in root directory */ - hfs_word_t drVBMSt; /* location (in 512-byte blocks) - of the volume bitmap */ - hfs_word_t drAllocPtr; /* location (in allocation blocks) - to begin next allocation search */ - hfs_word_t drNmAlBlks; /* number of allocation blocks */ - hfs_lword_t drAlBlkSiz; /* bytes in an allocation block */ - hfs_lword_t drClpSiz; /* clumpsize, the number of bytes to - allocate when extending a file */ - hfs_word_t drAlBlSt; /* location (in 512-byte blocks) - of the first allocation block */ - hfs_lword_t drNxtCNID; /* CNID to assign to the next - file or directory created */ - hfs_word_t drFreeBks; /* number of free allocation blocks */ - hfs_byte_t drVN[28]; /* the volume label */ - hfs_lword_t drVolBkUp; /* fs backup date/time */ - hfs_word_t drVSeqNum; /* backup sequence number */ - hfs_lword_t drWrCnt; /* fs write count */ - hfs_lword_t drXTClpSiz; /* clumpsize for the extents B-tree */ - hfs_lword_t drCTClpSiz; /* clumpsize for the catalog B-tree */ - hfs_word_t drNmRtDirs; /* number of directories in - the root directory */ - hfs_lword_t drFilCnt; /* number of files in the fs */ - hfs_lword_t drDirCnt; /* number of directories in the fs */ - hfs_byte_t drFndrInfo[32]; /* data used by the Finder */ - hfs_word_t drEmbedSigWord; /* embedded volume signature */ - hfs_lword_t drEmbedExtent; /* starting block number (xdrStABN) - and number of allocation blocks - (xdrNumABlks) occupied by embedded - volume */ - hfs_lword_t drXTFlSize; /* bytes in the extents B-tree */ - hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */ - hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */ - hfs_byte_t drCTExtRec[12]; /* catalog B-tree's first 3 extents */ -} __attribute__((packed)); - -/*================ Global functions ================*/ + +static int hfs_get_last_session(struct super_block *sb, + sector_t *start, sector_t *size) +{ + struct cdrom_multisession ms_info; + struct cdrom_tocentry te; + int res; + + /* default values */ + *start = 0; + *size = sb->s_bdev->bd_inode->i_size >> 9; + + if (HFS_SB(sb)->session >= 0) { + te.cdte_track = HFS_SB(sb)->session; + te.cdte_format = CDROM_LBA; + res = ioctl_by_bdev(sb->s_bdev, CDROMREADTOCENTRY, (unsigned long)&te); + if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) { + *start = (sector_t)te.cdte_addr.lba << 2; + return 0; + } + printk(KERN_ERR "HFS: Invalid session number or type of track\n"); + return -EINVAL; + } + ms_info.addr_format = CDROM_LBA; + res = ioctl_by_bdev(sb->s_bdev, CDROMMULTISESSION, (unsigned long)&ms_info); + if (!res && ms_info.xa_flag) + *start = (sector_t)ms_info.addr.lba << 2; + return 0; +} /* * hfs_mdb_get() @@ -81,135 +62,172 @@ struct raw_mdb { * Build the in-core MDB for a filesystem, including * the B-trees and the volume bitmap. */ -struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly, - hfs_s32 part_start) +int hfs_mdb_get(struct super_block *sb) { - struct hfs_mdb *mdb; - hfs_buffer buf; - struct raw_mdb *raw; - unsigned int bs, block; - int lcv, limit; - hfs_buffer *bmbuf; - - if (!HFS_NEW(mdb)) { - hfs_warn("hfs_fs: out of memory\n"); - return NULL; - } + struct buffer_head *bh; + struct hfs_mdb *mdb, *mdb2; + unsigned int block; + char *ptr; + int off2, len, size, sect; + sector_t part_start, part_size; + loff_t off; + u16 attrib; - memset(mdb, 0, sizeof(*mdb)); - mdb->magic = HFS_MDB_MAGIC; - mdb->sys_mdb = sys_mdb; - INIT_LIST_HEAD(&mdb->entry_dirty); - init_MUTEX(&mdb->bitmap_sem); - - /* See if this is an HFS filesystem */ - buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1); - if (!hfs_buffer_ok(buf)) { - hfs_warn("hfs_fs: Unable to read superblock\n"); - HFS_DELETE(mdb); - goto bail2; + /* set the device driver to 512-byte blocks */ + size = sb_min_blocksize(sb, HFS_SECTOR_SIZE); + if (!size) + return -EINVAL; + + if (hfs_get_last_session(sb, &part_start, &part_size)) + return -EINVAL; + while (1) { + /* See if this is an HFS filesystem */ + bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb); + if (!bh) + goto out; + + if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) + break; + brelse(bh); + + /* check for a partition block + * (should do this only for cdrom/loop though) + */ + if (hfs_part_find(sb, &part_start, &part_size)) + goto out; } - raw = (struct raw_mdb *)hfs_buffer_data(buf); - if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) { - hfs_buffer_put(buf); - HFS_DELETE(mdb); - goto bail2; + HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz); + if (!size || (size & (HFS_SECTOR_SIZE - 1))) { + hfs_warn("hfs_fs: bad allocation block size %d\n", size); + goto out_bh; } - mdb->buf = buf; - - bs = hfs_get_hl(raw->drAlBlkSiz); - if (!bs || (bs & (HFS_SECTOR_SIZE-1))) { - hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs); - hfs_buffer_put(buf); - HFS_DELETE(mdb); - goto bail2; + + size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE); + /* size must be a multiple of 512 */ + while (size & (size - 1)) + size -= HFS_SECTOR_SIZE; + sect = be16_to_cpu(mdb->drAlBlSt) + part_start; + /* align block size to first sector */ + while (sect & ((size - 1) >> HFS_SECTOR_SIZE_BITS)) + size >>= 1; + /* align block size to weird alloc size */ + while (HFS_SB(sb)->alloc_blksz & (size - 1)) + size >>= 1; + brelse(bh); + if (!sb_set_blocksize(sb, size)) { + printk("hfs_fs: unable to set blocksize to %u\n", size); + goto out; } - mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS; + + bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb); + if (!bh) + goto out; + if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) + goto out_bh; + + HFS_SB(sb)->mdb_bh = bh; + HFS_SB(sb)->mdb = mdb; /* These parameters are read from the MDB, and never written */ - mdb->create_date = hfs_get_hl(raw->drCrDate); - mdb->fs_ablocks = hfs_get_hs(raw->drNmAlBlks); - mdb->fs_start = hfs_get_hs(raw->drAlBlSt) + part_start; - mdb->backup_date = hfs_get_hl(raw->drVolBkUp); - mdb->clumpablks = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz) - >> HFS_SECTOR_SIZE_BITS; - memcpy(mdb->vname, raw->drVN, sizeof(raw->drVN)); + HFS_SB(sb)->part_start = part_start; + HFS_SB(sb)->fs_ablocks = be16_to_cpu(mdb->drNmAlBlks); + HFS_SB(sb)->fs_div = HFS_SB(sb)->alloc_blksz >> sb->s_blocksize_bits; + HFS_SB(sb)->clumpablks = be32_to_cpu(mdb->drClpSiz) / + HFS_SB(sb)->alloc_blksz; + if (!HFS_SB(sb)->clumpablks) + HFS_SB(sb)->clumpablks = 1; + HFS_SB(sb)->fs_start = (be16_to_cpu(mdb->drAlBlSt) + part_start) >> + (sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS); /* These parameters are read from and written to the MDB */ - mdb->modify_date = hfs_get_nl(raw->drLsMod); - mdb->attrib = hfs_get_ns(raw->drAtrb); - mdb->free_ablocks = hfs_get_hs(raw->drFreeBks); - mdb->next_id = hfs_get_hl(raw->drNxtCNID); - mdb->write_count = hfs_get_hl(raw->drWrCnt); - mdb->root_files = hfs_get_hs(raw->drNmFls); - mdb->root_dirs = hfs_get_hs(raw->drNmRtDirs); - mdb->file_count = hfs_get_hl(raw->drFilCnt); - mdb->dir_count = hfs_get_hl(raw->drDirCnt); + HFS_SB(sb)->free_ablocks = be16_to_cpu(mdb->drFreeBks); + HFS_SB(sb)->next_id = be32_to_cpu(mdb->drNxtCNID); + HFS_SB(sb)->root_files = be16_to_cpu(mdb->drNmFls); + HFS_SB(sb)->root_dirs = be16_to_cpu(mdb->drNmRtDirs); + HFS_SB(sb)->file_count = be32_to_cpu(mdb->drFilCnt); + HFS_SB(sb)->folder_count = be32_to_cpu(mdb->drDirCnt); /* TRY to get the alternate (backup) MDB. */ - lcv = mdb->fs_start + mdb->fs_ablocks * mdb->alloc_blksz; - limit = lcv + mdb->alloc_blksz; - for (; lcv < limit; ++lcv) { - buf = hfs_buffer_get(sys_mdb, lcv, 1); - if (hfs_buffer_ok(buf)) { - struct raw_mdb *tmp = - (struct raw_mdb *)hfs_buffer_data(buf); - - if (hfs_get_ns(tmp->drSigWord) == - htons(HFS_SUPER_MAGIC)) { - mdb->alt_buf = buf; - break; - } - } - hfs_buffer_put(buf); + sect = part_start + part_size - 2; + bh = sb_bread512(sb, sect, mdb2); + if (bh) { + if (mdb2->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) { + HFS_SB(sb)->alt_mdb_bh = bh; + HFS_SB(sb)->alt_mdb = mdb2; + } else + brelse(bh); } - - if (mdb->alt_buf == NULL) { + + if (!HFS_SB(sb)->alt_mdb) { hfs_warn("hfs_fs: unable to locate alternate MDB\n"); hfs_warn("hfs_fs: continuing without an alternate MDB\n"); } - + + HFS_SB(sb)->bitmap = (u32 *)__get_free_pages(GFP_KERNEL, PAGE_SIZE < 8192 ? 1 : 0); + if (!HFS_SB(sb)->bitmap) + goto out; + /* read in the bitmap */ - block = hfs_get_hs(raw->drVBMSt) + part_start; - bmbuf = mdb->bitmap; - lcv = (mdb->fs_ablocks + 4095) / 4096; - for ( ; lcv; --lcv, ++bmbuf, ++block) { - if (!hfs_buffer_ok(*bmbuf = - hfs_buffer_get(sys_mdb, block, 1))) { + block = be16_to_cpu(mdb->drVBMSt) + part_start; + off = (loff_t)block << HFS_SECTOR_SIZE_BITS; + size = (HFS_SB(sb)->fs_ablocks + 8) / 8; + ptr = (u8 *)HFS_SB(sb)->bitmap; + while (size) { + bh = sb_bread(sb, off >> sb->s_blocksize_bits); + if (!bh) { hfs_warn("hfs_fs: unable to read volume bitmap\n"); - goto bail1; + goto out; } + off2 = off & (sb->s_blocksize - 1); + len = min((int)sb->s_blocksize - off2, size); + memcpy(ptr, bh->b_data + off2, len); + brelse(bh); + ptr += len; + off += len; + size -= len; } - if (!(mdb->ext_tree = hfs_btree_init(mdb, htonl(HFS_EXT_CNID), - raw->drXTExtRec, - hfs_get_hl(raw->drXTFlSize), - hfs_get_hl(raw->drXTClpSiz))) || - !(mdb->cat_tree = hfs_btree_init(mdb, htonl(HFS_CAT_CNID), - raw->drCTExtRec, - hfs_get_hl(raw->drCTFlSize), - hfs_get_hl(raw->drCTClpSiz)))) { - hfs_warn("hfs_fs: unable to initialize data structures\n"); - goto bail1; + HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp); + if (!HFS_SB(sb)->ext_tree) { + hfs_warn("hfs_fs: unable to open extent tree\n"); + goto out; + } + HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp); + if (!HFS_SB(sb)->cat_tree) { + hfs_warn("hfs_fs: unable to open catalog tree\n"); + goto out; } - if (!(mdb->attrib & htons(HFS_SB_ATTRIB_CLEAN))) { - hfs_warn("hfs_fs: WARNING: mounting unclean filesystem.\n"); - } else if (!readonly) { + attrib = mdb->drAtrb; + if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT)) + || (attrib & cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT))) { + hfs_warn("HFS-fs warning: Filesystem was not cleanly unmounted, " + "running fsck.hfs is recommended. mounting read-only.\n"); + sb->s_flags |= MS_RDONLY; + } + if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) { + hfs_warn("HFS-fs: Filesystem is marked locked, mounting read-only.\n"); + sb->s_flags |= MS_RDONLY; + } + if (!(sb->s_flags & MS_RDONLY)) { /* Mark the volume uncleanly unmounted in case we crash */ - hfs_put_ns(mdb->attrib & htons(~HFS_SB_ATTRIB_CLEAN), - raw->drAtrb); - hfs_buffer_dirty(mdb->buf); - hfs_buffer_sync(mdb->buf); + mdb->drAtrb = attrib & cpu_to_be16(~HFS_SB_ATTRIB_UNMNT); + mdb->drAtrb = attrib | cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT); + mdb->drWrCnt = cpu_to_be32(be32_to_cpu(mdb->drWrCnt) + 1); + mdb->drLsMod = hfs_mtime(); + + mark_buffer_dirty(HFS_SB(sb)->mdb_bh); + hfs_buffer_sync(HFS_SB(sb)->mdb_bh); } - return mdb; + return 0; -bail1: - hfs_mdb_put(mdb, readonly); -bail2: - return NULL; +out_bh: + brelse(bh); +out: + hfs_mdb_put(sb); + return -EIO; } /* @@ -236,84 +254,90 @@ bail2: * If 'backup' is non-zero then the alternate MDB is also written * and the function doesn't return until it is actually on disk. */ -void hfs_mdb_commit(struct hfs_mdb *mdb, int backup) +void hfs_mdb_commit(struct super_block *sb) { - struct raw_mdb *raw = (struct raw_mdb *)hfs_buffer_data(mdb->buf); - - /* Commit catalog entries to buffers */ - hfs_cat_commit(mdb); - - /* Commit B-tree data to buffers */ - hfs_btree_commit(mdb->cat_tree, raw->drCTExtRec, raw->drCTFlSize); - hfs_btree_commit(mdb->ext_tree, raw->drXTExtRec, raw->drXTFlSize); - - /* Update write_count and modify_date */ - ++mdb->write_count; - mdb->modify_date = hfs_time(); - - /* These parameters may have been modified, so write them back */ - hfs_put_nl(mdb->modify_date, raw->drLsMod); - hfs_put_hs(mdb->free_ablocks, raw->drFreeBks); - hfs_put_hl(mdb->next_id, raw->drNxtCNID); - hfs_put_hl(mdb->write_count, raw->drWrCnt); - hfs_put_hs(mdb->root_files, raw->drNmFls); - hfs_put_hs(mdb->root_dirs, raw->drNmRtDirs); - hfs_put_hl(mdb->file_count, raw->drFilCnt); - hfs_put_hl(mdb->dir_count, raw->drDirCnt); - - /* write MDB to disk */ - hfs_buffer_dirty(mdb->buf); - - /* write the backup MDB, not returning until it is written. - * we only do this when either the catalog or extents overflow - * files grow. */ - if (backup && hfs_buffer_ok(mdb->alt_buf)) { - struct raw_mdb *tmp = (struct raw_mdb *) - hfs_buffer_data(mdb->alt_buf); - - if ((hfs_get_hl(tmp->drCTFlSize) < - hfs_get_hl(raw->drCTFlSize)) || - (hfs_get_hl(tmp->drXTFlSize) < - hfs_get_hl(raw->drXTFlSize))) { - memcpy(hfs_buffer_data(mdb->alt_buf), - hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE); - hfs_buffer_dirty(mdb->alt_buf); - hfs_buffer_sync(mdb->alt_buf); + struct hfs_mdb *mdb = HFS_SB(sb)->mdb; + + if (test_and_clear_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags)) { + /* These parameters may have been modified, so write them back */ + mdb->drLsMod = hfs_mtime(); + mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks); + mdb->drNxtCNID = cpu_to_be32(HFS_SB(sb)->next_id); + mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files); + mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs); + mdb->drFilCnt = cpu_to_be32(HFS_SB(sb)->file_count); + mdb->drDirCnt = cpu_to_be32(HFS_SB(sb)->folder_count); + + /* write MDB to disk */ + mark_buffer_dirty(HFS_SB(sb)->mdb_bh); + } + + /* write the backup MDB, not returning until it is written. + * we only do this when either the catalog or extents overflow + * files grow. */ + if (test_and_clear_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags) && + HFS_SB(sb)->alt_mdb) { + hfs_inode_write_fork(HFS_SB(sb)->ext_tree->inode, mdb->drXTExtRec, + &mdb->drXTFlSize, NULL); + hfs_inode_write_fork(HFS_SB(sb)->cat_tree->inode, mdb->drCTExtRec, + &mdb->drCTFlSize, NULL); + memcpy(HFS_SB(sb)->alt_mdb, HFS_SB(sb)->mdb, HFS_SECTOR_SIZE); + HFS_SB(sb)->alt_mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT); + HFS_SB(sb)->alt_mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT); + mark_buffer_dirty(HFS_SB(sb)->alt_mdb_bh); + hfs_buffer_sync(HFS_SB(sb)->alt_mdb_bh); + } + + if (test_and_clear_bit(HFS_FLG_BITMAP_DIRTY, &HFS_SB(sb)->flags)) { + struct buffer_head *bh; + sector_t block; + char *ptr; + int off, size, len; + + block = be16_to_cpu(HFS_SB(sb)->mdb->drVBMSt) + HFS_SB(sb)->part_start; + off = (block << HFS_SECTOR_SIZE_BITS) & (sb->s_blocksize - 1); + block >>= sb->s_blocksize_bits - HFS_SECTOR_SIZE_BITS; + size = (HFS_SB(sb)->fs_ablocks + 7) / 8; + ptr = (u8 *)HFS_SB(sb)->bitmap; + while (size) { + bh = sb_bread(sb, block); + if (!bh) { + hfs_warn("hfs_fs: unable to read volume bitmap\n"); + break; + } + len = min((int)sb->s_blocksize - off, size); + memcpy(bh->b_data + off, ptr, len); + mark_buffer_dirty(bh); + brelse(bh); + block++; + off = 0; + ptr += len; + size -= len; } - } + } +} + +void hfs_mdb_close(struct super_block *sb) +{ + /* update volume attributes */ + if (sb->s_flags & MS_RDONLY) + return; + HFS_SB(sb)->mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT); + HFS_SB(sb)->mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT); + mark_buffer_dirty(HFS_SB(sb)->mdb_bh); } /* * hfs_mdb_put() * * Release the resources associated with the in-core MDB. */ -void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) { - int lcv; - - /* invalidate cached catalog entries */ - hfs_cat_invalidate(mdb); - +void hfs_mdb_put(struct super_block *sb) +{ /* free the B-trees */ - hfs_btree_free(mdb->ext_tree); - hfs_btree_free(mdb->cat_tree); - - /* free the volume bitmap */ - for (lcv = 0; lcv < HFS_BM_MAXBLOCKS; ++lcv) { - hfs_buffer_put(mdb->bitmap[lcv]); - } - - /* update volume attributes */ - if (!readonly) { - struct raw_mdb *raw = - (struct raw_mdb *)hfs_buffer_data(mdb->buf); - hfs_put_ns(mdb->attrib, raw->drAtrb); - hfs_buffer_dirty(mdb->buf); - } + hfs_btree_close(HFS_SB(sb)->ext_tree); + hfs_btree_close(HFS_SB(sb)->cat_tree); /* free the buffers holding the primary and alternate MDBs */ - hfs_buffer_put(mdb->buf); - hfs_buffer_put(mdb->alt_buf); - - /* free the MDB */ - HFS_DELETE(mdb); + brelse(HFS_SB(sb)->mdb_bh); + brelse(HFS_SB(sb)->alt_mdb_bh); } diff --git a/fs/hfs/part_tbl.c b/fs/hfs/part_tbl.c index 2211572262bd..19c189dc74bf 100644 --- a/fs/hfs/part_tbl.c +++ b/fs/hfs/part_tbl.c @@ -1,40 +1,17 @@ /* - * linux/fs/hfs/part_tbl.c + * linux/fs/hfs/part_tbl.c * * Copyright (C) 1996-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * Original code to handle the new style Mac partition table based on * a patch contributed by Holger Schemel (aeglos@valinor.owl.de). - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - * - * The code in this file initializes some structures which contain - * pointers by calling memset(&foo, 0, sizeof(foo)). - * This produces the desired behavior only due to the non-ANSI - * assumption that the machine representation of NULL is all zeros. */ -#include "hfs.h" - -/*================ File-local data types ================*/ +#include "hfs_fs.h" /* - * The Macintosh Driver Descriptor Block - * - * On partitioned Macintosh media this is block 0. - * We really only need the "magic number" to check for partitioned media. - */ -struct hfs_drvr_desc { - hfs_word_t ddSig; /* The signature word */ - /* a bunch more stuff we don't need */ -}; - -/* * The new style Mac partition map * * For each partition on the media there is a physical block (512-byte @@ -42,28 +19,21 @@ struct hfs_drvr_desc { * contiguous starting at block 1. */ struct new_pmap { - hfs_word_t pmSig; /* Signature bytes to verify - that this is a partition - map block */ - hfs_word_t reSigPad; /* padding */ - hfs_lword_t pmMapBlkCnt; /* (At least in block 1) this - is the number of partition - map blocks */ - hfs_lword_t pmPyPartStart; /* The physical block number - of the first block in this - partition */ - hfs_lword_t pmPartBlkCnt; /* The number of physical - blocks in this partition */ - hfs_byte_t pmPartName[32]; /* (null terminated?) string - giving the name of this - partition */ - hfs_byte_t pmPartType[32]; /* (null terminated?) string - giving the type of this - partition */ + u16 pmSig; /* signature */ + u16 reSigPad; /* padding */ + u32 pmMapBlkCnt; /* partition blocks count */ + u32 pmPyPartStart; /* physical block start of partition */ + u32 pmPartBlkCnt; /* physical block count of partition */ + u8 pmPartName[32]; /* (null terminated?) string + giving the name of this + partition */ + u8 pmPartType[32]; /* (null terminated?) string + giving the type of this + partition */ /* a bunch more stuff we don't need */ -}; +} __packed; -/* +/* * The old style Mac partition map * * The partition map consists for a 2-byte signature followed by an @@ -71,95 +41,13 @@ struct new_pmap { * one of these. */ struct old_pmap { - hfs_word_t pdSig; /* Signature bytes */ + u16 pdSig; /* Signature bytes */ struct old_pmap_entry { - hfs_lword_t pdStart; - hfs_lword_t pdSize; - hfs_lword_t pdFSID; + u32 pdStart; + u32 pdSize; + u32 pdFSID; } pdEntry[42]; -} __attribute__((packed)); - -/*================ File-local functions ================*/ - -/* - * parse_new_part_table() - * - * Parse a new style partition map looking for the - * start and length of the 'part'th HFS partition. - */ -static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf, - int part, hfs_s32 *size, hfs_s32 *start) -{ - struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf); - hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt); - int hfs_part = 0; - int entry; - - for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) { - if (entry) { - /* read the next partition map entry */ - buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1); - if (!hfs_buffer_ok(buf)) { - hfs_warn("hfs_fs: unable to " - "read partition map.\n"); - goto bail; - } - pm = (struct new_pmap *)hfs_buffer_data(buf); - if (hfs_get_ns(pm->pmSig) != - htons(HFS_NEW_PMAP_MAGIC)) { - hfs_warn("hfs_fs: invalid " - "entry in partition map\n"); - hfs_buffer_put(buf); - goto bail; - } - } - - /* look for an HFS partition */ - if (!memcmp(pm->pmPartType,"Apple_HFS",9) && - ((hfs_part++) == part)) { - /* Found it! */ - *start = hfs_get_hl(pm->pmPyPartStart); - *size = hfs_get_hl(pm->pmPartBlkCnt); - } - - hfs_buffer_put(buf); - } - - return 0; - -bail: - return 1; -} - -/* - * parse_old_part_table() - * - * Parse a old style partition map looking for the - * start and length of the 'part'th HFS partition. - */ -static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf, - int part, hfs_s32 *size, hfs_s32 *start) -{ - struct old_pmap *pm = (struct old_pmap *)hfs_buffer_data(buf); - struct old_pmap_entry *p = &pm->pdEntry[0]; - int hfs_part = 0; - - while ((p->pdStart || p->pdSize || p->pdFSID) && !(*start)) { - /* look for an HFS partition */ - if ((hfs_get_nl(p->pdFSID) == htonl(0x54465331)/*"TFS1"*/) && - ((hfs_part++) == part)) { - /* Found it! */ - *start = hfs_get_hl(p->pdStart); - *size = hfs_get_hl(p->pdSize); - } - ++p; - } - hfs_buffer_put(buf); - - return 0; -} - -/*================ Global functions ================*/ +} __packed; /* * hfs_part_find() @@ -167,78 +55,63 @@ static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf, * Parse the partition map looking for the * start and length of the 'part'th HFS partition. */ -int hfs_part_find(hfs_sysmdb sys_mdb, int part, int silent, - hfs_s32 *size, hfs_s32 *start) +int hfs_part_find(struct super_block *sb, + sector_t *part_start, sector_t *part_size) { - hfs_buffer buf; - hfs_u16 sig; - int dd_found = 0; - int retval = 1; - - /* Read block 0 to see if this media is partitioned */ - buf = hfs_buffer_get(sys_mdb, HFS_DD_BLK, 1); - if (!hfs_buffer_ok(buf)) { - hfs_warn("hfs_fs: Unable to read block 0.\n"); - goto done; - } - sig = hfs_get_ns(((struct hfs_drvr_desc *)hfs_buffer_data(buf))->ddSig); - hfs_buffer_put(buf); - - if (sig == htons(HFS_DRVR_DESC_MAGIC)) { - /* We are definitely on partitioned media. */ - dd_found = 1; - } - - buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK, 1); - if (!hfs_buffer_ok(buf)) { - hfs_warn("hfs_fs: Unable to read block 1.\n"); - goto done; - } - - *size = *start = 0; - - switch (hfs_get_ns(hfs_buffer_data(buf))) { - case __constant_htons(HFS_OLD_PMAP_MAGIC): - retval = parse_old_part_table(sys_mdb, buf, part, size, start); - break; - - case __constant_htons(HFS_NEW_PMAP_MAGIC): - retval = parse_new_part_table(sys_mdb, buf, part, size, start); - break; - - default: - if (dd_found) { - /* The media claimed to have a partition map */ - if (!silent) { - hfs_warn("hfs_fs: This disk has an " - "unrecognized partition map type.\n"); + struct buffer_head *bh; + u16 *data; + int i, size, res; + + res = -ENOENT; + bh = sb_bread512(sb, *part_start + HFS_PMAP_BLK, data); + if (!bh) + return -EIO; + + switch (be16_to_cpu(*data)) { + case HFS_OLD_PMAP_MAGIC: + { + struct old_pmap *pm; + struct old_pmap_entry *p; + + pm = (struct old_pmap *)bh->b_data; + p = pm->pdEntry; + size = 42; + for (i = 0; i < size; p++, i++) { + if (p->pdStart && p->pdSize && + p->pdFSID == cpu_to_be32(0x54465331)/*"TFS1"*/ && + (HFS_SB(sb)->part < 0 || HFS_SB(sb)->part == i)) { + *part_start += be32_to_cpu(p->pdStart); + *part_size = be32_to_cpu(p->pdSize); + res = 0; } - } else { - /* Conclude that the media is not partitioned */ - retval = 0; } - goto done; - } - - if (!retval) { - if (*start == 0) { - if (part) { - hfs_warn("hfs_fs: unable to locate " - "HFS partition number %d.\n", part); - } else { - hfs_warn("hfs_fs: unable to locate any " - "HFS partitions.\n"); + break; + } + case HFS_NEW_PMAP_MAGIC: + { + struct new_pmap *pm; + + pm = (struct new_pmap *)bh->b_data; + size = be32_to_cpu(pm->pmMapBlkCnt); + for (i = 0; i < size;) { + if (!memcmp(pm->pmPartType,"Apple_HFS", 9) && + (HFS_SB(sb)->part < 0 || HFS_SB(sb)->part == i)) { + *part_start += be32_to_cpu(pm->pmPyPartStart); + *part_size = be32_to_cpu(pm->pmPartBlkCnt); + res = 0; + break; } - retval = 1; - } else if (*size < 0) { - hfs_warn("hfs_fs: Partition size > 1 Terabyte.\n"); - retval = 1; - } else if (*start < 0) { - hfs_warn("hfs_fs: Partition begins beyond 1 " - "Terabyte.\n"); - retval = 1; + brelse(bh); + bh = sb_bread512(sb, *part_start + HFS_PMAP_BLK + ++i, pm); + if (!bh) + return -EIO; + if (pm->pmSig != cpu_to_be16(HFS_NEW_PMAP_MAGIC)) + break; } + break; + } } -done: - return retval; + brelse(bh); + + return res; } diff --git a/fs/hfs/string.c b/fs/hfs/string.c index f499b9a805bb..927a5af79428 100644 --- a/fs/hfs/string.c +++ b/fs/hfs/string.c @@ -1,7 +1,8 @@ /* - * linux/fs/hfs/string.c + * linux/fs/hfs/string.c * * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * This file contains the string comparison function for the @@ -10,15 +11,10 @@ * The code in this file is derived from code which is copyright * 1986, 1989, 1990 by Abacus Research and Development, Inc. (ARDI) * It is used here by the permission of ARDI's president Cliff Matthews. - * - * If you discover bugs in this code please notify both the author of the - * Linux HFS file system: hargrove@sccm.stanford.edu (Paul H. Hargrove) - * and the author of ARDI's HFS code: ctm@ardi.com (Clifford T. Matthews) - * - * "XXX" in a comment is a note to myself to consider changing something. */ -#include "hfs.h" +#include "hfs_fs.h" +#include <linux/dcache.h> /*================ File-local variables ================*/ @@ -32,48 +28,22 @@ * special case for those two characters. */ static unsigned char caseorder[256] = { -0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, -0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, -0x20,0x22,0x23,0x28,0x29,0x2A,0x2B,0x2C,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36, -0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46, -0x47,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E, -0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xA9,0xAA,0xAB,0xAC,0xAD, -0x4E,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E, -0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xAF,0xB0,0xB1,0xB2,0xB3, -0x4A,0x4C,0x5A,0x60,0x7B,0x7F,0x98,0x4F,0x49,0x51,0x4A,0x4B,0x4C,0x5A,0x60,0x63, -0x64,0x65,0x6E,0x6F,0x70,0x71,0x7B,0x84,0x85,0x86,0x7F,0x80,0x9A,0x9B,0x9C,0x98, -0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0x94,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0x4D,0x81, -0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0x55,0x8A,0xCC,0x4D,0x81, -0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0x26,0x27,0xD4,0x20,0x49,0x4B,0x80,0x82,0x82, -0xD5,0xD6,0x24,0x25,0x2D,0x2E,0xD7,0xD8,0xA6,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, -0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, -0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF -}; - -/* - * unsigned char casefold[] - * - * Defines the mapping to lowercase characters on the Macintosh - * - * "Inverse" of the 'casefold' from ARDI's code. - */ -static unsigned char casefold[256] = { -0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, -0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, -0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, -0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, -0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, -0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F, -0x41,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, -0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, -0x8A,0x8C,0x8D,0x8E,0x96,0x9A,0x9F,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, -0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, -0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xBE,0xBF, -0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, -0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0x88,0x8B,0x9B,0xCF,0xCF, -0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, -0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, -0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x22,0x23,0x28,0x29,0x2A,0x2B,0x2C,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36, + 0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46, + 0x47,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E, + 0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xA9,0xAA,0xAB,0xAC,0xAD, + 0x4E,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E, + 0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xAF,0xB0,0xB1,0xB2,0xB3, + 0x4A,0x4C,0x5A,0x60,0x7B,0x7F,0x98,0x4F,0x49,0x51,0x4A,0x4B,0x4C,0x5A,0x60,0x63, + 0x64,0x65,0x6E,0x6F,0x70,0x71,0x7B,0x84,0x85,0x86,0x7F,0x80,0x9A,0x9B,0x9C,0x98, + 0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0x94,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0x4D,0x81, + 0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0x55,0x8A,0xCC,0x4D,0x81, + 0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0x26,0x27,0xD4,0x20,0x49,0x4B,0x80,0x82,0x82, + 0xD5,0xD6,0x24,0x25,0x2D,0x2E,0xD7,0xD8,0xA6,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }; /*================ Global functions ================*/ @@ -81,14 +51,19 @@ static unsigned char casefold[256] = { /* * Hash a string to an integer in a case-independent way */ -unsigned int hfs_strhash(const unsigned char *name, unsigned int len) +int hfs_hash_dentry(struct dentry *dentry, struct qstr *this) { - unsigned long hash = init_name_hash(); + const unsigned char *name = this->name; + unsigned int hash, len = this->len; - while (len--) - hash = partial_name_hash(caseorder[*name++], - hash); - return end_name_hash(hash); + if (len > HFS_NAMELEN) + len = HFS_NAMELEN; + + hash = init_name_hash(); + for (; len; len--) + hash = partial_name_hash(caseorder[*name++], hash); + this->hash = end_name_hash(hash); + return 0; } /* @@ -106,39 +81,35 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1, len = (len1 > len2) ? len2 : len1; while (len--) { - if ((tmp = (int)caseorder[*(s1++)] - - (int)caseorder[*(s2++)])) { + tmp = (int)caseorder[*(s1++)] - (int)caseorder[*(s2++)]; + if (tmp) return tmp; - } } return len1 - len2; } /* * Test for equality of two strings in the HFS filename character ordering. + * return 1 on failure and 0 on success */ -int hfs_streq(const unsigned char *s1, unsigned int len1, - const unsigned char *s2, unsigned int len2) +int hfs_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2) { - if (len1 != len2) { - return 0; - } + const unsigned char *n1, *n2; + int len; - while (len1--) { - if (caseorder[*(s1++)] != caseorder[*(s2++)]) { - return 0; - } - } - return 1; -} + len = s1->len; + if (len >= HFS_NAMELEN) { + if (s2->len < HFS_NAMELEN) + return 1; + len = HFS_NAMELEN; + } else if (len != s2->len) + return 1; -/* - * Convert a string to the Macintosh version of lower case. - */ -void hfs_tolower(unsigned char *p, int len) -{ + n1 = s1->name; + n2 = s2->name; while (len--) { - *p = casefold[*p]; - ++p; + if (caseorder[*n1++] != caseorder[*n2++]) + return 1; } + return 0; } diff --git a/fs/hfs/super.c b/fs/hfs/super.c index e66e4ac9c1e2..86e02098ac4f 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -1,7 +1,8 @@ /* - * linux/fs/hfs/super.c + * linux/fs/hfs/super.c * * Copyright (C) 1995-1997 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * This file contains hfs_read_super(), some of the super_ops and @@ -9,123 +10,22 @@ * inode.c since they deal with inodes. * * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - * - * The code in this file initializes some structures which contain - * pointers by calling memset(&foo, 0, sizeof(foo)). - * This produces the desired behavior only due to the non-ANSI - * assumption that the machine representation of NULL is all zeros. */ -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> - -#include <linux/config.h> /* for CONFIG_MAC_PARTITION */ -#include <linux/blkdev.h> +#include <linux/config.h> #include <linux/module.h> +#include <linux/blkdev.h> #include <linux/init.h> -#include <linux/parser.h> -#include <linux/smp_lock.h> #include <linux/vfs.h> -MODULE_LICENSE("GPL"); - -/*================ Forward declarations ================*/ - -static void hfs_read_inode(struct inode *); -static void hfs_put_super(struct super_block *); -static int hfs_statfs(struct super_block *, struct kstatfs *); -static void hfs_write_super(struct super_block *); - -static kmem_cache_t * hfs_inode_cachep; - -static struct inode *hfs_alloc_inode(struct super_block *sb) -{ - struct hfs_inode_info *ei; - ei = (struct hfs_inode_info *)kmem_cache_alloc(hfs_inode_cachep, SLAB_KERNEL); - if (!ei) - return NULL; - return &ei->vfs_inode; -} - -static void hfs_destroy_inode(struct inode *inode) -{ - kmem_cache_free(hfs_inode_cachep, HFS_I(inode)); -} - -static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) -{ - struct hfs_inode_info *ei = (struct hfs_inode_info *) foo; - - if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) - inode_init_once(&ei->vfs_inode); -} - -static int init_inodecache(void) -{ - hfs_inode_cachep = kmem_cache_create("hfs_inode_cache", - sizeof(struct hfs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, - init_once, NULL); - if (hfs_inode_cachep == NULL) - return -ENOMEM; - return 0; -} - -static void destroy_inodecache(void) -{ - if (kmem_cache_destroy(hfs_inode_cachep)) - printk(KERN_INFO "hfs_inode_cache: not all structures were freed\n"); -} - -/*================ Global variables ================*/ - -static struct super_operations hfs_super_operations = { - .alloc_inode = hfs_alloc_inode, - .destroy_inode = hfs_destroy_inode, - .read_inode = hfs_read_inode, - .put_inode = hfs_put_inode, - .put_super = hfs_put_super, - .write_super = hfs_write_super, - .statfs = hfs_statfs, -}; - -/*================ File-local variables ================*/ +#include "hfs_fs.h" +#include "btree.h" -static struct super_block *hfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) -{ - return get_sb_bdev(fs_type, flags, dev_name, data, hfs_fill_super); -} +const char hfs_version[]="0.96"; -static struct file_system_type hfs_fs = { - .owner = THIS_MODULE, - .name = "hfs", - .get_sb = hfs_get_sb, - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, -}; - -/*================ File-local functions ================*/ +static kmem_cache_t *hfs_inode_cachep; -/* - * hfs_read_inode() - * - * this doesn't actually do much. hfs_iget actually fills in the - * necessary inode information. - */ -static void hfs_read_inode(struct inode *inode) -{ - inode->i_mode = 0; -} +MODULE_LICENSE("GPL"); /* * hfs_write_super() @@ -148,20 +48,11 @@ static void hfs_read_inode(struct inode *inode) */ static void hfs_write_super(struct super_block *sb) { - struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb; - lock_kernel(); - /* is this a valid hfs superblock? */ - if (!sb || sb->s_magic != HFS_SUPER_MAGIC) { - unlock_kernel(); - return; - } - - if (!(sb->s_flags & MS_RDONLY)) { - /* sync everything to the buffers */ - hfs_mdb_commit(mdb, 0); - } sb->s_dirt = 0; - unlock_kernel(); + if (sb->s_flags & MS_RDONLY) + return; + /* sync everything to the buffers */ + hfs_mdb_commit(sb); } /* @@ -173,18 +64,9 @@ static void hfs_write_super(struct super_block *sb) */ static void hfs_put_super(struct super_block *sb) { - struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb; - - if (!(sb->s_flags & MS_RDONLY)) { - hfs_mdb_commit(mdb, 0); - sb->s_dirt = 0; - } - + hfs_mdb_close(sb); /* release the MDB's resources */ - hfs_mdb_put(mdb, sb->s_flags & MS_RDONLY); - - kfree(sb->s_fs_info); - sb->s_fs_info = NULL; + hfs_mdb_put(sb); } /* @@ -198,294 +80,161 @@ static void hfs_put_super(struct super_block *sb) */ static int hfs_statfs(struct super_block *sb, struct kstatfs *buf) { - struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb; - buf->f_type = HFS_SUPER_MAGIC; - buf->f_bsize = HFS_SECTOR_SIZE; - buf->f_blocks = mdb->alloc_blksz * mdb->fs_ablocks; - buf->f_bfree = mdb->alloc_blksz * mdb->free_ablocks; + buf->f_bsize = sb->s_blocksize; + buf->f_blocks = (u32)HFS_SB(sb)->fs_ablocks * HFS_SB(sb)->fs_div; + buf->f_bfree = (u32)HFS_SB(sb)->free_ablocks * HFS_SB(sb)->fs_div; buf->f_bavail = buf->f_bfree; - buf->f_files = mdb->fs_ablocks; - buf->f_ffree = mdb->free_ablocks; + buf->f_files = HFS_SB(sb)->fs_ablocks; + buf->f_ffree = HFS_SB(sb)->free_ablocks; buf->f_namelen = HFS_NAMELEN; return 0; } -enum { - Opt_version, Opt_uid, Opt_gid, Opt_umask, Opt_part, - Opt_type, Opt_creator, Opt_quiet, Opt_afpd, - Opt_names_netatalk, Opt_names_trivial, Opt_names_alpha, Opt_names_latin, - Opt_names_7bit, Opt_names_8bit, Opt_names_cap, - Opt_fork_netatalk, Opt_fork_single, Opt_fork_double, Opt_fork_cap, - Opt_case_lower, Opt_case_asis, - Opt_conv_binary, Opt_conv_text, Opt_conv_auto, -}; +int hfs_remount(struct super_block *sb, int *flags, char *data) +{ + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + return 0; + if (!(*flags & MS_RDONLY)) { + if (!(HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_UNMNT)) + || (HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_INCNSTNT))) { + printk("HFS-fs warning: Filesystem was not cleanly unmounted, " + "running fsck.hfs is recommended. leaving read-only.\n"); + sb->s_flags |= MS_RDONLY; + *flags |= MS_RDONLY; + } else if (HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_SLOCK)) { + printk("HFS-fs: Filesystem is marked locked, leaving read-only.\n"); + sb->s_flags |= MS_RDONLY; + *flags |= MS_RDONLY; + } + } + return 0; +} -static match_table_t tokens = { - {Opt_version, "version=%u"}, - {Opt_uid, "uid=%u"}, - {Opt_gid, "gid=%u"}, - {Opt_umask, "umask=%o"}, - {Opt_part, "part=%u"}, - {Opt_type, "type=%s"}, - {Opt_creator, "creator=%s"}, - {Opt_quiet, "quiet"}, - {Opt_afpd, "afpd"}, - {Opt_names_netatalk, "names=netatalk"}, - {Opt_names_trivial, "names=trivial"}, - {Opt_names_alpha, "names=alpha"}, - {Opt_names_latin, "names=latin"}, - {Opt_names_7bit, "names=7bit"}, - {Opt_names_8bit, "names=8bit"}, - {Opt_names_cap, "names=cap"}, - {Opt_names_netatalk, "names=n"}, - {Opt_names_trivial, "names=t"}, - {Opt_names_alpha, "names=a"}, - {Opt_names_latin, "names=l"}, - {Opt_names_7bit, "names=7"}, - {Opt_names_8bit, "names=8"}, - {Opt_names_cap, "names=c"}, - {Opt_fork_netatalk, "fork=netatalk"}, - {Opt_fork_single, "fork=single"}, - {Opt_fork_double, "fork=double"}, - {Opt_fork_cap, "fork=cap"}, - {Opt_fork_netatalk, "fork=n"}, - {Opt_fork_single, "fork=s"}, - {Opt_fork_double, "fork=d"}, - {Opt_fork_cap, "fork=c"}, - {Opt_case_lower, "case=lower"}, - {Opt_case_asis, "case=asis"}, - {Opt_case_lower, "case=l"}, - {Opt_case_asis, "case=a"}, - {Opt_conv_binary, "conv=binary"}, - {Opt_conv_text, "conv=text"}, - {Opt_conv_auto, "conv=auto"}, - {Opt_conv_binary, "conv=b"}, - {Opt_conv_text, "conv=t"}, - {Opt_conv_auto, "conv=a"}, +static struct inode *hfs_alloc_inode(struct super_block *sb) +{ + struct hfs_inode_info *i; + + i = kmem_cache_alloc(hfs_inode_cachep, SLAB_KERNEL); + return i ? &i->vfs_inode : NULL; +} + +static void hfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(hfs_inode_cachep, HFS_I(inode)); +} + +static struct super_operations hfs_super_operations = { + .alloc_inode = hfs_alloc_inode, + .destroy_inode = hfs_destroy_inode, + .write_inode = hfs_write_inode, + .clear_inode = hfs_clear_inode, + .put_super = hfs_put_super, + .write_super = hfs_write_super, + .statfs = hfs_statfs, + .remount_fs = hfs_remount, }; /* * parse_options() - * + * * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger * This function is called by hfs_read_super() to parse the mount options. */ -static int parse_options(char *options, struct hfs_sb_info *hsb, int *part) +static int parse_options(char *options, struct hfs_sb_info *hsb) { - char *p; - char names, fork; - substring_t args[MAX_OPT_ARGS]; - int option; + char *this_char, *value; /* initialize the sb with defaults */ - memset(hsb, 0, sizeof(*hsb)); - hsb->magic = HFS_SB_MAGIC; - hsb->s_uid = current->uid; - hsb->s_gid = current->gid; - hsb->s_umask = current->fs->umask; - hsb->s_type = 0x3f3f3f3f; /* == '????' */ + hsb->s_uid = current->uid; + hsb->s_gid = current->gid; + hsb->s_file_umask = 0644; + hsb->s_dir_umask = 0755; + hsb->s_type = 0x3f3f3f3f; /* == '????' */ hsb->s_creator = 0x3f3f3f3f; /* == '????' */ - hsb->s_lowercase = 0; - hsb->s_quiet = 0; - hsb->s_afpd = 0; - /* default version. 0 just selects the defaults */ - hsb->s_version = 0; - hsb->s_conv = 'b'; - names = '?'; - fork = '?'; - *part = 0; - - if (!options) { - goto done; - } - while ((p = strsep(&options,",")) != NULL) { - int token; - if (!*p) + hsb->s_quiet = 0; + hsb->part = -1; + hsb->session = -1; + + if (!options) + return 1; + + while ((this_char = strsep(&options, ","))) { + if (!*this_char) continue; + value = strchr(this_char, '='); + if (value) + *value++ = 0; - token = match_token(p, tokens, args); - switch (token) { - /* Numeric-valued options */ - case Opt_version: - if (match_int(&args[0], &option)) + /* Numeric-valued options */ + if (!strcmp(this_char, "uid")) { + if (!value || !*value) + return 0; + hsb->s_uid = simple_strtoul(value, &value, 0); + if (*value) + return 0; + } else if (!strcmp(this_char, "gid")) { + if (!value || !*value) return 0; - hsb->s_version = option; - break; - case Opt_uid: - if (match_int(&args[0], &option)) + hsb->s_gid = simple_strtoul(value, &value, 0); + if (*value) return 0; - hsb->s_uid = option; - break; - case Opt_gid: - if (match_int(&args[0], &option)) + } else if (!strcmp(this_char, "umask")) { + if (!value || !*value) return 0; - hsb->s_gid = option; - break; - case Opt_umask: - if (match_octal(&args[0], &option)) + hsb->s_file_umask = simple_strtoul(value, &value, 8); + hsb->s_dir_umask = hsb->s_file_umask; + if (*value) return 0; - hsb->s_umask = option; - break; - case Opt_part: - if (match_int(&args[0], &option)) + } else if (!strcmp(this_char, "file_umask")) { + if (!value || !*value) return 0; - *part = option; - break; - /* String-valued options */ - case Opt_type: - if (strlen(args[0].from) != 4) { + hsb->s_file_umask = simple_strtoul(value, &value, 8); + if (*value) return 0; - } - hsb->s_type = hfs_get_nl(args[0].from); - break; - case Opt_creator: - if (strlen(args[0].from) != 4) { + } else if (!strcmp(this_char, "dir_umask")) { + if (!value || !*value) + return 0; + hsb->s_dir_umask = simple_strtoul(value, &value, 8); + if (*value) + return 0; + } else if (!strcmp(this_char, "part")) { + if (!value || !*value) + return 0; + hsb->part = simple_strtoul(value, &value, 0); + if (*value) + return 0; + } else if (!strcmp(this_char, "session")) { + if (!value || !*value) + return 0; + hsb->session = simple_strtoul(value, &value, 0); + if (*value) + return 0; + /* String-valued options */ + } else if (!strcmp(this_char, "type") && value) { + if (strlen(value) != 4) + return 0; + hsb->s_type = *(u32 *)value; + } else if (!strcmp(this_char, "creator") && value) { + if (strlen(value) != 4) + return 0; + hsb->s_creator = *(u32 *)value; + /* Boolean-valued options */ + } else if (!strcmp(this_char, "quiet")) { + if (value) return 0; - } - hsb->s_creator = hfs_get_nl(args[0].from); - break; - /* Boolean-valued options */ - case Opt_quiet: hsb->s_quiet = 1; - break; - case Opt_afpd: - hsb->s_afpd = 1; - break; - /* Multiple choice options */ - case Opt_names_netatalk: - names = 'n'; - break; - case Opt_names_trivial: - names = 't'; - break; - case Opt_names_alpha: - names = 'a'; - break; - case Opt_names_latin: - names = 'l'; - break; - case Opt_names_7bit: - names = '7'; - break; - case Opt_names_8bit: - names = '8'; - break; - case Opt_names_cap: - names = 'c'; - break; - case Opt_fork_netatalk: - fork = 'n'; - break; - case Opt_fork_single: - fork = 's'; - break; - case Opt_fork_double: - fork = 'd'; - break; - case Opt_fork_cap: - fork = 'c'; - break; - case Opt_case_lower: - hsb->s_lowercase = 1; - break; - case Opt_case_asis: - hsb->s_lowercase = 0; - break; - case Opt_conv_binary: - hsb->s_conv = 'b'; - break; - case Opt_conv_text: - hsb->s_conv = 't'; - break; - case Opt_conv_auto: - hsb->s_conv = 'a'; - break; - default: + } else return 0; - } } -done: - /* Parse the "fork" and "names" options */ - if (fork == '?') { - fork = hsb->s_afpd ? 'n' : 'c'; - } - switch (fork) { - default: - case 'c': - hsb->s_ifill = hfs_cap_ifill; - hsb->s_reserved1 = hfs_cap_reserved1; - hsb->s_reserved2 = hfs_cap_reserved2; - break; - - case 's': - hfs_warn("hfs_fs: AppleSingle not yet implemented.\n"); - return 0; - /* break; */ - - case 'd': - hsb->s_ifill = hfs_dbl_ifill; - hsb->s_reserved1 = hfs_dbl_reserved1; - hsb->s_reserved2 = hfs_dbl_reserved2; - break; - - case 'n': - hsb->s_ifill = hfs_nat_ifill; - hsb->s_reserved1 = hfs_nat_reserved1; - hsb->s_reserved2 = hfs_nat_reserved2; - break; - } - - if (names == '?') { - names = fork; - } - switch (names) { - default: - case 'n': - hsb->s_nameout = hfs_colon2mac; - hsb->s_namein = hfs_mac2nat; - break; - - case 'c': - hsb->s_nameout = hfs_colon2mac; - hsb->s_namein = hfs_mac2cap; - break; - - case 't': - hsb->s_nameout = hfs_triv2mac; - hsb->s_namein = hfs_mac2triv; - break; - - case '7': - hsb->s_nameout = hfs_prcnt2mac; - hsb->s_namein = hfs_mac2seven; - break; - - case '8': - hsb->s_nameout = hfs_prcnt2mac; - hsb->s_namein = hfs_mac2eight; - break; - - case 'l': - hsb->s_nameout = hfs_latin2mac; - hsb->s_namein = hfs_mac2latin; - break; - - case 'a': /* 's' and 'd' are unadvertised aliases for 'alpha', */ - case 's': /* since 'alpha' is the default if fork=s or fork=d. */ - case 'd': /* (It is also helpful for poor typists!) */ - hsb->s_nameout = hfs_prcnt2mac; - hsb->s_namein = hfs_mac2alpha; - break; - } + hsb->s_dir_umask &= 0777; + hsb->s_file_umask &= 0777; return 1; } -/*================ Global functions ================*/ - /* * hfs_read_super() * @@ -497,122 +246,113 @@ done: * hfs_btree_init() to get the necessary data about the extents and * catalog B-trees and, finally, reading the root inode into memory. */ -int hfs_fill_super(struct super_block *s, void *data, int silent) +static int hfs_fill_super(struct super_block *sb, void *data, int silent) { struct hfs_sb_info *sbi; - struct hfs_mdb *mdb; - struct hfs_cat_key key; - hfs_s32 part_size, part_start; + struct hfs_find_data fd; + hfs_cat_rec rec; struct inode *root_inode; - int part; + int res; sbi = kmalloc(sizeof(struct hfs_sb_info), GFP_KERNEL); if (!sbi) return -ENOMEM; - s->s_fs_info = sbi; + sb->s_fs_info = sbi; memset(sbi, 0, sizeof(struct hfs_sb_info)); + INIT_HLIST_HEAD(&sbi->rsrc_inodes); - if (!parse_options((char *)data, sbi, &part)) { + res = -EINVAL; + if (!parse_options((char *)data, sbi)) { hfs_warn("hfs_fs: unable to parse mount options.\n"); - goto bail2; + goto bail3; } - /* set the device driver to 512-byte blocks */ - sb_set_blocksize(s, HFS_SECTOR_SIZE); + sb->s_op = &hfs_super_operations; + init_MUTEX(&sbi->bitmap_lock); -#ifdef CONFIG_MAC_PARTITION - /* check to see if we're in a partition */ - mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0); - - /* erk. try parsing the partition table ourselves */ - if (!mdb) { - if (hfs_part_find(s, part, silent, &part_size, &part_start)) { - goto bail2; - } - mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start); - } -#else - if (hfs_part_find(s, part, silent, &part_size, &part_start)) { - goto bail2; - } - - mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start); -#endif - - if (!mdb) { - if (!silent) { + res = hfs_mdb_get(sb); + if (res) { + if (!silent) hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n", - s->s_id); - } + hfs_mdb_name(sb)); goto bail2; } - sbi->s_mdb = mdb; - if (HFS_ITYPE(mdb->next_id) != 0) { - hfs_warn("hfs_fs: too many files.\n"); - goto bail1; - } - - s->s_magic = HFS_SUPER_MAGIC; - s->s_op = &hfs_super_operations; - /* try to get the root inode */ - hfs_cat_build_key(htonl(HFS_POR_CNID), - (struct hfs_name *)(mdb->vname), &key); - - root_inode = hfs_iget(hfs_cat_get(mdb, &key), HFS_ITYPE_NORM, NULL); - if (!root_inode) + hfs_find_init(HFS_SB(sb)->cat_tree, &fd); + res = hfs_cat_find_brec(sb, HFS_ROOT_CNID, &fd); + if (!res) + hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, fd.entrylength); + if (res) { + hfs_find_exit(&fd); goto bail_no_root; - - s->s_root = d_alloc_root(root_inode); - if (!s->s_root) + } + root_inode = hfs_iget(sb, &fd.search_key->cat, &rec); + hfs_find_exit(&fd); + if (!root_inode) goto bail_no_root; - /* fix up pointers. */ - HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] = - s->s_root; - s->s_root->d_op = &hfs_dentry_operations; + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + goto bail_no_root; + + sb->s_root->d_op = &hfs_dentry_operations; /* everything's okay */ return 0; -bail_no_root: +bail_no_root: hfs_warn("hfs_fs: get root inode failed.\n"); - iput(root_inode); -bail1: - hfs_mdb_put(mdb, s->s_flags & MS_RDONLY); + hfs_mdb_put(sb); bail2: +bail3: kfree(sbi); - s->s_fs_info = NULL; - return -EINVAL; + return res; +} + +static struct super_block *hfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, hfs_fill_super); +} + +static struct file_system_type hfs_fs_type = { + .owner = THIS_MODULE, + .name = "hfs", + .get_sb = hfs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static void hfs_init_once(void *p, kmem_cache_t *cachep, unsigned long flags) +{ + struct hfs_inode_info *i = p; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&i->vfs_inode); } static int __init init_hfs_fs(void) { - int err = init_inodecache(); - if (err) - goto out1; - hfs_cat_init(); - err = register_filesystem(&hfs_fs); + int err; + + hfs_inode_cachep = kmem_cache_create("hfs_inode_cache", + sizeof(struct hfs_inode_info), 0, SLAB_HWCACHE_ALIGN, + hfs_init_once, NULL); + if (!hfs_inode_cachep) + return -ENOMEM; + err = register_filesystem(&hfs_fs_type); if (err) - goto out; - return 0; -out: - hfs_cat_free(); - destroy_inodecache(); -out1: + kmem_cache_destroy(hfs_inode_cachep); return err; } -static void __exit exit_hfs_fs(void) { - hfs_cat_free(); - unregister_filesystem(&hfs_fs); - destroy_inodecache(); +static void __exit exit_hfs_fs(void) +{ + unregister_filesystem(&hfs_fs_type); + if (kmem_cache_destroy(hfs_inode_cachep)) + printk(KERN_INFO "hfs_inode_cache: not all structures were freed\n"); } module_init(init_hfs_fs) module_exit(exit_hfs_fs) - -#if defined(DEBUG_ALL) || defined(DEBUG_MEM) -long int hfs_alloc = 0; -#endif diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c index 1b083b8b9a2f..5bf89ec01cd4 100644 --- a/fs/hfs/sysdep.c +++ b/fs/hfs/sysdep.c @@ -1,109 +1,40 @@ /* - * linux/fs/hfs/sysdep.c + * linux/fs/hfs/sysdep.c * * Copyright (C) 1996 Paul H. Hargrove + * (C) 2003 Ardis Technologies <roman@ardistech.com> * This file may be distributed under the terms of the GNU General Public License. * * This file contains the code to do various system dependent things. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. */ -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> -#include <linux/smp_lock.h> - -static int hfs_revalidate_dentry(struct dentry *, struct nameidata *); -static int hfs_hash_dentry(struct dentry *, struct qstr *); -static int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); -static void hfs_dentry_iput(struct dentry *, struct inode *); -struct dentry_operations hfs_dentry_operations = -{ - .d_revalidate = hfs_revalidate_dentry, - .d_hash = hfs_hash_dentry, - .d_compare = hfs_compare_dentry, - .d_iput = hfs_dentry_iput, -}; - -/* - * hfs_buffer_get() - * - * Return a buffer for the 'block'th block of the media. - * If ('read'==0) then the buffer is not read from disk. - */ -hfs_buffer hfs_buffer_get(hfs_sysmdb sys_mdb, int block, int read) { - hfs_buffer tmp = HFS_BAD_BUFFER; - - if (read) { - tmp = sb_bread(sys_mdb, block); - } else { - tmp = sb_getblk(sys_mdb, block); - if (tmp) { - set_buffer_uptodate(tmp); - } - } - if (!tmp) { - hfs_error("hfs_fs: unable to read block 0x%08x from dev %s\n", - block, hfs_mdb_name(sys_mdb)); - } - - return tmp; -} +#include "hfs_fs.h" /* dentry case-handling: just lowercase everything */ -/* hfs_strhash now uses the same hashing function as the dcache. */ -static int hfs_hash_dentry(struct dentry *dentry, struct qstr *this) -{ - if (this->len > HFS_NAMELEN) - return 0; - - this->hash = hfs_strhash(this->name, this->len); - return 0; -} - -/* return 1 on failure and 0 on success */ -static int hfs_compare_dentry(struct dentry *dentry, struct qstr *a, - struct qstr *b) -{ - if (a->len != b->len) return 1; - - if (a->len > HFS_NAMELEN) - return 1; - - return !hfs_streq(a->name, a->len, b->name, b->len); -} - -static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode) -{ - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - - lock_kernel(); - entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL; - unlock_kernel(); - iput(inode); -} - static int hfs_revalidate_dentry(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; int diff; + if(!inode) + return 1; + /* fix up inode on a timezone change */ - lock_kernel(); - if (inode && - (diff = (hfs_to_utc(0) - HFS_I(inode)->tz_secondswest))) { + diff = sys_tz.tz_minuteswest * 60 - HFS_I(inode)->tz_secondswest; + if (diff) { inode->i_ctime.tv_sec += diff; inode->i_atime.tv_sec += diff; inode->i_mtime.tv_sec += diff; HFS_I(inode)->tz_secondswest += diff; } - unlock_kernel(); return 1; } + +struct dentry_operations hfs_dentry_operations = +{ + .d_revalidate = hfs_revalidate_dentry, + .d_hash = hfs_hash_dentry, + .d_compare = hfs_compare_dentry, +}; + diff --git a/fs/hfs/trans.c b/fs/hfs/trans.c index 64adf73fc5c6..fb9720abbadd 100644 --- a/fs/hfs/trans.c +++ b/fs/hfs/trans.c @@ -1,5 +1,5 @@ /* - * linux/fs/hfs/trans.c + * linux/fs/hfs/trans.c * * Copyright (C) 1995-1997 Paul H. Hargrove * This file may be distributed under the terms of the GNU General Public License. @@ -7,300 +7,13 @@ * This file contains routines for converting between the Macintosh * character set and various other encodings. This includes dealing * with ':' vs. '/' as the path-element separator. - * - * Latin-1 translation based on code contributed by Holger Schemel - * (aeglos@valinor.owl.de). - * - * The '8-bit', '7-bit ASCII' and '7-bit alphanumeric' encodings are - * implementations of the three encodings recommended by Apple in the - * document "AppleSingle/AppleDouble Formats: Developer's Note - * (9/94)". This document is available from Apple's Technical - * Information Library from the World Wide Web server - * www.info.apple.com. - * - * The 'CAP' encoding is an implementation of the naming scheme used - * by the Columbia AppleTalk Package, available for anonymous FTP from - * ????. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. */ -#include "hfs.h" -#include <linux/hfs_fs_sb.h> -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs.h> - -/*================ File-local variables ================*/ - -/* int->ASCII map for a single hex digit */ -static char hex[16] = {'0','1','2','3','4','5','6','7', - '8','9','a','b','c','d','e','f'}; -/* - * Latin-1 to Mac character set map - * - * For the sake of consistency this map is generated from the Mac to - * Latin-1 map the first time it is needed. This means there is just - * one map to maintain. - */ -static unsigned char latin2mac_map[128]; /* initially all zero */ - -/* - * Mac to Latin-1 map for the upper 128 characters (both have ASCII in - * the lower 128 positions) - */ -static unsigned char mac2latin_map[128] = { - 0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1, - 0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8, - 0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3, - 0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC, - 0x00, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF, - 0xAE, 0xA9, 0x00, 0xB4, 0xA8, 0x00, 0xC6, 0xD8, - 0x00, 0xB1, 0x00, 0x00, 0xA5, 0xB5, 0xF0, 0x00, - 0x00, 0x00, 0x00, 0xAA, 0xBA, 0x00, 0xE6, 0xF8, - 0xBF, 0xA1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xAB, - 0xBB, 0x00, 0xA0, 0xC0, 0xC3, 0xD5, 0x00, 0x00, - 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00, - 0xFF, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xB8, 0x00, 0x00, 0xC2, 0xCA, 0xC1, - 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4, - 0x00, 0xD2, 0xDA, 0xDB, 0xD9, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/*================ File-local functions ================*/ - -/* - * dehex() - * - * Given a hexadecimal digit in ASCII, return the integer representation. - */ -static inline const unsigned char dehex(char c) { - if ((c>='0')&&(c<='9')) { - return c-'0'; - } - if ((c>='a')&&(c<='f')) { - return c-'a'+10; - } - if ((c>='A')&&(c<='F')) { - return c-'A'+10; - } - return 0xff; -} +#include "hfs_fs.h" /*================ Global functions ================*/ /* - * hfs_mac2nat() - * - * Given a 'Pascal String' (a string preceded by a length byte) in - * the Macintosh character set produce the corresponding filename using - * the Netatalk name-mangling scheme, returning the length of the - * mangled filename. Note that the output string is not NULL terminated. - * - * The name-mangling works as follows: - * Characters 32-126 (' '-'~') except '/' and any initial '.' are passed - * unchanged from input to output. The remaining characters are replaced - * by three characters: ':xx' where xx is the hexadecimal representation - * of the character, using lowercase 'a' through 'f'. - */ -int hfs_mac2nat(char *out, const struct hfs_name *in) { - unsigned char c; - const unsigned char *p = in->Name; - int len = in->Len; - int count = 0; - - /* Special case for .AppleDesktop which in the - distant future may be a pseudodirectory. */ - if (strncmp(".AppleDesktop", p, len) == 0) { - strncpy(out, p, 13); - return 13; - } - - while (len--) { - c = *p++; - if ((c<32) || (c=='/') || (c>126) || (!count && (c=='.'))) { - *out++ = ':'; - *out++ = hex[(c>>4) & 0xf]; - *out++ = hex[c & 0xf]; - count += 3; - } else { - *out++ = c; - count++; - } - } - return count; -} - -/* - * hfs_mac2cap() - * - * Given a 'Pascal String' (a string preceded by a length byte) in - * the Macintosh character set produce the corresponding filename using - * the CAP name-mangling scheme, returning the length of the mangled - * filename. Note that the output string is not NULL terminated. - * - * The name-mangling works as follows: - * Characters 32-126 (' '-'~') except '/' are passed unchanged from - * input to output. The remaining characters are replaced by three - * characters: ':xx' where xx is the hexadecimal representation of the - * character, using lowercase 'a' through 'f'. - */ -int hfs_mac2cap(char *out, const struct hfs_name *in) { - unsigned char c; - const unsigned char *p = in->Name; - int len = in->Len; - int count = 0; - - while (len--) { - c = *p++; - if ((c<32) || (c=='/') || (c>126)) { - *out++ = ':'; - *out++ = hex[(c>>4) & 0xf]; - *out++ = hex[c & 0xf]; - count += 3; - } else { - *out++ = c; - count++; - } - } - return count; -} - -/* - * hfs_mac2eight() - * - * Given a 'Pascal String' (a string preceded by a length byte) in - * the Macintosh character set produce the corresponding filename using - * the '8-bit' name-mangling scheme, returning the length of the - * mangled filename. Note that the output string is not NULL - * terminated. - * - * This is one of the three recommended naming conventions described - * in Apple's document "AppleSingle/AppleDouble Formats: Developer's - * Note (9/94)" - * - * The name-mangling works as follows: - * Characters 0, '%' and '/' are replaced by three characters: '%xx' - * where xx is the hexadecimal representation of the character, using - * lowercase 'a' through 'f'. All other characters are passed - * unchanged from input to output. Note that this format is mainly - * implemented for completeness and is rather hard to read. - */ -int hfs_mac2eight(char *out, const struct hfs_name *in) { - unsigned char c; - const unsigned char *p = in->Name; - int len = in->Len; - int count = 0; - - while (len--) { - c = *p++; - if (!c || (c=='/') || (c=='%')) { - *out++ = '%'; - *out++ = hex[(c>>4) & 0xf]; - *out++ = hex[c & 0xf]; - count += 3; - } else { - *out++ = c; - count++; - } - } - return count; -} - -/* - * hfs_mac2seven() - * - * Given a 'Pascal String' (a string preceded by a length byte) in - * the Macintosh character set produce the corresponding filename using - * the '7-bit ASCII' name-mangling scheme, returning the length of the - * mangled filename. Note that the output string is not NULL - * terminated. - * - * This is one of the three recommended naming conventions described - * in Apple's document "AppleSingle/AppleDouble Formats: Developer's - * Note (9/94)" - * - * The name-mangling works as follows: - * Characters 0, '%', '/' and 128-255 are replaced by three - * characters: '%xx' where xx is the hexadecimal representation of the - * character, using lowercase 'a' through 'f'. All other characters - * are passed unchanged from input to output. Note that control - * characters (including newline) and space are unchanged make reading - * these filenames difficult. - */ -int hfs_mac2seven(char *out, const struct hfs_name *in) { - unsigned char c; - const unsigned char *p = in->Name; - int len = in->Len; - int count = 0; - - while (len--) { - c = *p++; - if (!c || (c=='/') || (c=='%') || (c&0x80)) { - *out++ = '%'; - *out++ = hex[(c>>4) & 0xf]; - *out++ = hex[c & 0xf]; - count += 3; - } else { - *out++ = c; - count++; - } - } - return count; -} - -/* - * hfs_mac2alpha() - * - * Given a 'Pascal String' (a string preceded by a length byte) in - * the Macintosh character set produce the corresponding filename using - * the '7-bit alphanumeric' name-mangling scheme, returning the length - * of the mangled filename. Note that the output string is not NULL - * terminated. - * - * This is one of the three recommended naming conventions described - * in Apple's document "AppleSingle/AppleDouble Formats: Developer's - * Note (9/94)" - * - * The name-mangling works as follows: - * The characters 'a'-'z', 'A'-'Z', '0'-'9', '_' and the last '.' in - * the filename are passed unchanged from input to output. All - * remaining characters (including any '.'s other than the last) are - * replaced by three characters: '%xx' where xx is the hexadecimal - * representation of the character, using lowercase 'a' through 'f'. - */ -int hfs_mac2alpha(char *out, const struct hfs_name *in) { - unsigned char c; - const unsigned char *p = in->Name; - int len = in->Len; - int count = 0; - const unsigned char *lp; /* last period */ - - /* strrchr() would be good here, but 'in' is not null-terminated */ - for (lp=p+len-1; (lp>=p)&&(*lp!='.'); --lp) {} - ++lp; - - while (len--) { - c = *p++; - if ((p==lp) || ((c>='0')&&(c<='9')) || ((c>='A')&&(c<='Z')) || - ((c>='a')&&(c<='z')) || (c=='_')) { - *out++ = c; - count++; - } else { - *out++ = '%'; - *out++ = hex[(c>>4) & 0xf]; - *out++ = hex[c & 0xf]; - count += 3; - } - } - return count; -} - -/* * hfs_mac2triv() * * Given a 'Pascal String' (a string preceded by a length byte) in @@ -314,154 +27,19 @@ int hfs_mac2alpha(char *out, const struct hfs_name *in) { * by ':' which never appears in HFS filenames. All other characters * are passed unchanged from input to output. */ -int hfs_mac2triv(char *out, const struct hfs_name *in) { - unsigned char c; - const unsigned char *p = in->Name; - int len = in->Len; - int count = 0; - - while (len--) { - c = *p++; - if (c=='/') { - *out++ = ':'; - } else { - *out++ = c; - } - count++; - } - return count; -} - -/* - * hfs_mac2latin() - * - * Given a 'Pascal String' (a string preceded by a length byte) in - * the Macintosh character set produce the corresponding filename using - * the 'Latin-1' name-mangling scheme, returning the length of the - * mangled filename. Note that the output string is not NULL - * terminated. - * - * The Macintosh character set and Latin-1 are both extensions of the - * ASCII character set. Some, but certainly not all, of the characters - * in the Macintosh character set are also in Latin-1 but not with the - * same encoding. This name-mangling scheme replaces the characters in - * the Macintosh character set that have Latin-1 equivalents by those - * equivalents; the characters 32-126, excluding '/' and '%', are - * passed unchanged from input to output. The remaining characters - * are replaced by three characters: '%xx' where xx is the hexadecimal - * representation of the character, using lowercase 'a' through 'f'. - * - * The array mac2latin_map[] indicates the correspondence between the - * two character sets. The byte in element x-128 gives the Latin-1 - * encoding of the character with encoding x in the Macintosh - * character set. A value of zero indicates Latin-1 has no - * corresponding character. - */ -int hfs_mac2latin(char *out, const struct hfs_name *in) { - unsigned char c; - const unsigned char *p = in->Name; - int len = in->Len; - int count = 0; +int hfs_mac2triv(char *out, const struct hfs_name *in) +{ + const char *p; + char c; + int i, len; - while (len--) { + len = in->len; + p = in->name; + for (i = 0; i < len; i++) { c = *p++; - - if ((c & 0x80) && mac2latin_map[c & 0x7f]) { - *out++ = mac2latin_map[c & 0x7f]; - count++; - } else if ((c>=32) && (c<=126) && (c!='/') && (c!='%')) { - *out++ = c; - count++; - } else { - *out++ = '%'; - *out++ = hex[(c>>4) & 0xf]; - *out++ = hex[c & 0xf]; - count += 3; - } - } - return count; -} - -/* - * hfs_colon2mac() - * - * Given an ASCII string (not null-terminated) and its length, - * generate the corresponding filename in the Macintosh character set - * using the 'CAP' name-mangling scheme, returning the length of the - * mangled filename. Note that the output string is not NULL - * terminated. - * - * This routine is a inverse to hfs_mac2cap() and hfs_mac2nat(). - * A ':' not followed by a 2-digit hexadecimal number (or followed - * by the codes for NULL or ':') is replaced by a '|'. - */ -void hfs_colon2mac(struct hfs_name *out, const char *in, int len) { - int hi, lo; - unsigned char code, c, *count; - unsigned char *p = out->Name; - - out->Len = 0; - count = &out->Len; - while (len-- && (*count < HFS_NAMELEN)) { - c = *in++; - (*count)++; - if (c!=':') { - *p++ = c; - } else if ((len<2) || - ((hi=dehex(in[0])) & 0xf0) || - ((lo=dehex(in[1])) & 0xf0) || - !(code = (hi << 4) | lo) || - (code == ':')) { - *p++ = '|'; - } else { - *p++ = code; - len -= 2; - in += 2; - } - } -} - -/* - * hfs_prcnt2mac() - * - * Given an ASCII string (not null-terminated) and its length, - * generate the corresponding filename in the Macintosh character set - * using Apple's three recommended name-mangling schemes, returning - * the length of the mangled filename. Note that the output string is - * not NULL terminated. - * - * This routine is a inverse to hfs_mac2alpha(), hfs_mac2seven() and - * hfs_mac2eight(). - * A '%' not followed by a 2-digit hexadecimal number (or followed - * by the code for NULL or ':') is unchanged. - * A ':' is replaced by a '|'. - */ -void hfs_prcnt2mac(struct hfs_name *out, const char *in, int len) { - int hi, lo; - unsigned char code, c, *count; - unsigned char *p = out->Name; - - out->Len = 0; - count = &out->Len; - while (len-- && (*count < HFS_NAMELEN)) { - c = *in++; - (*count)++; - if (c==':') { - *p++ = '|'; - } else if (c!='%') { - *p++ = c; - } else if ((len<2) || - ((hi=dehex(in[0])) & 0xf0) || - ((lo=dehex(in[1])) & 0xf0) || - !(code = (hi << 4) | lo) || - (code == ':')) { - *p++ = '%'; - } else { - *p++ = code; - len -= 2; - in += 2; - } + *out++ = c == '/' ? ':' : c; } + return i; } /* @@ -476,81 +54,19 @@ void hfs_prcnt2mac(struct hfs_name *out, const char *in, int len) { * This routine is a inverse to hfs_mac2triv(). * A ':' is replaced by a '/'. */ -void hfs_triv2mac(struct hfs_name *out, const char *in, int len) { - unsigned char c, *count; - unsigned char *p = out->Name; - - out->Len = 0; - count = &out->Len; - while (len-- && (*count < HFS_NAMELEN)) { - c = *in++; - (*count)++; - if (c==':') { - *p++ = '/'; - } else { - *p++ = c; - } - } -} - -/* - * hfs_latin2mac() - * - * Given an Latin-1 string (not null-terminated) and its length, - * generate the corresponding filename in the Macintosh character set - * using the 'Latin-1' name-mangling scheme, returning the length of - * the mangled filename. Note that the output string is not NULL - * terminated. - * - * This routine is a inverse to hfs_latin2cap(). - * A '%' not followed by a 2-digit hexadecimal number (or followed - * by the code for NULL or ':') is unchanged. - * A ':' is replaced by a '|'. - * - * Note that the character map is built the first time it is needed. - */ -void hfs_latin2mac(struct hfs_name *out, const char *in, int len) +void hfs_triv2mac(struct hfs_name *out, struct qstr *in) { - int hi, lo; - unsigned char code, c, *count; - unsigned char *p = out->Name; - static int map_initialized; - - if (!map_initialized) { - int i; - - /* build the inverse mapping at run time */ - for (i = 0; i < 128; i++) { - if ((c = mac2latin_map[i])) { - latin2mac_map[(int)c - 128] = i + 128; - } - } - map_initialized = 1; - } - - out->Len = 0; - count = &out->Len; - while (len-- && (*count < HFS_NAMELEN)) { - c = *in++; - (*count)++; - - if (c==':') { - *p++ = '|'; - } else if (c!='%') { - if (c<128 || !(*p = latin2mac_map[c-128])) { - *p = c; - } - p++; - } else if ((len<2) || - ((hi=dehex(in[0])) & 0xf0) || - ((lo=dehex(in[1])) & 0xf0) || - !(code = (hi << 4) | lo) || - (code == ':')) { - *p++ = '%'; - } else { - *p++ = code; - len -= 2; - in += 2; - } - } + const char *src; + char *dst, c; + int i, len; + + out->len = len = min((unsigned int)HFS_NAMELEN, in->len); + src = in->name; + dst = out->name; + for (i = 0; i < len; i++) { + c = *src++; + *dst++ = c == ':' ? '/' : c; + } + for (; i < HFS_NAMELEN; i++) + *dst++ = 0; } diff --git a/fs/hfs/version.c b/fs/hfs/version.c deleted file mode 100644 index ef0283ec0b6b..000000000000 --- a/fs/hfs/version.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * linux/fs/hfs/version.c - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains the version string for this release. - */ - -const char hfs_version[]="0.96"; diff --git a/fs/hfsplus/Makefile b/fs/hfsplus/Makefile new file mode 100644 index 000000000000..3cc0df730156 --- /dev/null +++ b/fs/hfsplus/Makefile @@ -0,0 +1,9 @@ +# +## Makefile for the linux hfsplus filesystem routines. +# + +obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o + +hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \ + bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o + diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c new file mode 100644 index 000000000000..d8305b7e1892 --- /dev/null +++ b/fs/hfsplus/bfind.c @@ -0,0 +1,209 @@ +/* + * linux/fs/hfsplus/bfind.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Search routines for btrees + */ + +#include <linux/slab.h> +#include "hfsplus_fs.h" + +int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) +{ + void *ptr; + + fd->tree = tree; + fd->bnode = NULL; + ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + fd->search_key = ptr; + fd->key = ptr + tree->max_key_len + 2; + dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", tree->cnid, __builtin_return_address(0)); + down(&tree->tree_lock); + return 0; +} + +void hfs_find_exit(struct hfs_find_data *fd) +{ + hfs_bnode_put(fd->bnode); + kfree(fd->search_key); + dprint(DBG_BNODE_REFS, "find_exit: %d (%p)\n", fd->tree->cnid, __builtin_return_address(0)); + up(&fd->tree->tree_lock); + fd->tree = NULL; +} + +/* Find the record in bnode that best matches key (not greater than...)*/ +int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd) +{ + int cmpval; + u16 off, len, keylen; + int rec; + int b, e; + int res; + + b = 0; + e = bnode->num_recs - 1; + res = -ENOENT; + do { + rec = (e + b) / 2; + len = hfs_brec_lenoff(bnode, rec, &off); + keylen = hfs_brec_keylen(bnode, rec); + hfs_bnode_read(bnode, fd->key, off, keylen); + cmpval = bnode->tree->keycmp(fd->key, fd->search_key); + if (!cmpval) { + e = rec; + res = 0; + goto done; + } + if (cmpval < 0) + b = rec + 1; + else + e = rec - 1; + } while (b <= e); + //printk("%d: %d,%d,%d\n", bnode->this, b, e, rec); + if (rec != e && e >= 0) { + len = hfs_brec_lenoff(bnode, e, &off); + keylen = hfs_brec_keylen(bnode, e); + hfs_bnode_read(bnode, fd->key, off, keylen); + } +done: + fd->record = e; + fd->keyoffset = off; + fd->keylength = keylen; + fd->entryoffset = off + keylen; + fd->entrylength = len - keylen; + return res; +} + +/* Traverse a B*Tree from the root to a leaf finding best fit to key */ +/* Return allocated copy of node found, set recnum to best record */ +int hfs_brec_find(struct hfs_find_data *fd) +{ + struct hfs_btree *tree; + struct hfs_bnode *bnode; + u32 data, nidx, parent; + int height, res; + + tree = fd->tree; + if (fd->bnode) + hfs_bnode_put(fd->bnode); + fd->bnode = NULL; + nidx = tree->root; + if (!nidx) + return -ENOENT; + height = tree->depth; + res = 0; + parent = 0; + for (;;) { + bnode = hfs_bnode_find(tree, nidx); + if (IS_ERR(bnode)) { + res = PTR_ERR(bnode); + bnode = NULL; + break; + } + if (bnode->height != height) + goto invalid; + if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF)) + goto invalid; + bnode->parent = parent; + + res = __hfs_brec_find(bnode, fd); + if (!height) + break; + if (fd->record < 0) + goto release; + + parent = nidx; + hfs_bnode_read(bnode, &data, fd->entryoffset, 4); + nidx = be32_to_cpu(data); + hfs_bnode_put(bnode); + } + fd->bnode = bnode; + return res; + +invalid: + printk("HFS+-fs: inconsistency in B*Tree (%d,%d,%d,%u,%u)\n", + height, bnode->height, bnode->type, nidx, parent); + res = -EIO; +release: + hfs_bnode_put(bnode); + return res; +} + +int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) +{ + int res; + + res = hfs_brec_find(fd); + if (res) + return res; + if (fd->entrylength > rec_len) + return -EINVAL; + hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength); + return 0; +} + +int hfs_brec_goto(struct hfs_find_data *fd, int cnt) +{ + struct hfs_btree *tree; + struct hfs_bnode *bnode; + int idx, res = 0; + u16 off, len, keylen; + + bnode = fd->bnode; + tree = bnode->tree; + + if (cnt < 0) { + cnt = -cnt; + while (cnt > fd->record) { + cnt -= fd->record + 1; + fd->record = bnode->num_recs - 1; + idx = bnode->prev; + if (!idx) { + res = -ENOENT; + goto out; + } + hfs_bnode_put(bnode); + bnode = hfs_bnode_find(tree, idx); + if (IS_ERR(bnode)) { + res = PTR_ERR(bnode); + bnode = NULL; + goto out; + } + } + fd->record -= cnt; + } else { + while (cnt >= bnode->num_recs - fd->record) { + cnt -= bnode->num_recs - fd->record; + fd->record = 0; + idx = bnode->next; + if (!idx) { + res = -ENOENT; + goto out; + } + hfs_bnode_put(bnode); + bnode = hfs_bnode_find(tree, idx); + if (IS_ERR(bnode)) { + res = PTR_ERR(bnode); + bnode = NULL; + goto out; + } + } + fd->record += cnt; + } + + len = hfs_brec_lenoff(bnode, fd->record, &off); + keylen = hfs_brec_keylen(bnode, fd->record); + fd->keyoffset = off; + fd->keylength = keylen; + fd->entryoffset = off + keylen; + fd->entrylength = len - keylen; + hfs_bnode_read(bnode, fd->key, off, keylen); +out: + fd->bnode = bnode; + return res; +} diff --git a/fs/hfsplus/bitmap.c b/fs/hfsplus/bitmap.c new file mode 100644 index 000000000000..644dcb51add3 --- /dev/null +++ b/fs/hfsplus/bitmap.c @@ -0,0 +1,220 @@ +/* + * linux/fs/hfsplus/bitmap.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handling of allocation file + */ + +#include <linux/pagemap.h> + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +#define PAGE_CACHE_BITS (PAGE_CACHE_SIZE * 8) + +int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, u32 *max) +{ + struct page *page; + struct address_space *mapping; + u32 *pptr, *curr, *end; + u32 val, mask, start, len; + int i; + + len = *max; + if (!len) + return size; + + dprint(DBG_BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len); + down(&HFSPLUS_SB(sb).alloc_file->i_sem); + mapping = HFSPLUS_SB(sb).alloc_file->i_mapping; + page = read_cache_page(mapping, offset / PAGE_CACHE_BITS, + (filler_t *)mapping->a_ops->readpage, NULL); + pptr = kmap(page); + curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32; + i = offset % 32; + offset &= ~(PAGE_CACHE_BITS - 1); + if ((size ^ offset) / PAGE_CACHE_BITS) + end = pptr + PAGE_CACHE_BITS / 32; + else + end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32; + + /* scan the first partial u32 for zero bits */ + val = *curr; + if (~val) { + val = be32_to_cpu(val); + mask = (1U << 31) >> i; + for (; i < 32; mask >>= 1, i++) { + if (!(val & mask)) + goto found; + } + } + curr++; + + /* scan complete u32s for the first zero bit */ + while (1) { + while (curr < end) { + val = *curr; + if (~val) { + val = be32_to_cpu(val); + mask = 1 << 31; + for (i = 0; i < 32; mask >>= 1, i++) { + if (!(val & mask)) + goto found; + } + } + curr++; + } + kunmap(page); + offset += PAGE_CACHE_BITS; + if (offset >= size) + break; + page = read_cache_page(mapping, offset / PAGE_CACHE_BITS, + (filler_t *)mapping->a_ops->readpage, NULL); + curr = pptr = kmap(page); + if ((size ^ offset) / PAGE_CACHE_BITS) + end = pptr + PAGE_CACHE_BITS / 32; + else + end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32; + } + dprint(DBG_BITMAP, "bitmap full\n"); + start = size; + goto out; + +found: + start = offset + (curr - pptr) * 32 + i; + if (start >= size) { + dprint(DBG_BITMAP, "bitmap full\n"); + goto out; + } + /* do any partial u32 at the start */ + len = min(size - start, len); + while (1) { + val |= mask; + if (++i >= 32) + break; + mask >>= 1; + if (!--len || val & mask) + goto done; + } + if (!--len) + goto done; + *curr++ = cpu_to_be32(val); + /* do full u32s */ + while (1) { + while (curr < end) { + val = be32_to_cpu(*curr); + if (len < 32) + goto last; + if (val) { + len = 32; + goto last; + } + *curr++ = 0xffffffffU; + len -= 32; + } + set_page_dirty(page); + kunmap(page); + offset += PAGE_CACHE_BITS; + page = read_cache_page(mapping, offset / PAGE_CACHE_BITS, + (filler_t *)mapping->a_ops->readpage, NULL); + pptr = kmap(page); + curr = pptr; + end = pptr + PAGE_CACHE_BITS / 32; + } +last: + /* do any partial u32 at end */ + mask = 1U << 31; + for (i = 0; i < len; i++) { + if (val & mask) + break; + val |= mask; + mask >>= 1; + } +done: + *curr = cpu_to_be32(val); + set_page_dirty(page); + kunmap(page); + *max = offset + (curr - pptr) * 32 + i - start; + HFSPLUS_SB(sb).free_blocks -= *max; + sb->s_dirt = 1; + dprint(DBG_BITMAP, "-> %u,%u\n", start, *max); +out: + up(&HFSPLUS_SB(sb).alloc_file->i_sem); + return start; +} + +int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count) +{ + struct page *page; + struct address_space *mapping; + u32 *pptr, *curr, *end; + u32 mask, len, pnr; + int i; + + /* is there any actual work to be done? */ + if (!count) + return 0; + + dprint(DBG_BITMAP, "block_free: %u,%u\n", offset, count); + /* are all of the bits in range? */ + if ((offset + count) > HFSPLUS_SB(sb).total_blocks) + return -2; + + down(&HFSPLUS_SB(sb).alloc_file->i_sem); + mapping = HFSPLUS_SB(sb).alloc_file->i_mapping; + pnr = offset / PAGE_CACHE_BITS; + page = read_cache_page(mapping, pnr, (filler_t *)mapping->a_ops->readpage, NULL); + pptr = kmap(page); + curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32; + end = pptr + PAGE_CACHE_BITS / 32; + len = count; + + /* do any partial u32 at the start */ + i = offset % 32; + if (i) { + int j = 32 - i; + mask = 0xffffffffU << j; + if (j > count) { + mask |= 0xffffffffU >> (i + count); + *curr++ &= cpu_to_be32(mask); + goto out; + } + *curr++ &= cpu_to_be32(mask); + count -= j; + } + + /* do full u32s */ + while (1) { + while (curr < end) { + if (count < 32) + goto done; + *curr++ = 0; + count -= 32; + } + if (!count) + break; + set_page_dirty(page); + kunmap(page); + page = read_cache_page(mapping, ++pnr, (filler_t *)mapping->a_ops->readpage, NULL); + pptr = kmap(page); + curr = pptr; + end = pptr + PAGE_CACHE_BITS / 32; + } +done: + /* do any partial u32 at end */ + if (count) { + mask = 0xffffffffU >> count; + *curr &= cpu_to_be32(mask); + } +out: + set_page_dirty(page); + kunmap(page); + HFSPLUS_SB(sb).free_blocks += len; + sb->s_dirt = 1; + up(&HFSPLUS_SB(sb).alloc_file->i_sem); + + return 0; +} diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c new file mode 100644 index 000000000000..ec8cb5dd28c2 --- /dev/null +++ b/fs/hfsplus/bnode.c @@ -0,0 +1,661 @@ +/* + * linux/fs/hfsplus/bnode.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handle basic btree node operations + */ + +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/pagemap.h> +#include <linux/fs.h> +#include <linux/swap.h> +#include <linux/version.h> + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +#define REF_PAGES 0 + +/* Copy a specified range of bytes from the raw data of a node */ +void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) +{ + struct page **pagep; + int l; + + off += node->page_offset; + pagep = node->page + (off >> PAGE_CACHE_SHIFT); + off &= ~PAGE_CACHE_MASK; + + l = min(len, (int)PAGE_CACHE_SIZE - off); + memcpy(buf, kmap(*pagep) + off, l); + kunmap(*pagep); + + while ((len -= l)) { + buf += l; + l = min(len, (int)PAGE_CACHE_SIZE); + memcpy(buf, kmap(*++pagep), l); + kunmap(*pagep); + } +} + +u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) +{ + u16 data; + // optimize later... + hfs_bnode_read(node, &data, off, 2); + return be16_to_cpu(data); +} + +u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) +{ + u8 data; + // optimize later... + hfs_bnode_read(node, &data, off, 1); + return be16_to_cpu(data); +} + +void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) +{ + struct hfs_btree *tree; + int key_len; + + tree = node->tree; + if (node->type == HFS_NODE_LEAF || + tree->attributes & HFS_TREE_VARIDXKEYS) + key_len = hfs_bnode_read_u16(node, off) + 2; + else + key_len = tree->max_key_len + 2; + + hfs_bnode_read(node, key, off, key_len); +} + +void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) +{ + struct page **pagep; + int l; + + off += node->page_offset; + pagep = node->page + (off >> PAGE_CACHE_SHIFT); + off &= ~PAGE_CACHE_MASK; + + l = min(len, (int)PAGE_CACHE_SIZE - off); + memcpy(kmap(*pagep) + off, buf, l); + set_page_dirty(*pagep); + kunmap(*pagep); + + while ((len -= l)) { + buf += l; + l = min(len, (int)PAGE_CACHE_SIZE); + memcpy(kmap(*++pagep), buf, l); + set_page_dirty(*pagep); + kunmap(*pagep); + } +} + +void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data) +{ + data = cpu_to_be16(data); + // optimize later... + hfs_bnode_write(node, &data, off, 2); +} + +void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) +{ + struct page **pagep; + int l; + + off += node->page_offset; + pagep = node->page + (off >> PAGE_CACHE_SHIFT); + off &= ~PAGE_CACHE_MASK; + + l = min(len, (int)PAGE_CACHE_SIZE - off); + memset(kmap(*pagep) + off, 0, l); + set_page_dirty(*pagep); + kunmap(*pagep); + + while ((len -= l)) { + l = min(len, (int)PAGE_CACHE_SIZE); + memset(kmap(*++pagep), 0, l); + set_page_dirty(*pagep); + kunmap(*pagep); + } +} + +void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, + struct hfs_bnode *src_node, int src, int len) +{ + struct hfs_btree *tree; + struct page **src_page, **dst_page; + int l; + + dprint(DBG_BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len); + if (!len) + return; + tree = src_node->tree; + src += src_node->page_offset; + dst += dst_node->page_offset; + src_page = src_node->page + (src >> PAGE_CACHE_SHIFT); + src &= ~PAGE_CACHE_MASK; + dst_page = dst_node->page + (dst >> PAGE_CACHE_SHIFT); + dst &= ~PAGE_CACHE_MASK; + + if (src == dst) { + l = min(len, (int)PAGE_CACHE_SIZE - src); + memcpy(kmap(*dst_page) + src, kmap(*src_page) + src, l); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + + while ((len -= l)) { + l = min(len, (int)PAGE_CACHE_SIZE); + memcpy(kmap(*++dst_page), kmap(*++src_page), l); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + } + } else { + void *src_ptr, *dst_ptr; + + do { + src_ptr = kmap(*src_page) + src; + dst_ptr = kmap(*dst_page) + dst; + if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) { + l = PAGE_CACHE_SIZE - src; + src = 0; + dst += l; + } else { + l = PAGE_CACHE_SIZE - dst; + src += l; + dst = 0; + } + l = min(len, l); + memcpy(dst_ptr, src_ptr, l); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + if (!dst) + dst_page++; + else + src_page++; + } while ((len -= l)); + } +} + +void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) +{ + struct page **src_page, **dst_page; + int l; + + dprint(DBG_BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len); + if (!len) + return; + src += node->page_offset; + dst += node->page_offset; + if (dst > src) { + src += len - 1; + src_page = node->page + (src >> PAGE_CACHE_SHIFT); + src = (src & ~PAGE_CACHE_MASK) + 1; + dst += len - 1; + dst_page = node->page + (dst >> PAGE_CACHE_SHIFT); + dst = (dst & ~PAGE_CACHE_MASK) + 1; + + if (src == dst) { + while (src < len) { + memmove(kmap(*dst_page), kmap(*src_page), src); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + len -= src; + src = PAGE_CACHE_SIZE; + src_page--; + dst_page--; + } + src -= len; + memmove(kmap(*dst_page) + src, kmap(*src_page) + src, len); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + } else { + void *src_ptr, *dst_ptr; + + do { + src_ptr = kmap(*src_page) + src; + dst_ptr = kmap(*dst_page) + dst; + if (src < dst) { + l = src; + src = PAGE_CACHE_SIZE; + dst -= l; + } else { + l = dst; + src -= l; + dst = PAGE_CACHE_SIZE; + } + l = min(len, l); + memmove(dst_ptr - l, src_ptr - l, l); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + if (dst == PAGE_CACHE_SIZE) + dst_page--; + else + src_page--; + } while ((len -= l)); + } + } else { + src_page = node->page + (src >> PAGE_CACHE_SHIFT); + src &= ~PAGE_CACHE_MASK; + dst_page = node->page + (dst >> PAGE_CACHE_SHIFT); + dst &= ~PAGE_CACHE_MASK; + + if (src == dst) { + l = min(len, (int)PAGE_CACHE_SIZE - src); + memmove(kmap(*dst_page) + src, kmap(*src_page) + src, l); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + + while ((len -= l)) { + l = min(len, (int)PAGE_CACHE_SIZE); + memmove(kmap(*++dst_page), kmap(*++src_page), l); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + } + } else { + void *src_ptr, *dst_ptr; + + do { + src_ptr = kmap(*src_page) + src; + dst_ptr = kmap(*dst_page) + dst; + if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) { + l = PAGE_CACHE_SIZE - src; + src = 0; + dst += l; + } else { + l = PAGE_CACHE_SIZE - dst; + src += l; + dst = 0; + } + l = min(len, l); + memmove(dst_ptr, src_ptr, l); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + if (!dst) + dst_page++; + else + src_page++; + } while ((len -= l)); + } + } +} + +void hfs_bnode_dump(struct hfs_bnode *node) +{ + struct hfs_bnode_desc desc; + u32 cnid; + int i, off, key_off; + + dprint(DBG_BNODE_MOD, "bnode: %d\n", node->this); + hfs_bnode_read(node, &desc, 0, sizeof(desc)); + dprint(DBG_BNODE_MOD, "%d, %d, %d, %d, %d\n", + be32_to_cpu(desc.next), be32_to_cpu(desc.prev), + desc.type, desc.height, be16_to_cpu(desc.num_recs)); + + off = node->tree->node_size - 2; + for (i = be16_to_cpu(desc.num_recs); i >= 0; off -= 2, i--) { + key_off = hfs_bnode_read_u16(node, off); + dprint(DBG_BNODE_MOD, " %d", key_off); + if (i && node->type == HFS_NODE_INDEX) { + int tmp; + + if (node->tree->attributes & HFS_TREE_VARIDXKEYS) + tmp = hfs_bnode_read_u16(node, key_off) + 2; + else + tmp = node->tree->max_key_len + 2; + dprint(DBG_BNODE_MOD, " (%d", tmp); + hfs_bnode_read(node, &cnid, key_off + tmp, 4); + dprint(DBG_BNODE_MOD, ",%d)", be32_to_cpu(cnid)); + } else if (i && node->type == HFS_NODE_LEAF) { + int tmp; + + tmp = hfs_bnode_read_u16(node, key_off); + dprint(DBG_BNODE_MOD, " (%d)", tmp); + } + } + dprint(DBG_BNODE_MOD, "\n"); +} + +void hfs_bnode_unlink(struct hfs_bnode *node) +{ + struct hfs_btree *tree; + struct hfs_bnode *tmp; + u32 cnid; + + tree = node->tree; + if (node->prev) { + tmp = hfs_bnode_find(tree, node->prev); + if (IS_ERR(tmp)) + return; + tmp->next = node->next; + cnid = cpu_to_be32(tmp->next); + hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, next), 4); + hfs_bnode_put(tmp); + } else if (node->type == HFS_NODE_LEAF) + tree->leaf_head = node->next; + + if (node->next) { + tmp = hfs_bnode_find(tree, node->next); + if (IS_ERR(tmp)) + return; + tmp->prev = node->prev; + cnid = cpu_to_be32(tmp->prev); + hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, prev), 4); + hfs_bnode_put(tmp); + } else if (node->type == HFS_NODE_LEAF) + tree->leaf_tail = node->prev; + + // move down? + if (!node->prev && !node->next) { + printk("hfs_btree_del_level\n"); + } + if (!node->parent) { + tree->root = 0; + tree->depth = 0; + } + set_bit(HFS_BNODE_DELETED, &node->flags); +} + +static inline int hfs_bnode_hash(u32 num) +{ + num = (num >> 16) + num; + num += num >> 8; + return num & (NODE_HASH_SIZE - 1); +} + +struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid) +{ + struct hfs_bnode *node; + + if (cnid >= tree->node_count) { + printk("HFS+-fs: request for non-existent node %d in B*Tree\n", cnid); + return NULL; + } + + for (node = tree->node_hash[hfs_bnode_hash(cnid)]; + node; node = node->next_hash) { + if (node->this == cnid) { + return node; + } + } + return NULL; +} + +static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) +{ + struct super_block *sb; + struct hfs_bnode *node, *node2; + struct address_space *mapping; + struct page *page; + int size, block, i, hash; + loff_t off; + + if (cnid >= tree->node_count) { + printk("HFS+-fs: request for non-existent node %d in B*Tree\n", cnid); + return NULL; + } + + sb = tree->inode->i_sb; + size = sizeof(struct hfs_bnode) + tree->pages_per_bnode * + sizeof(struct page *); + node = kmalloc(size, GFP_KERNEL); + if (!node) + return NULL; + memset(node, 0, size); + node->tree = tree; + node->this = cnid; + set_bit(HFS_BNODE_NEW, &node->flags); + atomic_set(&node->refcnt, 1); + dprint(DBG_BNODE_REFS, "new_node(%d:%d): 1\n", + node->tree->cnid, node->this); + init_waitqueue_head(&node->lock_wq); + spin_lock(&tree->hash_lock); + node2 = hfs_bnode_findhash(tree, cnid); + if (!node2) { + hash = hfs_bnode_hash(cnid); + node->next_hash = tree->node_hash[hash]; + tree->node_hash[hash] = node; + tree->node_hash_cnt++; + } else { + spin_unlock(&tree->hash_lock); + kfree(node); + wait_event(node2->lock_wq, !test_bit(HFS_BNODE_NEW, &node2->flags)); + return node2; + } + spin_unlock(&tree->hash_lock); + + mapping = tree->inode->i_mapping; + off = (loff_t)cnid << tree->node_size_shift; + block = off >> PAGE_CACHE_SHIFT; + node->page_offset = off & ~PAGE_CACHE_MASK; + for (i = 0; i < tree->pages_per_bnode; block++, i++) { + page = read_cache_page(mapping, block, (filler_t *)mapping->a_ops->readpage, NULL); + if (IS_ERR(page)) + goto fail; +#if !REF_PAGES + page_cache_release(page); +#endif + node->page[i] = page; + } + + return node; +fail: + set_bit(HFS_BNODE_ERROR, &node->flags); + return node; +} + +void hfs_bnode_unhash(struct hfs_bnode *node) +{ + struct hfs_bnode **p; + + dprint(DBG_BNODE_REFS, "remove_node(%d:%d): %d\n", + node->tree->cnid, node->this, atomic_read(&node->refcnt)); + for (p = &node->tree->node_hash[hfs_bnode_hash(node->this)]; + *p && *p != node; p = &(*p)->next_hash) + ; + if (!*p) + BUG(); + *p = node->next_hash; + node->tree->node_hash_cnt--; +} + +/* Load a particular node out of a tree */ +struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num) +{ + struct hfs_bnode *node; + struct hfs_bnode_desc *desc; + int i, rec_off, off, next_off; + int entry_size, key_size; + + spin_lock(&tree->hash_lock); + node = hfs_bnode_findhash(tree, num); + if (node) { + hfs_bnode_get(node); + spin_unlock(&tree->hash_lock); + wait_event(node->lock_wq, !test_bit(HFS_BNODE_NEW, &node->flags)); + return node; + } + spin_unlock(&tree->hash_lock); + node = __hfs_bnode_create(tree, num); + if (!node) + return ERR_PTR(-ENOMEM); + if (!test_bit(HFS_BNODE_NEW, &node->flags)) + return node; + + desc = (struct hfs_bnode_desc *)(kmap(node->page[0]) + node->page_offset); + node->prev = be32_to_cpu(desc->prev); + node->next = be32_to_cpu(desc->next); + node->num_recs = be16_to_cpu(desc->num_recs); + node->type = desc->type; + node->height = desc->height; + kunmap(node->page[0]); + + switch (node->type) { + case HFS_NODE_HEADER: + case HFS_NODE_MAP: + if (node->height != 0) + goto node_error; + break; + case HFS_NODE_LEAF: + if (node->height != 1) + goto node_error; + break; + case HFS_NODE_INDEX: + if (node->height <= 1 || node->height > tree->depth) + goto node_error; + break; + default: + goto node_error; + } + + rec_off = tree->node_size - 2; + off = hfs_bnode_read_u16(node, rec_off); + if (off != sizeof(struct hfs_bnode_desc)) + goto node_error; + for (i = 1; i <= node->num_recs; off = next_off, i++) { + rec_off -= 2; + next_off = hfs_bnode_read_u16(node, rec_off); + if (next_off <= off || + next_off > tree->node_size || + next_off & 1) + goto node_error; + entry_size = next_off - off; + if (node->type != HFS_NODE_INDEX && + node->type != HFS_NODE_LEAF) + continue; + key_size = hfs_bnode_read_u16(node, off) + 2; + if (key_size >= entry_size || key_size & 1) + goto node_error; + } + clear_bit(HFS_BNODE_NEW, &node->flags); + wake_up(&node->lock_wq); + return node; + +node_error: + set_bit(HFS_BNODE_ERROR, &node->flags); + clear_bit(HFS_BNODE_NEW, &node->flags); + wake_up(&node->lock_wq); + hfs_bnode_put(node); + return ERR_PTR(-EIO); +} + +void hfs_bnode_free(struct hfs_bnode *node) +{ + //int i; + + //for (i = 0; i < node->tree->pages_per_bnode; i++) + // if (node->page[i]) + // page_cache_release(node->page[i]); + kfree(node); +} + +struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num) +{ + struct hfs_bnode *node; + struct page **pagep; + int i; + + spin_lock(&tree->hash_lock); + node = hfs_bnode_findhash(tree, num); + spin_unlock(&tree->hash_lock); + if (node) { + printk("new node %u already hashed?\n", num); + BUG(); + } + node = __hfs_bnode_create(tree, num); + if (!node) + return ERR_PTR(-ENOMEM); + + pagep = node->page; + memset(kmap(*pagep) + node->page_offset, 0, + min((int)PAGE_CACHE_SIZE, (int)tree->node_size)); + set_page_dirty(*pagep); + kunmap(*pagep); + for (i = 1; i < tree->pages_per_bnode; i++) { + memset(kmap(*++pagep), 0, PAGE_CACHE_SIZE); + set_page_dirty(*pagep); + kunmap(*pagep); + } + clear_bit(HFS_BNODE_NEW, &node->flags); + wake_up(&node->lock_wq); + + return node; +} + +void hfs_bnode_get(struct hfs_bnode *node) +{ + if (node) { + atomic_inc(&node->refcnt); +#if REF_PAGES + { + int i; + for (i = 0; i < node->tree->pages_per_bnode; i++) + get_page(node->page[i]); + } +#endif + dprint(DBG_BNODE_REFS, "get_node(%d:%d): %d\n", + node->tree->cnid, node->this, atomic_read(&node->refcnt)); + } +} + +/* Dispose of resources used by a node */ +void hfs_bnode_put(struct hfs_bnode *node) +{ + if (node) { + struct hfs_btree *tree = node->tree; + int i; + + dprint(DBG_BNODE_REFS, "put_node(%d:%d): %d\n", + node->tree->cnid, node->this, atomic_read(&node->refcnt)); + if (!atomic_read(&node->refcnt)) + BUG(); + if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock)) { +#if REF_PAGES + for (i = 0; i < tree->pages_per_bnode; i++) + put_page(node->page[i]); +#endif + return; + } + for (i = 0; i < tree->pages_per_bnode; i++) { + mark_page_accessed(node->page[i]); +#if REF_PAGES + put_page(node->page[i]); +#endif + } + + if (test_bit(HFS_BNODE_DELETED, &node->flags)) { + hfs_bnode_unhash(node); + spin_unlock(&tree->hash_lock); + hfs_bmap_free(node); + hfs_bnode_free(node); + return; + } + spin_unlock(&tree->hash_lock); + } +} + +void hfsplus_lock_bnode(struct hfs_bnode *node) +{ + wait_event(node->lock_wq, !test_and_set_bit(HFS_BNODE_LOCK, &node->flags)); +} + +void hfsplus_unlock_bnode(struct hfs_bnode *node) +{ + clear_bit(HFS_BNODE_LOCK, &node->flags); + if (waitqueue_active(&node->lock_wq)) + wake_up(&node->lock_wq); +} diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c new file mode 100644 index 000000000000..2d1ff31b140b --- /dev/null +++ b/fs/hfsplus/brec.c @@ -0,0 +1,487 @@ +/* + * linux/fs/hfsplus/brec.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handle individual btree records + */ + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +/* Get the length and offset of the given record in the given node */ +u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off) +{ + u16 retval[2]; + u16 dataoff; + + dataoff = node->tree->node_size - (rec + 2) * 2; + hfs_bnode_read(node, retval, dataoff, 4); + *off = be16_to_cpu(retval[1]); + return be16_to_cpu(retval[0]) - *off; +} + +/* Get the length of the key from a keyed record */ +u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) +{ + u16 retval, recoff; + + if (node->type != HFS_NODE_INDEX && node->type != HFS_NODE_LEAF) + return 0; + + if ((node->type == HFS_NODE_INDEX) && + !(node->tree->attributes & HFS_TREE_VARIDXKEYS)) { + retval = node->tree->max_key_len; + } else { + recoff = hfs_bnode_read_u16(node, node->tree->node_size - (rec + 1) * 2); + if (!recoff) + return 0; + if (node->tree->attributes & HFS_TREE_BIGKEYS) + retval = hfs_bnode_read_u16(node, recoff) + 2; + else + retval = (hfs_bnode_read_u8(node, recoff) | 1) + 1; + } + return retval; +} + +int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) +{ + struct hfs_btree *tree; + struct hfs_bnode *node, *new_node; + int size, key_len, rec; + int data_off, end_off; + int idx_rec_off, data_rec_off, end_rec_off; + u32 cnid; + + tree = fd->tree; + if (!fd->bnode) { + if (!tree->root) + hfs_btree_inc_height(tree); + fd->bnode = hfs_bnode_find(tree, tree->leaf_head); + if (IS_ERR(fd->bnode)) + return PTR_ERR(fd->bnode); + fd->record = -1; + } + new_node = NULL; + key_len = be16_to_cpu(fd->search_key->key_len) + 2; +again: + /* new record idx and complete record size */ + rec = fd->record + 1; + size = key_len + entry_len; + + node = fd->bnode; + hfs_bnode_dump(node); + /* get last offset */ + end_rec_off = tree->node_size - (node->num_recs + 1) * 2; + end_off = hfs_bnode_read_u16(node, end_rec_off); + end_rec_off -= 2; + dprint(DBG_BNODE_MOD, "insert_rec: %d, %d, %d, %d\n", rec, size, end_off, end_rec_off); + if (size > end_rec_off - end_off) { + if (new_node) + panic("not enough room!\n"); + new_node = hfs_bnode_split(fd); + if (IS_ERR(new_node)) + return PTR_ERR(new_node); + goto again; + } + if (node->type == HFS_NODE_LEAF) { + tree->leaf_count++; + mark_inode_dirty(tree->inode); + } + node->num_recs++; + /* write new last offset */ + hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs); + hfs_bnode_write_u16(node, end_rec_off, end_off + size); + data_off = end_off; + data_rec_off = end_rec_off + 2; + idx_rec_off = tree->node_size - (rec + 1) * 2; + if (idx_rec_off == data_rec_off) + goto skip; + /* move all following entries */ + do { + data_off = hfs_bnode_read_u16(node, data_rec_off + 2); + hfs_bnode_write_u16(node, data_rec_off, data_off + size); + data_rec_off += 2; + } while (data_rec_off < idx_rec_off); + + /* move data away */ + hfs_bnode_move(node, data_off + size, data_off, + end_off - data_off); + +skip: + hfs_bnode_write(node, fd->search_key, data_off, key_len); + hfs_bnode_write(node, entry, data_off + key_len, entry_len); + hfs_bnode_dump(node); + + if (new_node) { + /* update parent key if we inserted a key + * at the start of the first node + */ + if (!rec && new_node != node) + hfs_brec_update_parent(fd); + + hfs_bnode_put(fd->bnode); + if (!new_node->parent) { + hfs_btree_inc_height(tree); + new_node->parent = tree->root; + } + fd->bnode = hfs_bnode_find(tree, new_node->parent); + + /* create index data entry */ + cnid = cpu_to_be32(new_node->this); + entry = &cnid; + entry_len = sizeof(cnid); + + /* get index key */ + hfs_bnode_read_key(new_node, fd->search_key, 14); + __hfs_brec_find(fd->bnode, fd); + + hfs_bnode_put(new_node); + new_node = NULL; + + if (tree->attributes & HFS_TREE_VARIDXKEYS) + key_len = be16_to_cpu(fd->search_key->key_len) + 2; + else { + fd->search_key->key_len = tree->max_key_len; + key_len = tree->max_key_len + 2; + } + goto again; + } + + if (!rec) + hfs_brec_update_parent(fd); + + return 0; +} + +int hfs_brec_remove(struct hfs_find_data *fd) +{ + struct hfs_btree *tree; + struct hfs_bnode *node, *parent; + int end_off, rec_off, data_off, size; + + tree = fd->tree; + node = fd->bnode; +again: + rec_off = tree->node_size - (fd->record + 2) * 2; + end_off = tree->node_size - (node->num_recs + 1) * 2; + + if (node->type == HFS_NODE_LEAF) { + tree->leaf_count--; + mark_inode_dirty(tree->inode); + } + hfs_bnode_dump(node); + dprint(DBG_BNODE_MOD, "remove_rec: %d, %d\n", fd->record, fd->keylength + fd->entrylength); + if (!--node->num_recs) { + hfs_bnode_unlink(node); + if (!node->parent) + return 0; + parent = hfs_bnode_find(tree, node->parent); + if (IS_ERR(parent)) + return PTR_ERR(parent); + hfs_bnode_put(node); + node = fd->bnode = parent; + + __hfs_brec_find(node, fd); + goto again; + } + hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs); + + if (rec_off == end_off) + goto skip; + size = fd->keylength + fd->entrylength; + + do { + data_off = hfs_bnode_read_u16(node, rec_off); + hfs_bnode_write_u16(node, rec_off + 2, data_off - size); + rec_off -= 2; + } while (rec_off >= end_off); + + /* fill hole */ + hfs_bnode_move(node, fd->keyoffset, fd->keyoffset + size, + data_off - fd->keyoffset - size); +skip: + hfs_bnode_dump(node); + if (!fd->record) + hfs_brec_update_parent(fd); + return 0; +} + +struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd) +{ + struct hfs_btree *tree; + struct hfs_bnode *node, *new_node; + struct hfs_bnode_desc node_desc; + int num_recs, new_rec_off, new_off, old_rec_off; + int data_start, data_end, size; + + tree = fd->tree; + node = fd->bnode; + new_node = hfs_bmap_alloc(tree); + if (IS_ERR(new_node)) + return new_node; + hfs_bnode_get(node); + dprint(DBG_BNODE_MOD, "split_nodes: %d - %d - %d\n", + node->this, new_node->this, node->next); + new_node->next = node->next; + new_node->prev = node->this; + new_node->parent = node->parent; + new_node->type = node->type; + new_node->height = node->height; + + size = tree->node_size / 2 - node->num_recs * 2 - 14; + old_rec_off = tree->node_size - 4; + num_recs = 1; + for (;;) { + data_start = hfs_bnode_read_u16(node, old_rec_off); + if (data_start > size) + break; + old_rec_off -= 2; + if (++num_recs < node->num_recs) + continue; + /* panic? */ + hfs_bnode_put(node); + hfs_bnode_put(new_node); + return ERR_PTR(-ENOSPC); + } + + if (fd->record + 1 < num_recs) { + /* new record is in the lower half, + * so leave some more space there + */ + old_rec_off += 2; + num_recs--; + data_start = hfs_bnode_read_u16(node, old_rec_off); + } else { + hfs_bnode_put(node); + hfs_bnode_get(new_node); + fd->bnode = new_node; + fd->record -= num_recs; + fd->keyoffset -= data_start - 14; + fd->entryoffset -= data_start - 14; + } + new_node->num_recs = node->num_recs - num_recs; + node->num_recs = num_recs; + + new_rec_off = tree->node_size - 2; + new_off = 14; + size = data_start - new_off; + num_recs = new_node->num_recs; + data_end = data_start; + while (num_recs) { + hfs_bnode_write_u16(new_node, new_rec_off, new_off); + old_rec_off -= 2; + new_rec_off -= 2; + data_end = hfs_bnode_read_u16(node, old_rec_off); + new_off = data_end - size; + num_recs--; + } + hfs_bnode_write_u16(new_node, new_rec_off, new_off); + hfs_bnode_copy(new_node, 14, node, data_start, data_end - data_start); + + /* update new bnode header */ + node_desc.next = cpu_to_be32(new_node->next); + node_desc.prev = cpu_to_be32(new_node->prev); + node_desc.type = new_node->type; + node_desc.height = new_node->height; + node_desc.num_recs = cpu_to_be16(new_node->num_recs); + node_desc.reserved = 0; + hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc)); + + /* update previous bnode header */ + node->next = new_node->this; + hfs_bnode_read(node, &node_desc, 0, sizeof(node_desc)); + node_desc.next = cpu_to_be32(node->next); + node_desc.num_recs = cpu_to_be16(node->num_recs); + hfs_bnode_write(node, &node_desc, 0, sizeof(node_desc)); + + /* update next bnode header */ + if (new_node->next) { + struct hfs_bnode *next_node = hfs_bnode_find(tree, new_node->next); + next_node->prev = new_node->this; + hfs_bnode_read(next_node, &node_desc, 0, sizeof(node_desc)); + node_desc.prev = cpu_to_be32(next_node->prev); + hfs_bnode_write(next_node, &node_desc, 0, sizeof(node_desc)); + hfs_bnode_put(next_node); + } else if (node->this == tree->leaf_tail) { + /* if there is no next node, this might be the new tail */ + tree->leaf_tail = new_node->this; + mark_inode_dirty(tree->inode); + } + + hfs_bnode_dump(node); + hfs_bnode_dump(new_node); + hfs_bnode_put(node); + + return new_node; +} + +int hfs_brec_update_parent(struct hfs_find_data *fd) +{ + struct hfs_btree *tree; + struct hfs_bnode *node, *new_node, *parent; + int newkeylen, diff; + int rec, rec_off, end_rec_off; + int start_off, end_off; + + tree = fd->tree; + node = fd->bnode; + new_node = NULL; + if (!node->parent) + return 0; + +again: + parent = hfs_bnode_find(tree, node->parent); + if (IS_ERR(parent)) + return PTR_ERR(parent); + __hfs_brec_find(parent, fd); + hfs_bnode_dump(parent); + rec = fd->record; + + /* size difference between old and new key */ + if (tree->attributes & HFS_TREE_VARIDXKEYS) + newkeylen = hfs_bnode_read_u16(node, 14) + 2; + else + fd->keylength = newkeylen = tree->max_key_len + 2; + dprint(DBG_BNODE_MOD, "update_rec: %d, %d, %d\n", rec, fd->keylength, newkeylen); + + rec_off = tree->node_size - (rec + 2) * 2; + end_rec_off = tree->node_size - (parent->num_recs + 1) * 2; + diff = newkeylen - fd->keylength; + if (!diff) + goto skip; + if (diff > 0) { + end_off = hfs_bnode_read_u16(parent, end_rec_off); + if (end_rec_off - end_off < diff) { + + printk("splitting index node...\n"); + fd->bnode = parent; + new_node = hfs_bnode_split(fd); + if (IS_ERR(new_node)) + return PTR_ERR(new_node); + parent = fd->bnode; + rec = fd->record; + rec_off = tree->node_size - (rec + 2) * 2; + end_rec_off = tree->node_size - (parent->num_recs + 1) * 2; + } + } + + end_off = start_off = hfs_bnode_read_u16(parent, rec_off); + hfs_bnode_write_u16(parent, rec_off, start_off + diff); + start_off -= 4; /* move previous cnid too */ + + while (rec_off > end_rec_off) { + rec_off -= 2; + end_off = hfs_bnode_read_u16(parent, rec_off); + hfs_bnode_write_u16(parent, rec_off, end_off + diff); + } + hfs_bnode_move(parent, start_off + diff, start_off, + end_off - start_off); +skip: + hfs_bnode_copy(parent, fd->keyoffset, node, 14, newkeylen); + hfs_bnode_dump(parent); + + hfs_bnode_put(node); + node = parent; + + if (new_node) { + u32 cnid; + + fd->bnode = hfs_bnode_find(tree, new_node->parent); + /* create index key and entry */ + hfs_bnode_read_key(new_node, fd->search_key, 14); + cnid = cpu_to_be32(new_node->this); + + __hfs_brec_find(fd->bnode, fd); + hfs_brec_insert(fd, &cnid, sizeof(cnid)); + hfs_bnode_put(fd->bnode); + hfs_bnode_put(new_node); + + if (!rec) { + if (new_node == node) + goto out; + /* restore search_key */ + hfs_bnode_read_key(node, fd->search_key, 14); + } + } + + if (!rec && node->parent) + goto again; +out: + fd->bnode = node; + return 0; +} + +int hfs_btree_inc_height(struct hfs_btree *tree) +{ + struct hfs_bnode *node, *new_node; + struct hfs_bnode_desc node_desc; + int key_size, rec; + u32 cnid; + + node = NULL; + if (tree->root) { + node = hfs_bnode_find(tree, tree->root); + if (IS_ERR(node)) + return PTR_ERR(node); + } + new_node = hfs_bmap_alloc(tree); + if (IS_ERR(new_node)) { + hfs_bnode_put(node); + return PTR_ERR(new_node); + } + + tree->root = new_node->this; + if (!tree->depth) { + tree->leaf_head = tree->leaf_tail = new_node->this; + new_node->type = HFS_NODE_LEAF; + new_node->num_recs = 0; + } else { + new_node->type = HFS_NODE_INDEX; + new_node->num_recs = 1; + } + new_node->parent = 0; + new_node->next = 0; + new_node->prev = 0; + new_node->height = ++tree->depth; + + node_desc.next = cpu_to_be32(new_node->next); + node_desc.prev = cpu_to_be32(new_node->prev); + node_desc.type = new_node->type; + node_desc.height = new_node->height; + node_desc.num_recs = cpu_to_be16(new_node->num_recs); + node_desc.reserved = 0; + hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc)); + + rec = tree->node_size - 2; + hfs_bnode_write_u16(new_node, rec, 14); + + if (node) { + /* insert old root idx into new root */ + node->parent = tree->root; + if (node->type == HFS_NODE_LEAF || + tree->attributes & HFS_TREE_VARIDXKEYS) + key_size = hfs_bnode_read_u16(node, 14) + 2; + else + key_size = tree->max_key_len + 2; + hfs_bnode_copy(new_node, 14, node, 14, key_size); + + if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) { + key_size = tree->max_key_len + 2; + hfs_bnode_write_u16(new_node, 14, tree->max_key_len); + } + cnid = cpu_to_be32(node->this); + hfs_bnode_write(new_node, &cnid, 14 + key_size, 4); + + rec -= 2; + hfs_bnode_write_u16(new_node, rec, 14 + key_size + 4); + + hfs_bnode_put(node); + } + hfs_bnode_put(new_node); + mark_inode_dirty(tree->inode); + + return 0; +} diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c new file mode 100644 index 000000000000..84870a169fc7 --- /dev/null +++ b/fs/hfsplus/btree.c @@ -0,0 +1,319 @@ +/* + * linux/fs/hfsplus/btree.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handle opening/closing btree + */ + +#include <linux/slab.h> +#include <linux/pagemap.h> + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + + +/* Get a reference to a B*Tree and do some initial checks */ +struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) +{ + struct hfs_btree *tree; + struct hfs_btree_header_rec *head; + struct address_space *mapping; + struct page *page; + unsigned int shift, size; + + tree = kmalloc(sizeof(*tree), GFP_KERNEL); + if (!tree) + return NULL; + memset(tree, 0, sizeof(*tree)); + + init_MUTEX(&tree->tree_lock); + spin_lock_init(&tree->hash_lock); + /* Set the correct compare function */ + tree->sb = sb; + tree->cnid = id; + if (id == HFSPLUS_EXT_CNID) { + tree->keycmp = hfsplus_ext_cmp_key; + } else if (id == HFSPLUS_CAT_CNID) { + tree->keycmp = hfsplus_cat_cmp_key; + } else { + printk("HFS+-fs: unknown B*Tree requested\n"); + goto free_tree; + } + tree->inode = iget(sb, id); + if (!tree->inode) + goto free_tree; + + mapping = tree->inode->i_mapping; + page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, NULL); + if (IS_ERR(page)) + goto free_tree; + + /* Load the header */ + head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc)); + tree->root = be32_to_cpu(head->root); + tree->leaf_count = be32_to_cpu(head->leaf_count); + tree->leaf_head = be32_to_cpu(head->leaf_head); + tree->leaf_tail = be32_to_cpu(head->leaf_tail); + tree->node_count = be32_to_cpu(head->node_count); + tree->free_nodes = be32_to_cpu(head->free_nodes); + tree->attributes = be32_to_cpu(head->attributes); + tree->node_size = be16_to_cpu(head->node_size); + tree->max_key_len = be16_to_cpu(head->max_key_len); + tree->depth = be16_to_cpu(head->depth); + + size = tree->node_size; + if (!size || size & (size - 1)) + goto fail_page; + if (!tree->node_count) + goto fail_page; + for (shift = 0; size >>= 1; shift += 1) + ; + tree->node_size_shift = shift; + + tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + + kunmap(page); + page_cache_release(page); + return tree; + + fail_page: + tree->inode->i_mapping->a_ops = &hfsplus_aops; + page_cache_release(page); + free_tree: + iput(tree->inode); + kfree(tree); + return NULL; +} + +/* Release resources used by a btree */ +void hfs_btree_close(struct hfs_btree *tree) +{ + struct hfs_bnode *node; + int i; + + if (!tree) + return; + + for (i = 0; i < NODE_HASH_SIZE; i++) { + while ((node = tree->node_hash[i])) { + tree->node_hash[i] = node->next_hash; + if (atomic_read(&node->refcnt)) + printk("HFS+: node %d:%d still has %d user(s)!\n", + node->tree->cnid, node->this, atomic_read(&node->refcnt)); + hfs_bnode_free(node); + tree->node_hash_cnt--; + } + } + iput(tree->inode); + kfree(tree); +} + +void hfs_btree_write(struct hfs_btree *tree) +{ + struct hfs_btree_header_rec *head; + struct hfs_bnode *node; + struct page *page; + + node = hfs_bnode_find(tree, 0); + if (IS_ERR(node)) + /* panic? */ + return; + /* Load the header */ + page = node->page[0]; + head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc)); + + head->root = cpu_to_be32(tree->root); + head->leaf_count = cpu_to_be32(tree->leaf_count); + head->leaf_head = cpu_to_be32(tree->leaf_head); + head->leaf_tail = cpu_to_be32(tree->leaf_tail); + head->node_count = cpu_to_be32(tree->node_count); + head->free_nodes = cpu_to_be32(tree->free_nodes); + head->attributes = cpu_to_be32(tree->attributes); + head->depth = cpu_to_be16(tree->depth); + + kunmap(page); + set_page_dirty(page); + hfs_bnode_put(node); +} + +static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx) +{ + struct hfs_btree *tree = prev->tree; + struct hfs_bnode *node; + struct hfs_bnode_desc desc; + u32 cnid; + + node = hfs_bnode_create(tree, idx); + if (IS_ERR(node)) + return node; + + tree->free_nodes--; + prev->next = idx; + cnid = cpu_to_be32(idx); + hfs_bnode_write(prev, &cnid, offsetof(struct hfs_bnode_desc, next), 4); + + node->type = HFS_NODE_MAP; + node->num_recs = 1; + hfs_bnode_clear(node, 0, tree->node_size); + desc.next = 0; + desc.prev = 0; + desc.type = HFS_NODE_MAP; + desc.height = 0; + desc.num_recs = cpu_to_be16(1); + desc.reserved = 0; + hfs_bnode_write(node, &desc, 0, sizeof(desc)); + hfs_bnode_write_u16(node, 14, 0x8000); + hfs_bnode_write_u16(node, tree->node_size - 2, 14); + hfs_bnode_write_u16(node, tree->node_size - 4, tree->node_size - 6); + + return node; +} + +struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) +{ + struct hfs_bnode *node, *next_node; + struct page **pagep; + u32 nidx, idx; + u16 off, len; + u8 *data, byte, m; + int i; + + while (!tree->free_nodes) { + struct inode *inode = tree->inode; + u32 count; + int res; + + res = hfsplus_file_extend(inode); + if (res) + return ERR_PTR(res); + inode->i_blocks = HFSPLUS_I(inode).alloc_blocks << + HFSPLUS_SB(tree->sb).fs_shift; + HFSPLUS_I(inode).phys_size = inode->i_size = + (loff_t)inode->i_blocks << tree->sb->s_blocksize_bits; + count = inode->i_size >> tree->node_size_shift; + tree->free_nodes = count - tree->node_count; + tree->node_count = count; + } + + nidx = 0; + node = hfs_bnode_find(tree, nidx); + if (IS_ERR(node)) + return node; + len = hfs_brec_lenoff(node, 2, &off); + + off += node->page_offset; + pagep = node->page + (off >> PAGE_CACHE_SHIFT); + data = kmap(*pagep); + off &= ~PAGE_CACHE_MASK; + idx = 0; + + for (;;) { + while (len) { + byte = data[off]; + if (byte != 0xff) { + for (m = 0x80, i = 0; i < 8; m >>= 1, i++) { + if (!(byte & m)) { + idx += i; + data[off] |= m; + set_page_dirty(*pagep); + kunmap(*pagep); + tree->free_nodes--; + mark_inode_dirty(tree->inode); + hfs_bnode_put(node); + if (!idx) { + printk("unexpected idx %u (%u)\n", idx, node->this); + BUG(); + } + return hfs_bnode_create(tree, idx); + } + } + } + if (++off >= PAGE_CACHE_SIZE) { + kunmap(*pagep); + data = kmap(*++pagep); + off = 0; + } + idx += 8; + len--; + } + kunmap(*pagep); + nidx = node->next; + if (!nidx) { + printk("create new bmap node...\n"); + next_node = hfs_bmap_new_bmap(node, idx); + } else + next_node = hfs_bnode_find(tree, nidx); + hfs_bnode_put(node); + if (IS_ERR(next_node)) + return next_node; + node = next_node; + + len = hfs_brec_lenoff(node, 0, &off); + off += node->page_offset; + pagep = node->page + (off >> PAGE_CACHE_SHIFT); + data = kmap(*pagep); + off &= ~PAGE_CACHE_MASK; + } +} + +void hfs_bmap_free(struct hfs_bnode *node) +{ + struct hfs_btree *tree; + struct page *page; + u16 off, len; + u32 nidx; + u8 *data, byte, m; + + dprint(DBG_BNODE_MOD, "btree_free_node: %u\n", node->this); + if (!node->this) + BUG(); + tree = node->tree; + nidx = node->this; + node = hfs_bnode_find(tree, 0); + if (IS_ERR(node)) + return; + len = hfs_brec_lenoff(node, 2, &off); + while (nidx >= len * 8) { + u32 i; + + nidx -= len * 8; + i = node->next; + hfs_bnode_put(node); + if (!i) { + /* panic */; + printk("HFS: unable to free bnode %u. bmap not found!\n", node->this); + return; + } + node = hfs_bnode_find(tree, i); + if (IS_ERR(node)) + return; + if (node->type != HFS_NODE_MAP) { + /* panic */; + printk("HFS: invalid bmap found! (%u,%d)\n", node->this, node->type); + hfs_bnode_put(node); + return; + } + len = hfs_brec_lenoff(node, 0, &off); + } + off += node->page_offset + nidx / 8; + page = node->page[off >> PAGE_CACHE_SHIFT]; + data = kmap(page); + off &= ~PAGE_CACHE_MASK; + m = 1 << (~nidx & 7); + byte = data[off]; + if (!(byte & m)) { + printk("HFS: trying to free free bnode %u(%d)\n", node->this, node->type); + kunmap(page); + hfs_bnode_put(node); + return; + } + data[off] = byte & ~m; + set_page_dirty(page); + kunmap(page); + hfs_bnode_put(node); + tree->free_nodes++; + mark_inode_dirty(tree->inode); +} diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c new file mode 100644 index 000000000000..03a01a3f6f4b --- /dev/null +++ b/fs/hfsplus/catalog.c @@ -0,0 +1,344 @@ +/* + * linux/fs/hfsplus/catalog.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handling of catalog records + */ + +#include <linux/sched.h> + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +int hfsplus_cat_cmp_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2) +{ + u32 k1p, k2p; + + k1p = k1->cat.parent; + k2p = k2->cat.parent; + if (k1p != k2p) + return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1; + + return hfsplus_unistrcmp(&k1->cat.name, &k2->cat.name); +} + +void hfsplus_cat_build_key(hfsplus_btree_key *key, u32 parent, + struct qstr *str) +{ + int len; + + key->cat.parent = cpu_to_be32(parent); + if (str) { + hfsplus_asc2uni(&key->cat.name, str->name, str->len); + len = be16_to_cpu(key->cat.name.length); + } else + len = key->cat.name.length = 0; + key->key_len = cpu_to_be16(6 + 2 * len); +} + +static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent, + struct hfsplus_unistr *name) +{ + int ustrlen; + + ustrlen = be16_to_cpu(name->length); + key->cat.parent = cpu_to_be32(parent); + key->cat.name.length = cpu_to_be16(ustrlen); + ustrlen *= 2; + memcpy(key->cat.name.unicode, name->unicode, ustrlen); + key->key_len = cpu_to_be16(6 + ustrlen); +} + +static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) +{ + if (inode->i_flags & S_IMMUTABLE) + perms->rootflags |= HFSPLUS_FLG_IMMUTABLE; + else + perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE; + if (inode->i_flags & S_APPEND) + perms->rootflags |= HFSPLUS_FLG_APPEND; + else + perms->rootflags &= ~HFSPLUS_FLG_APPEND; + HFSPLUS_I(inode).rootflags = perms->rootflags; + HFSPLUS_I(inode).userflags = perms->userflags; + perms->mode = cpu_to_be16(inode->i_mode); + perms->owner = cpu_to_be32(inode->i_uid); + perms->group = cpu_to_be32(inode->i_gid); +} + +static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct inode *inode) +{ + if (S_ISDIR(inode->i_mode)) { + struct hfsplus_cat_folder *folder; + + folder = &entry->folder; + memset(folder, 0, sizeof(*folder)); + folder->type = cpu_to_be16(HFSPLUS_FOLDER); + folder->id = cpu_to_be32(inode->i_ino); + folder->create_date = folder->content_mod_date = + folder->attribute_mod_date = folder->access_date = hfsp_now2mt(); + hfsplus_set_perms(inode, &folder->permissions); + if (inode == HFSPLUS_SB(inode->i_sb).hidden_dir) + /* invisible and namelocked */ + folder->user_info.frFlags = cpu_to_be16(0x5000); + return sizeof(*folder); + } else { + struct hfsplus_cat_file *file; + + file = &entry->file; + memset(file, 0, sizeof(*file)); + file->type = cpu_to_be16(HFSPLUS_FILE); + file->flags = cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS); + file->id = cpu_to_be32(cnid); + file->create_date = file->content_mod_date = + file->attribute_mod_date = file->access_date = hfsp_now2mt(); + if (cnid == inode->i_ino) { + hfsplus_set_perms(inode, &file->permissions); + file->user_info.fdType = cpu_to_be32(HFSPLUS_SB(inode->i_sb).type); + file->user_info.fdCreator = cpu_to_be32(HFSPLUS_SB(inode->i_sb).creator); + if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE) + file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED); + } else { + file->user_info.fdType = cpu_to_be32(HFSP_HARDLINK_TYPE); + file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR); + file->user_info.fdFlags = cpu_to_be16(0x100); + file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode).dev); + } + return sizeof(*file); + } +} + +static int hfsplus_fill_cat_thread(hfsplus_cat_entry *entry, int type, + u32 parentid, struct qstr *str) +{ + entry->type = cpu_to_be16(type); + entry->thread.reserved = 0; + entry->thread.parentID = cpu_to_be32(parentid); + hfsplus_asc2uni(&entry->thread.nodeName, str->name, str->len); + return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2; +} + +/* Try to get a catalog entry for given catalog id */ +int hfsplus_find_cat(struct super_block *sb, u32 cnid, + struct hfs_find_data *fd) +{ + hfsplus_cat_entry tmp; + int err; + u16 type; + + hfsplus_cat_build_key(fd->search_key, cnid, NULL); + err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry)); + if (err) + return err; + + type = be16_to_cpu(tmp.type); + if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) { + printk("HFS+-fs: Found bad thread record in catalog\n"); + return -EIO; + } + + hfsplus_cat_build_key_uni(fd->search_key, be32_to_cpu(tmp.thread.parentID), + &tmp.thread.nodeName); + return hfs_brec_find(fd); +} + +int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct inode *inode) +{ + struct hfs_find_data fd; + struct super_block *sb; + hfsplus_cat_entry entry; + int entry_size; + int err; + + dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n", str->name, cnid, inode->i_nlink); + sb = dir->i_sb; + hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + + hfsplus_cat_build_key(fd.search_key, cnid, NULL); + entry_size = hfsplus_fill_cat_thread(&entry, S_ISDIR(inode->i_mode) ? + HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD, + dir->i_ino, str); + err = hfs_brec_find(&fd); + if (err != -ENOENT) { + if (!err) + err = -EEXIST; + goto out; + } + err = hfs_brec_insert(&fd, &entry, entry_size); + if (err) + goto out; + + hfsplus_cat_build_key(fd.search_key, dir->i_ino, str); + entry_size = hfsplus_cat_build_record(&entry, cnid, inode); + err = hfs_brec_find(&fd); + if (err != -ENOENT) { + /* panic? */ + if (!err) + err = -EEXIST; + goto out; + } + err = hfs_brec_insert(&fd, &entry, entry_size); + if (!err) { + dir->i_size++; + mark_inode_dirty(dir); + } +out: + hfs_find_exit(&fd); + + return err; +} + +int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) +{ + struct super_block *sb; + struct hfs_find_data fd; + struct hfsplus_fork_raw fork; + struct list_head *pos; + int err, off; + u16 type; + + dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid); + sb = dir->i_sb; + hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + + if (!str) { + int len; + + hfsplus_cat_build_key(fd.search_key, cnid, NULL); + err = hfs_brec_find(&fd); + if (err) + goto out; + + off = fd.entryoffset + offsetof(struct hfsplus_cat_thread, nodeName); + fd.search_key->cat.parent = cpu_to_be32(dir->i_ino); + hfs_bnode_read(fd.bnode, &fd.search_key->cat.name.length, off, 2); + len = be16_to_cpu(fd.search_key->cat.name.length) * 2; + hfs_bnode_read(fd.bnode, &fd.search_key->cat.name.unicode, off + 2, len); + fd.search_key->key_len = cpu_to_be16(6 + len); + } else + hfsplus_cat_build_key(fd.search_key, dir->i_ino, str); + + err = hfs_brec_find(&fd); + if (err) + goto out; + + type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset); + if (type == HFSPLUS_FILE) { +#if 0 + off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork); + hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork)); + hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA); +#endif + + off = fd.entryoffset + offsetof(struct hfsplus_cat_file, rsrc_fork); + hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork)); + hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC); + } + + list_for_each(pos, &HFSPLUS_I(dir).open_dir_list) { + struct hfsplus_readdir_data *rd = + list_entry(pos, struct hfsplus_readdir_data, list); + if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) + rd->file->f_pos--; + } + + err = hfs_brec_remove(&fd); + if (err) + goto out; + + hfsplus_cat_build_key(fd.search_key, cnid, NULL); + err = hfs_brec_find(&fd); + if (err) + goto out; + + err = hfs_brec_remove(&fd); + if (err) + goto out; + + dir->i_size--; + mark_inode_dirty(dir); +out: + hfs_find_exit(&fd); + + return err; +} + +int hfsplus_rename_cat(u32 cnid, + struct inode *src_dir, struct qstr *src_name, + struct inode *dst_dir, struct qstr *dst_name) +{ + struct super_block *sb; + struct hfs_find_data src_fd, dst_fd; + hfsplus_cat_entry entry; + int entry_size, type; + int err = 0; + + dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name, + dst_dir->i_ino, dst_name->name); + sb = src_dir->i_sb; + hfs_find_init(HFSPLUS_SB(sb).cat_tree, &src_fd); + dst_fd = src_fd; + + /* find the old dir entry and read the data */ + hfsplus_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); + err = hfs_brec_find(&src_fd); + if (err) + goto out; + + hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset, + src_fd.entrylength); + + /* create new dir entry with the data from the old entry */ + hfsplus_cat_build_key(dst_fd.search_key, dst_dir->i_ino, dst_name); + err = hfs_brec_find(&dst_fd); + if (err != -ENOENT) { + if (!err) + err = -EEXIST; + goto out; + } + + err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength); + if (err) + goto out; + dst_dir->i_size++; + mark_inode_dirty(dst_dir); + + /* finally remove the old entry */ + hfsplus_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); + err = hfs_brec_find(&src_fd); + if (err) + goto out; + err = hfs_brec_remove(&src_fd); + if (err) + goto out; + src_dir->i_size--; + mark_inode_dirty(src_dir); + + /* remove old thread entry */ + hfsplus_cat_build_key(src_fd.search_key, cnid, NULL); + err = hfs_brec_find(&src_fd); + if (err) + goto out; + type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset); + err = hfs_brec_remove(&src_fd); + if (err) + goto out; + + /* create new thread entry */ + hfsplus_cat_build_key(dst_fd.search_key, cnid, NULL); + entry_size = hfsplus_fill_cat_thread(&entry, type, dst_dir->i_ino, dst_name); + err = hfs_brec_find(&dst_fd); + if (err != -ENOENT) { + if (!err) + err = -EEXIST; + goto out; + } + err = hfs_brec_insert(&dst_fd, &entry, entry_size); +out: + hfs_bnode_put(dst_fd.bnode); + hfs_find_exit(&src_fd); + return err; +} diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c new file mode 100644 index 000000000000..bcdd9521b590 --- /dev/null +++ b/fs/hfsplus/dir.c @@ -0,0 +1,471 @@ +/* + * linux/fs/hfsplus/dir.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handling of directories + */ + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/version.h> + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +/* Find the entry inside dir named dentry->d_name */ +static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct inode *inode = NULL; + struct hfs_find_data fd; + struct super_block *sb; + hfsplus_cat_entry entry; + int err; + u32 cnid, linkid = 0; + u16 type; + + sb = dir->i_sb; + dentry->d_fsdata = NULL; + hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfsplus_cat_build_key(fd.search_key, dir->i_ino, &dentry->d_name); +again: + err = hfs_brec_read(&fd, &entry, sizeof(entry)); + if (err) { + if (err == -ENOENT) { + hfs_find_exit(&fd); + /* No such entry */ + inode = NULL; + goto out; + } + goto fail; + } + type = be16_to_cpu(entry.type); + if (type == HFSPLUS_FOLDER) { + if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) { + err = -EIO; + goto fail; + } + cnid = be32_to_cpu(entry.folder.id); + } else if (type == HFSPLUS_FILE) { + if (fd.entrylength < sizeof(struct hfsplus_cat_file)) { + err = -EIO; + goto fail; + } + cnid = be32_to_cpu(entry.file.id); + if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) && + entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR)) { + struct qstr str; + char name[32]; + + if (dentry->d_fsdata) { + err = -ENOENT; + inode = NULL; + goto out; + } + dentry->d_fsdata = (void *)(unsigned long)cnid; + linkid = be32_to_cpu(entry.file.permissions.dev); + str.len = sprintf(name, "iNode%d", linkid); + str.name = name; + hfsplus_cat_build_key(fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str); + goto again; + } else if (!dentry->d_fsdata) + dentry->d_fsdata = (void *)(unsigned long)cnid; + } else { + printk("HFS+-fs: Illegal catalog entry type in lookup\n"); + err = -EIO; + goto fail; + } + hfs_find_exit(&fd); + inode = iget(dir->i_sb, cnid); + if (!inode) + return ERR_PTR(-EACCES); + if (S_ISREG(inode->i_mode)) + HFSPLUS_I(inode).dev = linkid; +out: + d_add(dentry, inode); + return NULL; +fail: + hfs_find_exit(&fd); + return ERR_PTR(err); +} + +static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; + int len, err; + char strbuf[HFSPLUS_MAX_STRLEN + 1]; + hfsplus_cat_entry entry; + struct hfs_find_data fd; + struct hfsplus_readdir_data *rd; + u16 type; + + if (filp->f_pos >= inode->i_size) + return 0; + + hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfsplus_cat_build_key(fd.search_key, inode->i_ino, NULL); + err = hfs_brec_find(&fd); + if (err) + goto out; + + switch ((u32)filp->f_pos) { + case 0: + /* This is completely artificial... */ + if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR)) + goto out; + filp->f_pos++; + /* fall through */ + case 1: + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); + if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) { + printk("HFS+-fs: bad catalog folder thread\n"); + err = -EIO; + goto out; + } + if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) { + printk("HFS+-fs: truncated catalog thread\n"); + err = -EIO; + goto out; + } + if (filldir(dirent, "..", 2, 1, + be32_to_cpu(entry.thread.parentID), DT_DIR)) + goto out; + filp->f_pos++; + /* fall through */ + default: + if (filp->f_pos >= inode->i_size) + goto out; + err = hfs_brec_goto(&fd, filp->f_pos - 1); + if (err) + goto out; + } + + for (;;) { + if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) { + printk("HFS+-fs: walked past end of dir\n"); + err = -EIO; + goto out; + } + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); + type = be16_to_cpu(entry.type); + len = HFSPLUS_MAX_STRLEN; + err = hfsplus_uni2asc(&fd.key->cat.name, strbuf, &len); + if (err) + goto out; + if (type == HFSPLUS_FOLDER) { + if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) { + printk("HFS+-fs: small dir entry\n"); + err = -EIO; + goto out; + } + if (HFSPLUS_SB(sb).hidden_dir && + HFSPLUS_SB(sb).hidden_dir->i_ino == be32_to_cpu(entry.folder.id)) + goto next; + if (filldir(dirent, strbuf, len, filp->f_pos, + be32_to_cpu(entry.folder.id), DT_DIR)) + break; + } else if (type == HFSPLUS_FILE) { + if (fd.entrylength < sizeof(struct hfsplus_cat_file)) { + printk("HFS+-fs: small file entry\n"); + err = -EIO; + goto out; + } + if (filldir(dirent, strbuf, len, filp->f_pos, + be32_to_cpu(entry.file.id), DT_REG)) + break; + } else { + printk("HFS+-fs: bad catalog entry type\n"); + err = -EIO; + goto out; + } + next: + filp->f_pos++; + if (filp->f_pos >= inode->i_size) + goto out; + err = hfs_brec_goto(&fd, 1); + if (err) + goto out; + } + rd = filp->private_data; + if (!rd) { + rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL); + if (!rd) { + err = -ENOMEM; + goto out; + } + filp->private_data = rd; + rd->file = filp; + list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list); + } + memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); +out: + hfs_find_exit(&fd); + return err; +} + +static int hfsplus_dir_release(struct inode *inode, struct file *file) +{ + struct hfsplus_readdir_data *rd = file->private_data; + if (rd) { + list_del(&rd->list); + kfree(rd); + } + return 0; +} + +int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + struct inode *inode; + int res; + + inode = hfsplus_new_inode(dir->i_sb, mode); + if (!inode) + return -ENOSPC; + + res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); + if (res) { + inode->i_nlink = 0; + iput(inode); + return res; + } + dentry->d_fsdata = (void *)inode->i_ino; + d_instantiate(dentry, inode); + mark_inode_dirty(inode); + return 0; +} + +int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry) +{ + struct super_block *sb = dst_dir->i_sb; + struct inode *inode = src_dentry->d_inode; + struct inode *src_dir = src_dentry->d_parent->d_inode; + struct qstr str; + char name[32]; + u32 cnid, id; + int res; + + if (HFSPLUS_IS_RSRC(inode)) + return -EPERM; + + if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { + for (;;) { + get_random_bytes(&id, sizeof(cnid)); + id &= 0x3fffffff; + str.name = name; + str.len = sprintf(name, "iNode%d", id); + res = hfsplus_rename_cat(inode->i_ino, + src_dir, &src_dentry->d_name, + HFSPLUS_SB(sb).hidden_dir, &str); + if (!res) + break; + if (res != -EEXIST) + return res; + } + HFSPLUS_I(inode).dev = id; + cnid = HFSPLUS_SB(sb).next_cnid++; + src_dentry->d_fsdata = (void *)(unsigned long)cnid; + res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); + if (res) + /* panic? */ + return res; + HFSPLUS_SB(sb).file_count++; + } + cnid = HFSPLUS_SB(sb).next_cnid++; + res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); + if (res) + return res; + + inode->i_nlink++; + dst_dentry->d_fsdata = (void *)(unsigned long)cnid; + d_instantiate(dst_dentry, inode); + atomic_inc(&inode->i_count); + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + HFSPLUS_SB(sb).file_count++; + sb->s_dirt = 1; + + return 0; +} + +int hfsplus_unlink(struct inode *dir, struct dentry *dentry) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode = dentry->d_inode; + struct qstr str; + char name[32]; + u32 cnid; + int res; + + if (HFSPLUS_IS_RSRC(inode)) + return -EPERM; + + cnid = (u32)(unsigned long)dentry->d_fsdata; + if (inode->i_ino == cnid && + atomic_read(&HFSPLUS_I(inode).opencnt)) { + str.name = name; + str.len = sprintf(name, "temp%lu", inode->i_ino); + res = hfsplus_rename_cat(inode->i_ino, + dir, &dentry->d_name, + HFSPLUS_SB(sb).hidden_dir, &str); + if (!res) + inode->i_flags |= S_DEAD; + return res; + } + res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); + if (res) + return res; + + inode->i_nlink--; + hfsplus_delete_inode(inode); + if (inode->i_ino != cnid && !inode->i_nlink) { + if (!atomic_read(&HFSPLUS_I(inode).opencnt)) { + res = hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); + if (!res) + hfsplus_delete_inode(inode); + } else + inode->i_flags |= S_DEAD; + } + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + + return res; +} + +int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct inode *inode; + int res; + + inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode); + if (!inode) + return -ENOSPC; + + res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); + if (res) { + inode->i_nlink = 0; + iput(inode); + return res; + } + d_instantiate(dentry, inode); + mark_inode_dirty(inode); + return 0; +} + +int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode; + int res; + + inode = dentry->d_inode; + if (inode->i_size != 2) + return -ENOTEMPTY; + res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); + if (res) + return res; + inode->i_nlink = 0; + inode->i_ctime = CURRENT_TIME; + hfsplus_delete_inode(inode); + mark_inode_dirty(inode); + return 0; +} + +int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + struct super_block *sb; + struct inode *inode; + int res; + + sb = dir->i_sb; + inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO); + if (!inode) + return -ENOSPC; + + res = page_symlink(inode, symname, strlen(symname) + 1); + if (res) { + inode->i_nlink = 0; + iput (inode); + return res; + } + + mark_inode_dirty(inode); + res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); + + if (!res) { + d_instantiate(dentry, inode); + mark_inode_dirty(inode); + } + + return res; +} + +int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) +{ + struct super_block *sb; + struct inode *inode; + int res; + + sb = dir->i_sb; + inode = hfsplus_new_inode(sb, mode); + if (!inode) + return -ENOSPC; + + res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); + if (res) { + inode->i_nlink = 0; + iput(inode); + return res; + } + init_special_inode(inode, mode, rdev); + d_instantiate(dentry, inode); + mark_inode_dirty(inode); + + return 0; +} + +int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int res; + + /* Unlink destination if it already exists */ + if (new_dentry->d_inode) { + res = hfsplus_unlink(new_dir, new_dentry); + if (res) + return res; + } + + res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata, + old_dir, &old_dentry->d_name, + new_dir, &new_dentry->d_name); + if (!res) + new_dentry->d_fsdata = old_dentry->d_fsdata; + return res; +} + +struct inode_operations hfsplus_dir_inode_operations = { + .lookup = hfsplus_lookup, + .create = hfsplus_create, + .link = hfsplus_link, + .unlink = hfsplus_unlink, + .mkdir = hfsplus_mkdir, + .rmdir = hfsplus_rmdir, + .symlink = hfsplus_symlink, + .mknod = hfsplus_mknod, + .rename = hfsplus_rename, +}; + +struct file_operations hfsplus_dir_operations = { + .read = generic_read_dir, + .readdir = hfsplus_readdir, + .ioctl = hfsplus_ioctl, + .llseek = generic_file_llseek, + .release = hfsplus_dir_release, +}; diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c new file mode 100644 index 000000000000..3cf272194117 --- /dev/null +++ b/fs/hfsplus/extents.c @@ -0,0 +1,502 @@ +/* + * linux/fs/hfsplus/extents.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handling of Extents both in catalog and extents overflow trees + */ + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/pagemap.h> +#include <linux/version.h> + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +/* Compare two extents keys, returns 0 on same, pos/neg for difference */ +int hfsplus_ext_cmp_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2) +{ + u32 k1id, k2id; + u32 k1s, k2s; + + k1id = k1->ext.cnid; + k2id = k2->ext.cnid; + if (k1id != k2id) + return be32_to_cpu(k1id) < be32_to_cpu(k2id) ? -1 : 1; + + if (k1->ext.fork_type != k2->ext.fork_type) + return k1->ext.fork_type < k2->ext.fork_type ? -1 : 1; + + k1s = k1->ext.start_block; + k2s = k2->ext.start_block; + if (k1s == k2s) + return 0; + return be32_to_cpu(k1s) < be32_to_cpu(k2s) ? -1 : 1; +} + +void hfsplus_ext_build_key(hfsplus_btree_key *key, u32 cnid, + u32 block, u8 type) +{ + key->key_len = cpu_to_be16(HFSPLUS_EXT_KEYLEN - 2); + key->ext.cnid = cpu_to_be32(cnid); + key->ext.start_block = cpu_to_be32(block); + key->ext.fork_type = type; + key->ext.pad = 0; +} + +static u32 hfsplus_ext_find_block(struct hfsplus_extent *ext, u32 off) +{ + int i; + u32 count; + + for (i = 0; i < 8; ext++, i++) { + count = be32_to_cpu(ext->block_count); + if (off < count) + return be32_to_cpu(ext->start_block) + off; + off -= count; + } + /* panic? */ + return 0; +} + +static int hfsplus_ext_block_count(struct hfsplus_extent *ext) +{ + int i; + u32 count = 0; + + for (i = 0; i < 8; ext++, i++) + count += be32_to_cpu(ext->block_count); + return count; +} + +static u32 hfsplus_ext_lastblock(struct hfsplus_extent *ext) +{ + int i; + + ext += 7; + for (i = 0; i < 7; ext--, i++) + if (ext->block_count) + break; + return be32_to_cpu(ext->start_block) + be32_to_cpu(ext->block_count); +} + +static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) +{ + int res; + + hfsplus_ext_build_key(fd->search_key, inode->i_ino, HFSPLUS_I(inode).cached_start, + HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); + res = hfs_brec_find(fd); + if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_NEW) { + if (res != -ENOENT) + return; + hfs_brec_insert(fd, HFSPLUS_I(inode).cached_extents, sizeof(hfsplus_extent_rec)); + HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); + } else { + if (res) + return; + hfs_bnode_write(fd->bnode, HFSPLUS_I(inode).cached_extents, fd->entryoffset, fd->entrylength); + HFSPLUS_I(inode).flags &= ~HFSPLUS_FLG_EXT_DIRTY; + } +} + +void hfsplus_ext_write_extent(struct inode *inode) +{ + if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY) { + struct hfs_find_data fd; + + hfs_find_init(HFSPLUS_SB(inode->i_sb).ext_tree, &fd); + __hfsplus_ext_write_extent(inode, &fd); + hfs_find_exit(&fd); + } +} + +static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd, + struct hfsplus_extent *extent, + u32 cnid, u32 block, u8 type) +{ + int res; + + hfsplus_ext_build_key(fd->search_key, cnid, block, type); + fd->key->ext.cnid = 0; + res = hfs_brec_find(fd); + if (res && res != -ENOENT) + return res; + if (fd->key->ext.cnid != fd->search_key->ext.cnid || + fd->key->ext.fork_type != fd->search_key->ext.fork_type) + return -ENOENT; + if (fd->entrylength != sizeof(hfsplus_extent_rec)) + return -EIO; + hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfsplus_extent_rec)); + return 0; +} + +static inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block) +{ + int res; + + if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY) + __hfsplus_ext_write_extent(inode, fd); + + res = __hfsplus_ext_read_extent(fd, HFSPLUS_I(inode).cached_extents, inode->i_ino, + block, HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); + if (!res) { + HFSPLUS_I(inode).cached_start = be32_to_cpu(fd->key->ext.start_block); + HFSPLUS_I(inode).cached_blocks = hfsplus_ext_block_count(HFSPLUS_I(inode).cached_extents); + } else { + HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0; + HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); + } + return res; +} + +static int hfsplus_ext_read_extent(struct inode *inode, u32 block) +{ + struct hfs_find_data fd; + int res; + + if (block >= HFSPLUS_I(inode).cached_start && + block < HFSPLUS_I(inode).cached_start + HFSPLUS_I(inode).cached_blocks) + return 0; + + hfs_find_init(HFSPLUS_SB(inode->i_sb).ext_tree, &fd); + res = __hfsplus_ext_cache_extent(&fd, inode, block); + hfs_find_exit(&fd); + return res; +} + +/* Get a block at iblock for inode, possibly allocating if create */ +int hfsplus_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb; + int res = -EIO; + u32 ablock, dblock, mask; + int shift; + + sb = inode->i_sb; + + /* Convert inode block to disk allocation block */ + shift = HFSPLUS_SB(sb).alloc_blksz_shift - sb->s_blocksize_bits; + ablock = iblock >> HFSPLUS_SB(sb).fs_shift; + + if (iblock >= inode->i_blocks) { + if (iblock > inode->i_blocks || !create) + return -EIO; + if (ablock >= HFSPLUS_I(inode).alloc_blocks) { + res = hfsplus_file_extend(inode); + if (res) + return res; + } + } else + create = 0; + + if (ablock < HFSPLUS_I(inode).first_blocks) { + dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).first_extents, ablock); + goto done; + } + + down(&HFSPLUS_I(inode).extents_lock); + res = hfsplus_ext_read_extent(inode, ablock); + if (!res) { + dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).cached_extents, ablock - + HFSPLUS_I(inode).cached_start); + } else { + up(&HFSPLUS_I(inode).extents_lock); + return -EIO; + } + up(&HFSPLUS_I(inode).extents_lock); + +done: + dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", inode->i_ino, (long long)iblock, dblock); + mask = (1 << HFSPLUS_SB(sb).fs_shift) - 1; + map_bh(bh_result, sb, (dblock << HFSPLUS_SB(sb).fs_shift) + HFSPLUS_SB(sb).blockoffset + (iblock & mask)); + if (create) { + set_buffer_new(bh_result); + HFSPLUS_I(inode).phys_size += sb->s_blocksize; + inode->i_blocks++; + mark_inode_dirty(inode); + } + return 0; +} + +static void hfsplus_dump_extent(struct hfsplus_extent *extent) +{ + int i; + + dprint(DBG_EXTENT, " "); + for (i = 0; i < 8; i++) + dprint(DBG_EXTENT, " %u:%u", be32_to_cpu(extent[i].start_block), + be32_to_cpu(extent[i].block_count)); + dprint(DBG_EXTENT, "\n"); +} + +static int hfsplus_add_extent(struct hfsplus_extent *extent, u32 offset, + u32 alloc_block, u32 block_count) +{ + u32 count, start; + int i; + + hfsplus_dump_extent(extent); + for (i = 0; i < 8; extent++, i++) { + count = be32_to_cpu(extent->block_count); + if (offset == count) { + start = be32_to_cpu(extent->start_block); + if (alloc_block != start + count) { + if (++i >= 8) + return -ENOSPC; + extent++; + extent->start_block = cpu_to_be32(alloc_block); + } else + block_count += count; + extent->block_count = cpu_to_be32(block_count); + return 0; + } else if (offset < count) + break; + offset -= count; + } + /* panic? */ + return -EIO; +} + +int hfsplus_free_extents(struct super_block *sb, struct hfsplus_extent *extent, + u32 offset, u32 block_nr) +{ + u32 count, start; + int i; + + hfsplus_dump_extent(extent); + for (i = 0; i < 8; extent++, i++) { + count = be32_to_cpu(extent->block_count); + if (offset == count) + goto found; + else if (offset < count) + break; + offset -= count; + } + /* panic? */ + return -EIO; +found: + for (;;) { + start = be32_to_cpu(extent->start_block); + if (count <= block_nr) { + hfsplus_block_free(sb, start, count); + extent->block_count = 0; + extent->start_block = 0; + block_nr -= count; + } else { + count -= block_nr; + hfsplus_block_free(sb, start + count, block_nr); + extent->block_count = cpu_to_be32(count); + block_nr = 0; + } + if (!block_nr || !i) + return 0; + i--; + extent--; + count = be32_to_cpu(extent->block_count); + } +} + +int hfsplus_free_fork(struct super_block *sb, u32 cnid, struct hfsplus_fork_raw *fork, int type) +{ + struct hfs_find_data fd; + hfsplus_extent_rec ext_entry; + u32 total_blocks, blocks, start; + int res, i; + + total_blocks = be32_to_cpu(fork->total_blocks); + if (!total_blocks) + return 0; + + blocks = 0; + for (i = 0; i < 8; i++) + blocks += be32_to_cpu(fork->extents[i].block_count); + + res = hfsplus_free_extents(sb, fork->extents, blocks, blocks); + if (res) + return res; + if (total_blocks == blocks) + return 0; + + hfs_find_init(HFSPLUS_SB(sb).ext_tree, &fd); + do { + res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid, + total_blocks, type); + if (res) + break; + start = be32_to_cpu(fd.key->ext.start_block); + hfsplus_free_extents(sb, ext_entry, + total_blocks - start, + total_blocks); + hfs_brec_remove(&fd); + total_blocks = start; + } while (total_blocks > blocks); + hfs_find_exit(&fd); + + return res; +} + +int hfsplus_file_extend(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + u32 start, len, goal; + int res; + + if (HFSPLUS_SB(sb).alloc_file->i_size * 8 < HFSPLUS_SB(sb).total_blocks - HFSPLUS_SB(sb).free_blocks + 8) { + // extend alloc file + printk("extend alloc file! (%Lu,%u,%u)\n", HFSPLUS_SB(sb).alloc_file->i_size * 8, + HFSPLUS_SB(sb).total_blocks, HFSPLUS_SB(sb).free_blocks); + return -ENOSPC; + //BUG(); + } + + down(&HFSPLUS_I(inode).extents_lock); + if (HFSPLUS_I(inode).alloc_blocks == HFSPLUS_I(inode).first_blocks) + goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).first_extents); + else { + res = hfsplus_ext_read_extent(inode, HFSPLUS_I(inode).alloc_blocks); + if (res) + goto out; + goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).cached_extents); + } + + len = HFSPLUS_I(inode).clump_blocks; + start = hfsplus_block_allocate(sb, HFSPLUS_SB(sb).total_blocks, goal, &len); + if (start >= HFSPLUS_SB(sb).total_blocks) { + start = hfsplus_block_allocate(sb, goal, 0, &len); + if (start >= goal) { + res = -ENOSPC; + goto out; + } + } + + dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); + if (HFSPLUS_I(inode).alloc_blocks <= HFSPLUS_I(inode).first_blocks) { + if (!HFSPLUS_I(inode).first_blocks) { + dprint(DBG_EXTENT, "first extents\n"); + /* no extents yet */ + HFSPLUS_I(inode).first_extents[0].start_block = cpu_to_be32(start); + HFSPLUS_I(inode).first_extents[0].block_count = cpu_to_be32(len); + res = 0; + } else { + /* try to append to extents in inode */ + res = hfsplus_add_extent(HFSPLUS_I(inode).first_extents, + HFSPLUS_I(inode).alloc_blocks, + start, len); + if (res == -ENOSPC) + goto insert_extent; + } + if (!res) { + hfsplus_dump_extent(HFSPLUS_I(inode).first_extents); + HFSPLUS_I(inode).first_blocks += len; + } + } else { + res = hfsplus_add_extent(HFSPLUS_I(inode).cached_extents, + HFSPLUS_I(inode).alloc_blocks - + HFSPLUS_I(inode).cached_start, + start, len); + if (!res) { + hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); + HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY; + HFSPLUS_I(inode).cached_blocks += len; + } else if (res == -ENOSPC) + goto insert_extent; + } +out: + up(&HFSPLUS_I(inode).extents_lock); + if (!res) { + HFSPLUS_I(inode).alloc_blocks += len; + mark_inode_dirty(inode); + } + return res; + +insert_extent: + dprint(DBG_EXTENT, "insert new extent\n"); + hfsplus_ext_write_extent(inode); + + memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); + HFSPLUS_I(inode).cached_extents[0].start_block = cpu_to_be32(start); + HFSPLUS_I(inode).cached_extents[0].block_count = cpu_to_be32(len); + hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); + HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW; + HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).alloc_blocks; + HFSPLUS_I(inode).cached_blocks = len; + + res = 0; + goto out; +} + +void hfsplus_file_truncate(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct hfs_find_data fd; + u32 alloc_cnt, blk_cnt, start; + int res; + + dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino, + (long long)HFSPLUS_I(inode).phys_size, inode->i_size); + if (inode->i_size > HFSPLUS_I(inode).phys_size) { + struct address_space *mapping = inode->i_mapping; + struct page *page; + u32 size = inode->i_size - 1; + int res; + + page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); + if (!page) + return; + size &= PAGE_CACHE_SIZE - 1; + size++; + res = mapping->a_ops->prepare_write(NULL, page, size, size); + if (!res) + res = mapping->a_ops->commit_write(NULL, page, size, size); + if (res) + inode->i_size = HFSPLUS_I(inode).phys_size; + unlock_page(page); + page_cache_release(page); + mark_inode_dirty(inode); + return; + } + blk_cnt = (inode->i_size + HFSPLUS_SB(sb).alloc_blksz - 1) >> HFSPLUS_SB(sb).alloc_blksz_shift; + alloc_cnt = HFSPLUS_I(inode).alloc_blocks; + if (blk_cnt == alloc_cnt) + goto out; + + down(&HFSPLUS_I(inode).extents_lock); + hfs_find_init(HFSPLUS_SB(sb).ext_tree, &fd); + while (1) { + if (alloc_cnt == HFSPLUS_I(inode).first_blocks) { + hfsplus_free_extents(sb, HFSPLUS_I(inode).first_extents, + alloc_cnt, alloc_cnt - blk_cnt); + hfsplus_dump_extent(HFSPLUS_I(inode).first_extents); + HFSPLUS_I(inode).first_blocks = blk_cnt; + break; + } + res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt); + if (res) + break; + start = HFSPLUS_I(inode).cached_start; + hfsplus_free_extents(sb, HFSPLUS_I(inode).cached_extents, + alloc_cnt - start, alloc_cnt - blk_cnt); + hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); + if (blk_cnt > start) { + HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY; + break; + } + alloc_cnt = start; + HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0; + HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); + hfs_brec_remove(&fd); + } + hfs_find_exit(&fd); + up(&HFSPLUS_I(inode).extents_lock); + + HFSPLUS_I(inode).alloc_blocks = blk_cnt; +out: + HFSPLUS_I(inode).phys_size = inode->i_size; + mark_inode_dirty(inode); + inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; +} diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h new file mode 100644 index 000000000000..2e172a80fc5a --- /dev/null +++ b/fs/hfsplus/hfsplus_fs.h @@ -0,0 +1,412 @@ +/* + * linux/include/linux/hfsplus_fs.h + * + * Copyright (C) 1999 + * Brad Boyer (flar@pants.nu) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + */ + +#ifndef _LINUX_HFSPLUS_FS_H +#define _LINUX_HFSPLUS_FS_H + +#include <linux/fs.h> +#include <linux/version.h> +#include <linux/buffer_head.h> +#include "hfsplus_raw.h" + +#define DBG_BNODE_REFS 0x00000001 +#define DBG_BNODE_MOD 0x00000002 +#define DBG_CAT_MOD 0x00000004 +#define DBG_INODE 0x00000008 +#define DBG_SUPER 0x00000010 +#define DBG_EXTENT 0x00000020 +#define DBG_BITMAP 0x00000040 + +//#define DBG_MASK (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD) +//#define DBG_MASK (DBG_BNODE_MOD|DBG_CAT_MOD|DBG_INODE) +//#define DBG_MASK (DBG_CAT_MOD|DBG_BNODE_REFS|DBG_INODE|DBG_EXTENT) +#define DBG_MASK (0) + +#define dprint(flg, fmt, args...) \ + if (flg & DBG_MASK) printk(fmt , ## args) + +/* Runtime config options */ +#define HFSPLUS_DEF_CR_TYPE 0x3F3F3F3F /* '????' */ + +#define HFSPLUS_TYPE_DATA 0x00 +#define HFSPLUS_TYPE_RSRC 0xFF + +typedef int (*btree_keycmp)(hfsplus_btree_key *, hfsplus_btree_key *); + +#define NODE_HASH_SIZE 256 + +/* An HFS+ BTree held in memory */ +struct hfs_btree { + struct super_block *sb; + struct inode *inode; + btree_keycmp keycmp; + + u32 cnid; + u32 root; + u32 leaf_count; + u32 leaf_head; + u32 leaf_tail; + u32 node_count; + u32 free_nodes; + u32 attributes; + + unsigned int node_size; + unsigned int node_size_shift; + unsigned int max_key_len; + unsigned int depth; + + //unsigned int map1_size, map_size; + struct semaphore tree_lock; + + unsigned int pages_per_bnode; + spinlock_t hash_lock; + struct hfs_bnode *node_hash[NODE_HASH_SIZE]; + int node_hash_cnt; +}; + +struct page; + +/* An HFS+ BTree node in memory */ +struct hfs_bnode { + struct hfs_btree *tree; + + u32 prev; + u32 this; + u32 next; + u32 parent; + + u16 num_recs; + u8 type; + u8 height; + + struct hfs_bnode *next_hash; + unsigned long flags; + wait_queue_head_t lock_wq; + atomic_t refcnt; + unsigned int page_offset; + struct page *page[0]; +}; + +#define HFS_BNODE_LOCK 0 +#define HFS_BNODE_ERROR 1 +#define HFS_BNODE_NEW 2 +#define HFS_BNODE_DIRTY 3 +#define HFS_BNODE_DELETED 4 + +/* + * HFS+ superblock info (built from Volume Header on disk) + */ + +struct hfsplus_vh; +struct hfs_btree; + +struct hfsplus_sb_info { + struct buffer_head *s_vhbh; + struct hfsplus_vh *s_vhdr; + struct hfs_btree *ext_tree; + struct hfs_btree *cat_tree; + struct hfs_btree *attr_tree; + struct inode *alloc_file; + struct inode *hidden_dir; + + /* Runtime variables */ + u32 blockoffset; + u32 sect_count; + int fs_shift; + + /* Stuff in host order from Vol Header */ + u32 alloc_blksz; + int alloc_blksz_shift; + u32 total_blocks; + u32 free_blocks; + u32 next_alloc; + u32 next_cnid; + u32 file_count; + u32 folder_count; + u32 data_clump_blocks, rsrc_clump_blocks; + + /* Config options */ + u32 creator; + u32 type; + + umode_t umask; + uid_t uid; + gid_t gid; + + int part, session; + + unsigned long flags; + + atomic_t inode_cnt; + u32 last_inode_cnt; + + struct hlist_head rsrc_inodes; +}; + +#define HFSPLUS_SB_WRITEBACKUP 0x0001 + + +struct hfsplus_inode_info { + struct semaphore extents_lock; + u32 clump_blocks, alloc_blocks; + /* Allocation extents from catalog record or volume header */ + hfsplus_extent_rec first_extents; + u32 first_blocks; + hfsplus_extent_rec cached_extents; + u32 cached_start, cached_blocks; + atomic_t opencnt; + + struct inode *rsrc_inode; + unsigned long flags; + + /* Device number in hfsplus_permissions in catalog */ + u32 dev; + /* BSD system and user file flags */ + u8 rootflags; + u8 userflags; + + struct list_head open_dir_list; + loff_t phys_size; + struct inode vfs_inode; +}; + +#define HFSPLUS_FLG_RSRC 0x0001 +#define HFSPLUS_FLG_EXT_DIRTY 0x0002 +#define HFSPLUS_FLG_EXT_NEW 0x0004 + +#define HFSPLUS_IS_DATA(inode) (!(HFSPLUS_I(inode).flags & HFSPLUS_FLG_RSRC)) +#define HFSPLUS_IS_RSRC(inode) (HFSPLUS_I(inode).flags & HFSPLUS_FLG_RSRC) + +struct hfs_find_data { + /* filled by caller */ + hfsplus_btree_key *search_key; + hfsplus_btree_key *key; + /* filled by find */ + struct hfs_btree *tree; + struct hfs_bnode *bnode; + /* filled by findrec */ + int record; + int keyoffset, keylength; + int entryoffset, entrylength; +}; + +struct hfsplus_readdir_data { + struct list_head list; + struct file *file; + struct hfsplus_cat_key key; +}; + +#define hfs_btree_open hfsplus_btree_open +#define hfs_btree_close hfsplus_btree_close +#define hfs_btree_write hfsplus_btree_write +#define hfs_bmap_alloc hfsplus_bmap_alloc +#define hfs_bmap_free hfsplus_bmap_free +#define hfs_bnode_read hfsplus_bnode_read +#define hfs_bnode_read_u16 hfsplus_bnode_read_u16 +#define hfs_bnode_read_u8 hfsplus_bnode_read_u8 +#define hfs_bnode_read_key hfsplus_bnode_read_key +#define hfs_bnode_write hfsplus_bnode_write +#define hfs_bnode_write_u16 hfsplus_bnode_write_u16 +#define hfs_bnode_clear hfsplus_bnode_clear +#define hfs_bnode_copy hfsplus_bnode_copy +#define hfs_bnode_move hfsplus_bnode_move +#define hfs_bnode_dump hfsplus_bnode_dump +#define hfs_bnode_unlink hfsplus_bnode_unlink +#define hfs_bnode_findhash hfsplus_bnode_findhash +#define hfs_bnode_find hfsplus_bnode_find +#define hfs_bnode_unhash hfsplus_bnode_unhash +#define hfs_bnode_free hfsplus_bnode_free +#define hfs_bnode_create hfsplus_bnode_create +#define hfs_bnode_get hfsplus_bnode_get +#define hfs_bnode_put hfsplus_bnode_put +#define hfs_brec_lenoff hfsplus_brec_lenoff +#define hfs_brec_keylen hfsplus_brec_keylen +#define hfs_brec_insert hfsplus_brec_insert +#define hfs_brec_remove hfsplus_brec_remove +#define hfs_bnode_split hfsplus_bnode_split +#define hfs_brec_update_parent hfsplus_brec_update_parent +#define hfs_btree_inc_height hfsplus_btree_inc_height +#define hfs_find_init hfsplus_find_init +#define hfs_find_exit hfsplus_find_exit +#define __hfs_brec_find __hplusfs_brec_find +#define hfs_brec_find hfsplus_brec_find +#define hfs_brec_read hfsplus_brec_read +#define hfs_brec_goto hfsplus_brec_goto +#define hfs_part_find hfsplus_part_find + +/* + * definitions for ext2 flag ioctls (linux really needs a generic + * interface for this). + */ + +/* ext2 ioctls (EXT2_IOC_GETFLAGS and EXT2_IOC_SETFLAGS) to support + * chattr/lsattr */ +#define HFSPLUS_IOC_EXT2_GETFLAGS _IOR('f', 1, long) +#define HFSPLUS_IOC_EXT2_SETFLAGS _IOW('f', 2, long) + +#define EXT2_FLAG_IMMUTABLE 0x00000010 /* Immutable file */ +#define EXT2_FLAG_APPEND 0x00000020 /* writes to file may only append */ +#define EXT2_FLAG_NODUMP 0x00000040 /* do not dump file */ + + +/* + * Functions in any *.c used in other files + */ + +/* bitmap.c */ +int hfsplus_block_allocate(struct super_block *, u32, u32, u32 *); +int hfsplus_block_free(struct super_block *, u32, u32); + +/* btree.c */ +struct hfs_btree *hfs_btree_open(struct super_block *, u32); +void hfs_btree_close(struct hfs_btree *); +void hfs_btree_write(struct hfs_btree *); +struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *); +void hfs_bmap_free(struct hfs_bnode *); + +/* bnode.c */ +void hfs_bnode_read(struct hfs_bnode *, void *, int, int); +u16 hfs_bnode_read_u16(struct hfs_bnode *, int); +u8 hfs_bnode_read_u8(struct hfs_bnode *, int); +void hfs_bnode_read_key(struct hfs_bnode *, void *, int); +void hfs_bnode_write(struct hfs_bnode *, void *, int, int); +void hfs_bnode_write_u16(struct hfs_bnode *, int, u16); +void hfs_bnode_clear(struct hfs_bnode *, int, int); +void hfs_bnode_copy(struct hfs_bnode *, int, + struct hfs_bnode *, int, int); +void hfs_bnode_move(struct hfs_bnode *, int, int, int); +void hfs_bnode_dump(struct hfs_bnode *); +void hfs_bnode_unlink(struct hfs_bnode *); +struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32); +struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32); +void hfs_bnode_unhash(struct hfs_bnode *); +void hfs_bnode_free(struct hfs_bnode *); +struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32); +void hfs_bnode_get(struct hfs_bnode *); +void hfs_bnode_put(struct hfs_bnode *); + +/* brec.c */ +u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *); +u16 hfs_brec_keylen(struct hfs_bnode *, u16); +int hfs_brec_insert(struct hfs_find_data *, void *, int); +int hfs_brec_remove(struct hfs_find_data *); +struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *); +int hfs_brec_update_parent(struct hfs_find_data *); +int hfs_btree_inc_height(struct hfs_btree *); + +/* bfind.c */ +int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); +void hfs_find_exit(struct hfs_find_data *); +int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *); +int hfs_brec_find(struct hfs_find_data *); +int hfs_brec_read(struct hfs_find_data *, void *, int); +int hfs_brec_goto(struct hfs_find_data *, int); + +/* catalog.c */ +int hfsplus_cat_cmp_key(hfsplus_btree_key *, hfsplus_btree_key *); +void hfsplus_cat_build_key(hfsplus_btree_key *, u32, struct qstr *); +int hfsplus_find_cat(struct super_block *, u32, struct hfs_find_data *); +int hfsplus_create_cat(u32, struct inode *, struct qstr *, struct inode *); +int hfsplus_delete_cat(u32, struct inode *, struct qstr *); +int hfsplus_rename_cat(u32, struct inode *, struct qstr *, + struct inode *, struct qstr *); + +/* extents.c */ +int hfsplus_ext_cmp_key(hfsplus_btree_key *, hfsplus_btree_key *); +void hfsplus_ext_build_key(hfsplus_btree_key *, u32, u32, u8); +void hfsplus_ext_write_extent(struct inode *); +int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int); +int hfsplus_free_fork(struct super_block *, u32, struct hfsplus_fork_raw *, int); +int hfsplus_file_extend(struct inode *); +void hfsplus_file_truncate(struct inode *); + +/* inode.c */ +extern struct address_space_operations hfsplus_aops; +extern struct address_space_operations hfsplus_btree_aops; + +void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *); +void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *); +int hfsplus_cat_read_inode(struct inode *, struct hfs_find_data *); +void hfsplus_cat_write_inode(struct inode *); +struct inode *hfsplus_new_inode(struct super_block *, int); +void hfsplus_delete_inode(struct inode *); + +/* ioctl.c */ +int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); + +/* options.c */ +int parse_options(char *, struct hfsplus_sb_info *); +void fill_defaults(struct hfsplus_sb_info *); +void fill_current(struct hfsplus_sb_info *, struct hfsplus_sb_info *); + +/* tables.c */ +extern u16 case_fold_table[]; + +/* unicode.c */ +int hfsplus_unistrcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *); +int hfsplus_uni2asc(const struct hfsplus_unistr *, char *, int *); +int hfsplus_asc2uni(struct hfsplus_unistr *, const char *, int); + +/* wrapper.c */ +int hfsplus_read_wrapper(struct super_block *); + +int hfs_part_find(struct super_block *, sector_t *, sector_t *); + +/* access macros */ +/* +static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} +static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode) +{ + return list_entry(inode, struct hfsplus_inode_info, vfs_inode); +} +*/ +#define HFSPLUS_SB(super) (*(struct hfsplus_sb_info *)(super)->s_fs_info) +#define HFSPLUS_I(inode) (*list_entry(inode, struct hfsplus_inode_info, vfs_inode)) + +#if 1 +#define hfsplus_kmap(p) ({ struct page *__p = (p); kmap(__p); }) +#define hfsplus_kunmap(p) ({ struct page *__p = (p); kunmap(__p); __p; }) +#else +#define hfsplus_kmap(p) kmap(p) +#define hfsplus_kunmap(p) kunmap(p) +#endif + +#define sb_bread512(sb, sec, data) ({ \ + struct buffer_head *__bh; \ + sector_t __block; \ + loff_t __start; \ + int __offset; \ + \ + __start = (loff_t)(sec) << HFSPLUS_SECTOR_SHIFT;\ + __block = __start >> (sb)->s_blocksize_bits; \ + __offset = __start & ((sb)->s_blocksize - 1); \ + __bh = sb_bread((sb), __block); \ + if (likely(__bh != NULL)) \ + data = (void *)(__bh->b_data + __offset);\ + else \ + data = NULL; \ + __bh; \ +}) + +/* time macros */ +#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U) +#define __hfsp_ut2mt(t) (cpu_to_be32(t + 2082844800U)) + +/* compatibility */ +#define hfsp_mt2ut(t) (struct timespec){ .tv_sec = __hfsp_mt2ut(t) } +#define hfsp_ut2mt(t) __hfsp_ut2mt((t).tv_sec) +#define hfsp_now2mt() __hfsp_ut2mt(get_seconds()) + +#define kdev_t_to_nr(x) (x) + +#endif diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h new file mode 100644 index 000000000000..696f06f8a24e --- /dev/null +++ b/fs/hfsplus/hfsplus_raw.h @@ -0,0 +1,326 @@ +/* + * linux/include/linux/hfsplus_raw.h + * + * Copyright (C) 1999 + * Brad Boyer (flar@pants.nu) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Format of structures on disk + * Information taken from Apple Technote #1150 (HFS Plus Volume Format) + * + */ + +#ifndef _LINUX_HFSPLUS_RAW_H +#define _LINUX_HFSPLUS_RAW_H + +#include <linux/types.h> + +#define __packed __attribute__ ((packed)) + +/* Some constants */ +#define HFSPLUS_SECTOR_SIZE 512 +#define HFSPLUS_SECTOR_SHIFT 9 +#define HFSPLUS_VOLHEAD_SECTOR 2 +#define HFSPLUS_VOLHEAD_SIG 0x482b +#define HFSPLUS_SUPER_MAGIC 0x482b +#define HFSPLUS_CURRENT_VERSION 4 + +#define HFSP_WRAP_MAGIC 0x4244 +#define HFSP_WRAP_ATTRIB_SLOCK 0x8000 +#define HFSP_WRAP_ATTRIB_SPARED 0x0200 + +#define HFSP_WRAPOFF_SIG 0x00 +#define HFSP_WRAPOFF_ATTRIB 0x0A +#define HFSP_WRAPOFF_ABLKSIZE 0x14 +#define HFSP_WRAPOFF_ABLKSTART 0x1C +#define HFSP_WRAPOFF_EMBEDSIG 0x7C +#define HFSP_WRAPOFF_EMBEDEXT 0x7E + +#define HFSP_HIDDENDIR_NAME "\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data" + +#define HFSP_HARDLINK_TYPE 0x686c6e6b /* 'hlnk' */ +#define HFSP_HFSPLUS_CREATOR 0x6866732b /* 'hfs+' */ + +#define HFSP_MOUNT_VERSION 0x482b4c78 /* 'H+Lx' */ + +/* Structures used on disk */ + +typedef u32 hfsplus_cnid; +typedef u16 hfsplus_unichr; + +/* A "string" as used in filenames, etc. */ +struct hfsplus_unistr { + u16 length; + hfsplus_unichr unicode[255]; +} __packed; + +#define HFSPLUS_MAX_STRLEN 255 + +/* POSIX permissions */ +struct hfsplus_perm { + u32 owner; + u32 group; + u8 rootflags; + u8 userflags; + u16 mode; + u32 dev; +} __packed; + +#define HFSPLUS_FLG_NODUMP 0x01 +#define HFSPLUS_FLG_IMMUTABLE 0x02 +#define HFSPLUS_FLG_APPEND 0x04 + +/* A single contiguous area of a file */ +struct hfsplus_extent { + u32 start_block; + u32 block_count; +} __packed; +typedef struct hfsplus_extent hfsplus_extent_rec[8]; + +/* Information for a "Fork" in a file */ +struct hfsplus_fork_raw { + u64 total_size; + u32 clump_size; + u32 total_blocks; + hfsplus_extent_rec extents; +} __packed; + +/* HFS+ Volume Header */ +struct hfsplus_vh { + u16 signature; + u16 version; + u32 attributes; + u32 last_mount_vers; + u32 reserved; + + u32 create_date; + u32 modify_date; + u32 backup_date; + u32 checked_date; + + u32 file_count; + u32 folder_count; + + u32 blocksize; + u32 total_blocks; + u32 free_blocks; + + u32 next_alloc; + u32 rsrc_clump_sz; + u32 data_clump_sz; + hfsplus_cnid next_cnid; + + u32 write_count; + u64 encodings_bmp; + + u8 finder_info[32]; + + struct hfsplus_fork_raw alloc_file; + struct hfsplus_fork_raw ext_file; + struct hfsplus_fork_raw cat_file; + struct hfsplus_fork_raw attr_file; + struct hfsplus_fork_raw start_file; +} __packed; + +/* HFS+ volume attributes */ +#define HFSPLUS_VOL_UNMNT (1 << 8) +#define HFSPLUS_VOL_SPARE_BLK (1 << 9) +#define HFSPLUS_VOL_NOCACHE (1 << 10) +#define HFSPLUS_VOL_INCNSTNT (1 << 11) +#define HFSPLUS_VOL_SOFTLOCK (1 << 15) + +/* HFS+ BTree node descriptor */ +struct hfs_bnode_desc { + u32 next; + u32 prev; + s8 type; + u8 height; + u16 num_recs; + u16 reserved; +} __packed; + +/* HFS+ BTree node types */ +#define HFS_NODE_INDEX 0x00 +#define HFS_NODE_HEADER 0x01 +#define HFS_NODE_MAP 0x02 +#define HFS_NODE_LEAF 0xFF + +/* HFS+ BTree header */ +struct hfs_btree_header_rec { + u16 depth; + u32 root; + u32 leaf_count; + u32 leaf_head; + u32 leaf_tail; + u16 node_size; + u16 max_key_len; + u32 node_count; + u32 free_nodes; + u16 reserved1; + u32 clump_size; + u8 btree_type; + u8 reserved2; + u32 attributes; + u32 reserved3[16]; +} __packed; + +/* BTree attributes */ +#define HFS_TREE_BIGKEYS 2 +#define HFS_TREE_VARIDXKEYS 4 + +/* HFS+ BTree misc info */ +#define HFSPLUS_TREE_HEAD 0 +#define HFSPLUS_NODE_MXSZ 32768 + +/* Some special File ID numbers (stolen from hfs.h) */ +#define HFSPLUS_POR_CNID 1 /* Parent Of the Root */ +#define HFSPLUS_ROOT_CNID 2 /* ROOT directory */ +#define HFSPLUS_EXT_CNID 3 /* EXTents B-tree */ +#define HFSPLUS_CAT_CNID 4 /* CATalog B-tree */ +#define HFSPLUS_BAD_CNID 5 /* BAD blocks file */ +#define HFSPLUS_ALLOC_CNID 6 /* ALLOCation file */ +#define HFSPLUS_START_CNID 7 /* STARTup file */ +#define HFSPLUS_ATTR_CNID 8 /* ATTRibutes file */ +#define HFSPLUS_EXCH_CNID 15 /* ExchangeFiles temp id */ +#define HFSPLUS_FIRSTUSER_CNID 16 /* first available user id */ + +/* HFS+ catalog entry key */ +struct hfsplus_cat_key { + u16 key_len; + hfsplus_cnid parent; + struct hfsplus_unistr name; +} __packed; + + +/* Structs from hfs.h */ +struct hfsp_point { + u16 v; + u16 h; +} __packed; + +struct hfsp_rect { + u16 top; + u16 left; + u16 bottom; + u16 right; +} __packed; + + +/* HFS directory info (stolen from hfs.h */ +struct DInfo { + struct hfsp_rect frRect; + u16 frFlags; + struct hfsp_point frLocation; + u16 frView; +} __packed; + +struct DXInfo { + struct hfsp_point frScroll; + u32 frOpenChain; + u16 frUnused; + u16 frComment; + u32 frPutAway; +} __packed; + +/* HFS+ folder data (part of an hfsplus_cat_entry) */ +struct hfsplus_cat_folder { + s16 type; + u16 flags; + u32 valence; + hfsplus_cnid id; + u32 create_date; + u32 content_mod_date; + u32 attribute_mod_date; + u32 access_date; + u32 backup_date; + struct hfsplus_perm permissions; + struct DInfo user_info; + struct DXInfo finder_info; + u32 text_encoding; + u32 reserved; +} __packed; + +/* HFS file info (stolen from hfs.h) */ +struct FInfo { + u32 fdType; + u32 fdCreator; + u16 fdFlags; + struct hfsp_point fdLocation; + u16 fdFldr; +} __packed; + +struct FXInfo { + u16 fdIconID; + u8 fdUnused[8]; + u16 fdComment; + u32 fdPutAway; +} __packed; + +/* HFS+ file data (part of a cat_entry) */ +struct hfsplus_cat_file { + s16 type; + u16 flags; + u32 reserved1; + hfsplus_cnid id; + u32 create_date; + u32 content_mod_date; + u32 attribute_mod_date; + u32 access_date; + u32 backup_date; + struct hfsplus_perm permissions; + struct FInfo user_info; + struct FXInfo finder_info; + u32 text_encoding; + u32 reserved2; + + struct hfsplus_fork_raw data_fork; + struct hfsplus_fork_raw rsrc_fork; +} __packed; + +/* File attribute bits */ +#define HFSPLUS_FILE_LOCKED 0x0001 +#define HFSPLUS_FILE_THREAD_EXISTS 0x0002 + +/* HFS+ catalog thread (part of a cat_entry) */ +struct hfsplus_cat_thread { + s16 type; + s16 reserved; + hfsplus_cnid parentID; + struct hfsplus_unistr nodeName; +} __packed; + +#define HFSPLUS_MIN_THREAD_SZ 10 + +/* A data record in the catalog tree */ +typedef union { + s16 type; + struct hfsplus_cat_folder folder; + struct hfsplus_cat_file file; + struct hfsplus_cat_thread thread; +} __packed hfsplus_cat_entry; + +/* HFS+ catalog entry type */ +#define HFSPLUS_FOLDER 0x0001 +#define HFSPLUS_FILE 0x0002 +#define HFSPLUS_FOLDER_THREAD 0x0003 +#define HFSPLUS_FILE_THREAD 0x0004 + +/* HFS+ extents tree key */ +struct hfsplus_ext_key { + u16 key_len; + u8 fork_type; + u8 pad; + hfsplus_cnid cnid; + u32 start_block; +} __packed; + +#define HFSPLUS_EXT_KEYLEN 12 + +/* HFS+ generic BTree key */ +typedef union { + u16 key_len; + struct hfsplus_cat_key cat; + struct hfsplus_ext_key ext; +} __packed hfsplus_btree_key; + +#endif diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c new file mode 100644 index 000000000000..435e7e4bff8e --- /dev/null +++ b/fs/hfsplus/inode.c @@ -0,0 +1,549 @@ +/* + * linux/fs/hfsplus/inode.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Inode handling routines + */ + +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/pagemap.h> +#include <linux/version.h> +#include <linux/mpage.h> + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +static int hfsplus_readpage(struct file *file, struct page *page) +{ + //printk("readpage: %lu\n", page->index); + return block_read_full_page(page, hfsplus_get_block); +} + +static int hfsplus_writepage(struct page *page, struct writeback_control *wbc) +{ + //printk("writepage: %lu\n", page->index); + return block_write_full_page(page, hfsplus_get_block, wbc); +} + +static int hfsplus_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page, from, to, hfsplus_get_block, + &HFSPLUS_I(page->mapping->host).phys_size); +} + +static sector_t hfsplus_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping, block, hfsplus_get_block); +} + +int hfsplus_releasepage(struct page *page, int mask) +{ + struct inode *inode = page->mapping->host; + struct super_block *sb = inode->i_sb; + struct hfs_btree *tree; + struct hfs_bnode *node; + u32 nidx; + int i, res = 1; + + switch (inode->i_ino) { + case HFSPLUS_EXT_CNID: + tree = HFSPLUS_SB(sb).ext_tree; + break; + case HFSPLUS_CAT_CNID: + tree = HFSPLUS_SB(sb).cat_tree; + break; + case HFSPLUS_ATTR_CNID: + tree = HFSPLUS_SB(sb).attr_tree; + break; + default: + BUG(); + return 0; + } + if (tree->node_size >= PAGE_CACHE_SIZE) { + nidx = page->index >> (tree->node_size_shift - PAGE_CACHE_SHIFT); + spin_lock(&tree->hash_lock); + node = hfs_bnode_findhash(tree, nidx); + if (!node) + ; + else if (atomic_read(&node->refcnt)) + res = 0; + else for (i = 0; i < tree->pages_per_bnode; i++) { + if (PageActive(node->page[i])) { + res = 0; + break; + } + } + if (res && node) { + hfs_bnode_unhash(node); + hfs_bnode_free(node); + } + spin_unlock(&tree->hash_lock); + } else { + nidx = page->index << (PAGE_CACHE_SHIFT - tree->node_size_shift); + i = 1 << (PAGE_CACHE_SHIFT - tree->node_size_shift); + spin_lock(&tree->hash_lock); + do { + node = hfs_bnode_findhash(tree, nidx++); + if (!node) + continue; + if (atomic_read(&node->refcnt)) { + res = 0; + break; + } + hfs_bnode_unhash(node); + hfs_bnode_free(node); + } while (--i); + spin_unlock(&tree->hash_lock); + } + //printk("releasepage: %lu,%x = %d\n", page->index, mask, res); + return res; +} + +static int hfsplus_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, + struct buffer_head *bh_result, int create) +{ + int ret; + + ret = hfsplus_get_block(inode, iblock, bh_result, create); + if (!ret) + bh_result->b_size = (1 << inode->i_blkbits); + return ret; +} + +static int hfsplus_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_dentry->d_inode->i_mapping->host; + + return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, hfsplus_get_blocks, NULL); +} + +static int hfsplus_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + return mpage_writepages(mapping, wbc, hfsplus_get_block); +} + +struct address_space_operations hfsplus_btree_aops = { + .readpage = hfsplus_readpage, + .writepage = hfsplus_writepage, + .sync_page = block_sync_page, + .prepare_write = hfsplus_prepare_write, + .commit_write = generic_commit_write, + .bmap = hfsplus_bmap, + .releasepage = hfsplus_releasepage, +}; + +struct address_space_operations hfsplus_aops = { + .readpage = hfsplus_readpage, + .writepage = hfsplus_writepage, + .sync_page = block_sync_page, + .prepare_write = hfsplus_prepare_write, + .commit_write = generic_commit_write, + .bmap = hfsplus_bmap, + .direct_IO = hfsplus_direct_IO, + .writepages = hfsplus_writepages, +}; + +static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct hfs_find_data fd; + struct super_block *sb = dir->i_sb; + struct inode *inode = NULL; + int err; + + if (HFSPLUS_IS_RSRC(dir) || strcmp(dentry->d_name.name, "rsrc")) + goto out; + + inode = HFSPLUS_I(dir).rsrc_inode; + if (inode) + goto out; + + inode = new_inode(sb); + if (!inode) + return ERR_PTR(-ENOMEM); + + inode->i_ino = dir->i_ino; + INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list); + init_MUTEX(&HFSPLUS_I(inode).extents_lock); + HFSPLUS_I(inode).flags = HFSPLUS_FLG_RSRC; + + hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + err = hfsplus_find_cat(sb, dir->i_ino, &fd); + if (!err) + err = hfsplus_cat_read_inode(inode, &fd); + hfs_find_exit(&fd); + if (err) { + iput(inode); + return ERR_PTR(err); + } + HFSPLUS_I(inode).rsrc_inode = dir; + HFSPLUS_I(dir).rsrc_inode = inode; + igrab(dir); + hlist_add_head(&inode->i_hash, &HFSPLUS_SB(sb).rsrc_inodes); + mark_inode_dirty(inode); + { + void hfsplus_inode_check(struct super_block *sb); + atomic_inc(&HFSPLUS_SB(sb).inode_cnt); + hfsplus_inode_check(sb); + } +out: + d_add(dentry, inode); + return NULL; +} + +static void hfsplus_get_perms(struct inode *inode, struct hfsplus_perm *perms, int dir) +{ + struct super_block *sb = inode->i_sb; + u16 mode; + + mode = be16_to_cpu(perms->mode); + + inode->i_uid = be32_to_cpu(perms->owner); + if (!inode->i_uid && !mode) + inode->i_uid = HFSPLUS_SB(sb).uid; + + inode->i_gid = be32_to_cpu(perms->group); + if (!inode->i_gid && !mode) + inode->i_gid = HFSPLUS_SB(sb).gid; + + if (dir) { + mode = mode ? (mode & S_IALLUGO) : + (S_IRWXUGO & ~(HFSPLUS_SB(sb).umask)); + mode |= S_IFDIR; + } else if (!mode) + mode = S_IFREG | ((S_IRUGO|S_IWUGO) & + ~(HFSPLUS_SB(sb).umask)); + inode->i_mode = mode; + + HFSPLUS_I(inode).rootflags = perms->rootflags; + HFSPLUS_I(inode).userflags = perms->userflags; + if (perms->rootflags & HFSPLUS_FLG_IMMUTABLE) + inode->i_flags |= S_IMMUTABLE; + else + inode->i_flags &= ~S_IMMUTABLE; + if (perms->rootflags & HFSPLUS_FLG_APPEND) + inode->i_flags |= S_APPEND; + else + inode->i_flags &= ~S_APPEND; +} + +static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) +{ + if (inode->i_flags & S_IMMUTABLE) + perms->rootflags |= HFSPLUS_FLG_IMMUTABLE; + else + perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE; + if (inode->i_flags & S_APPEND) + perms->rootflags |= HFSPLUS_FLG_APPEND; + else + perms->rootflags &= ~HFSPLUS_FLG_APPEND; + perms->userflags = HFSPLUS_I(inode).userflags; + perms->mode = cpu_to_be16(inode->i_mode); + perms->owner = cpu_to_be32(inode->i_uid); + perms->group = cpu_to_be32(inode->i_gid); + perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev); +} + +static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + /* MAY_EXEC is also used for lookup, if no x bit is set allow lookup, + * open_exec has the same test, so it's still not executable, if a x bit + * is set fall back to standard permission check. + */ + if (S_ISREG(inode->i_mode) && mask & MAY_EXEC && !(inode->i_mode & 0111)) + return 0; + return vfs_permission(inode, mask); +} + + +static int hfsplus_file_open(struct inode *inode, struct file *file) +{ + if (HFSPLUS_IS_RSRC(inode)) + inode = HFSPLUS_I(inode).rsrc_inode; + if (atomic_read(&file->f_count) != 1) + return 0; + atomic_inc(&HFSPLUS_I(inode).opencnt); + return 0; +} + +static int hfsplus_file_release(struct inode *inode, struct file *file) +{ + struct super_block *sb = inode->i_sb; + + if (HFSPLUS_IS_RSRC(inode)) + inode = HFSPLUS_I(inode).rsrc_inode; + if (atomic_read(&file->f_count) != 0) + return 0; + if (atomic_dec_and_test(&HFSPLUS_I(inode).opencnt)) { + down(&inode->i_sem); + hfsplus_file_truncate(inode); + if (inode->i_flags & S_DEAD) { + hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); + hfsplus_delete_inode(inode); + } + up(&inode->i_sem); + } + return 0; +} + +extern struct inode_operations hfsplus_dir_inode_operations; +extern struct file_operations hfsplus_dir_operations; + +struct inode_operations hfsplus_file_inode_operations = { + .lookup = hfsplus_file_lookup, + .truncate = hfsplus_file_truncate, + .permission = hfsplus_permission, +}; + +struct file_operations hfsplus_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .fsync = file_fsync, + .open = hfsplus_file_open, + .release = hfsplus_file_release, + .ioctl = hfsplus_ioctl, +}; + +struct inode *hfsplus_new_inode(struct super_block *sb, int mode) +{ + struct inode *inode = new_inode(sb); + if (!inode) + return NULL; + + { + void hfsplus_inode_check(struct super_block *sb); + atomic_inc(&HFSPLUS_SB(sb).inode_cnt); + hfsplus_inode_check(sb); + } + inode->i_ino = HFSPLUS_SB(sb).next_cnid++; + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_nlink = 1; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list); + init_MUTEX(&HFSPLUS_I(inode).extents_lock); + atomic_set(&HFSPLUS_I(inode).opencnt, 0); + HFSPLUS_I(inode).flags = 0; + if (S_ISDIR(inode->i_mode)) { + inode->i_size = 2; + HFSPLUS_SB(sb).folder_count++; + inode->i_op = &hfsplus_dir_inode_operations; + inode->i_fop = &hfsplus_dir_operations; + } else if (S_ISREG(inode->i_mode)) { + HFSPLUS_SB(sb).file_count++; + inode->i_op = &hfsplus_file_inode_operations; + inode->i_fop = &hfsplus_file_operations; + inode->i_mapping->a_ops = &hfsplus_aops; + HFSPLUS_I(inode).clump_blocks = HFSPLUS_SB(sb).data_clump_blocks; + memset(HFSPLUS_I(inode).first_extents, 0, sizeof(hfsplus_extent_rec)); + memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); + HFSPLUS_I(inode).alloc_blocks = 0; + HFSPLUS_I(inode).first_blocks = 0; + HFSPLUS_I(inode).cached_start = 0; + HFSPLUS_I(inode).cached_blocks = 0; + HFSPLUS_I(inode).phys_size = 0; + HFSPLUS_I(inode).rsrc_inode = 0; + } else if (S_ISLNK(inode->i_mode)) { + HFSPLUS_SB(sb).file_count++; + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &hfsplus_aops; + HFSPLUS_I(inode).clump_blocks = 1; + } else + HFSPLUS_SB(sb).file_count++; + insert_inode_hash(inode); + mark_inode_dirty(inode); + sb->s_dirt = 1; + + return inode; +} + +void hfsplus_delete_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + + if (S_ISDIR(inode->i_mode)) { + HFSPLUS_SB(sb).folder_count--; + sb->s_dirt = 1; + return; + } + HFSPLUS_SB(sb).file_count--; + if (S_ISREG(inode->i_mode)) { + if (!inode->i_nlink) { + inode->i_size = 0; + hfsplus_file_truncate(inode); + } + } else if (S_ISLNK(inode->i_mode)) { + inode->i_size = 0; + hfsplus_file_truncate(inode); + } + sb->s_dirt = 1; +} + +void hfsplus_inode_read_fork(struct inode *inode, struct hfsplus_fork_raw *fork) +{ + struct super_block *sb = inode->i_sb; + u32 count; + int i; + + memcpy(&HFSPLUS_I(inode).first_extents, &fork->extents, + sizeof(hfsplus_extent_rec)); + for (count = 0, i = 0; i < 8; i++) + count += be32_to_cpu(fork->extents[i].block_count); + HFSPLUS_I(inode).first_blocks = count; + memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); + HFSPLUS_I(inode).cached_start = 0; + HFSPLUS_I(inode).cached_blocks = 0; + + HFSPLUS_I(inode).alloc_blocks = be32_to_cpu(fork->total_blocks); + inode->i_size = HFSPLUS_I(inode).phys_size = be64_to_cpu(fork->total_size); + inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + HFSPLUS_I(inode).clump_blocks = be32_to_cpu(fork->clump_size) >> HFSPLUS_SB(sb).alloc_blksz_shift; + if (!HFSPLUS_I(inode).clump_blocks) + HFSPLUS_I(inode).clump_blocks = HFSPLUS_IS_RSRC(inode) ? HFSPLUS_SB(sb).rsrc_clump_blocks : + HFSPLUS_SB(sb).data_clump_blocks; +} + +void hfsplus_inode_write_fork(struct inode *inode, struct hfsplus_fork_raw *fork) +{ + memcpy(&fork->extents, &HFSPLUS_I(inode).first_extents, + sizeof(hfsplus_extent_rec)); + fork->total_size = cpu_to_be64(inode->i_size); + fork->total_blocks = cpu_to_be32(HFSPLUS_I(inode).alloc_blocks); +} + +int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) +{ + hfsplus_cat_entry entry; + int res = 0; + u16 type; + + type = hfs_bnode_read_u16(fd->bnode, fd->entryoffset); + + HFSPLUS_I(inode).dev = 0; + inode->i_blksize = PAGE_SIZE; /* Doesn't seem to be useful... */ + if (type == HFSPLUS_FOLDER) { + struct hfsplus_cat_folder *folder = &entry.folder; + + if (fd->entrylength < sizeof(struct hfsplus_cat_folder)) + /* panic? */; + hfs_bnode_read(fd->bnode, &entry, fd->entryoffset, + sizeof(struct hfsplus_cat_folder)); + hfsplus_get_perms(inode, &folder->permissions, 1); + inode->i_nlink = 1; + inode->i_size = 2 + be32_to_cpu(folder->valence); + inode->i_atime = hfsp_mt2ut(folder->access_date); + inode->i_mtime = hfsp_mt2ut(folder->content_mod_date); + inode->i_ctime = inode->i_mtime; + inode->i_blocks = 0; + inode->i_op = &hfsplus_dir_inode_operations; + inode->i_fop = &hfsplus_dir_operations; + } else if (type == HFSPLUS_FILE) { + struct hfsplus_cat_file *file = &entry.file; + + if (fd->entrylength < sizeof(struct hfsplus_cat_file)) + /* panic? */; + hfs_bnode_read(fd->bnode, &entry, fd->entryoffset, + sizeof(struct hfsplus_cat_file)); + + hfsplus_inode_read_fork(inode, HFSPLUS_IS_DATA(inode) ? + &file->data_fork : &file->rsrc_fork); + hfsplus_get_perms(inode, &file->permissions, 0); + inode->i_nlink = 1; + if (S_ISREG(inode->i_mode)) { + if (file->permissions.dev) + inode->i_nlink = be32_to_cpu(file->permissions.dev); + inode->i_op = &hfsplus_file_inode_operations; + inode->i_fop = &hfsplus_file_operations; + inode->i_mapping->a_ops = &hfsplus_aops; + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &hfsplus_aops; + } else { + init_special_inode(inode, inode->i_mode, + be32_to_cpu(file->permissions.dev)); + } + inode->i_atime = hfsp_mt2ut(file->access_date); + inode->i_mtime = hfsp_mt2ut(file->content_mod_date); + inode->i_ctime = inode->i_mtime; + } else { + printk("HFS+-fs: bad catalog entry used to create inode\n"); + res = -EIO; + } + return res; +} + +void hfsplus_cat_write_inode(struct inode *inode) +{ + struct hfs_find_data fd; + hfsplus_cat_entry entry; + + if (HFSPLUS_IS_RSRC(inode)) { + mark_inode_dirty(HFSPLUS_I(inode).rsrc_inode); + return; + } + + if (!inode->i_nlink) + return; + + if (hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd)) + /* panic? */ + return; + + if (hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd)) + /* panic? */ + goto out; + + if (S_ISDIR(inode->i_mode)) { + struct hfsplus_cat_folder *folder = &entry.folder; + + if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) + /* panic? */; + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, + sizeof(struct hfsplus_cat_folder)); + /* simple node checks? */ + hfsplus_set_perms(inode, &folder->permissions); + folder->access_date = hfsp_ut2mt(inode->i_atime); + folder->content_mod_date = hfsp_ut2mt(inode->i_mtime); + folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime); + folder->valence = cpu_to_be32(inode->i_size - 2); + hfs_bnode_write(fd.bnode, &entry, fd.entryoffset, + sizeof(struct hfsplus_cat_folder)); + } else { + struct hfsplus_cat_file *file = &entry.file; + + if (fd.entrylength < sizeof(struct hfsplus_cat_file)) + /* panic? */; + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, + sizeof(struct hfsplus_cat_file)); + hfsplus_inode_write_fork(inode, &file->data_fork); + if (HFSPLUS_I(inode).rsrc_inode) + hfsplus_inode_write_fork(HFSPLUS_I(inode).rsrc_inode, &file->rsrc_fork); + if (S_ISREG(inode->i_mode)) + HFSPLUS_I(inode).dev = inode->i_nlink; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + HFSPLUS_I(inode).dev = kdev_t_to_nr(inode->i_rdev); + hfsplus_set_perms(inode, &file->permissions); + if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE) + file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED); + else + file->flags &= cpu_to_be16(~HFSPLUS_FILE_LOCKED); + file->access_date = hfsp_ut2mt(inode->i_atime); + file->content_mod_date = hfsp_ut2mt(inode->i_mtime); + file->attribute_mod_date = hfsp_ut2mt(inode->i_ctime); + hfs_bnode_write(fd.bnode, &entry, fd.entryoffset, + sizeof(struct hfsplus_cat_file)); + } +out: + hfs_find_exit(&fd); +} diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c new file mode 100644 index 000000000000..440e0dacf627 --- /dev/null +++ b/fs/hfsplus/ioctl.c @@ -0,0 +1,82 @@ +/* + * linux/fs/hfsplus/ioctl.c + * + * Copyright (C) 2003 + * Ethan Benson <erbenson@alaska.net> + * partially derived from linux/fs/ext2/ioctl.c + * Copyright (C) 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * hfsplus ioctls + */ + +#include <linux/fs.h> +#include <linux/sched.h> +#include <asm/uaccess.h> +#include "hfsplus_fs.h" + +int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + unsigned int flags; + + switch (cmd) { + case HFSPLUS_IOC_EXT2_GETFLAGS: + flags = 0; + if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE) + flags |= EXT2_FLAG_IMMUTABLE; /* EXT2_IMMUTABLE_FL */ + if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND) + flags |= EXT2_FLAG_APPEND; /* EXT2_APPEND_FL */ + if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP) + flags |= EXT2_FLAG_NODUMP; /* EXT2_NODUMP_FL */ + return put_user(flags, (int *)arg); + case HFSPLUS_IOC_EXT2_SETFLAGS: { + if (IS_RDONLY(inode)) + return -EROFS; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EACCES; + + if (get_user(flags, (int *)arg)) + return -EFAULT; + + if (flags & (EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND) || + HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) { + if (!capable(CAP_LINUX_IMMUTABLE)) + return -EPERM; + } + + /* don't silently ignore unsupported ext2 flags */ + if (flags & ~(EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND| + EXT2_FLAG_NODUMP)) + return -EOPNOTSUPP; + + if (flags & EXT2_FLAG_IMMUTABLE) { /* EXT2_IMMUTABLE_FL */ + inode->i_flags |= S_IMMUTABLE; + HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE; + } else { + inode->i_flags &= ~S_IMMUTABLE; + HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE; + } + if (flags & EXT2_FLAG_APPEND) { /* EXT2_APPEND_FL */ + inode->i_flags |= S_APPEND; + HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND; + } else { + inode->i_flags &= ~S_APPEND; + HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND; + } + if (flags & EXT2_FLAG_NODUMP) /* EXT2_NODUMP_FL */ + HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP; + else + HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP; + + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + return 0; + } + default: + return -ENOTTY; + } +} diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c new file mode 100644 index 000000000000..20c802ff5db0 --- /dev/null +++ b/fs/hfsplus/options.c @@ -0,0 +1,127 @@ +/* + * linux/fs/hfsplus/options.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Option parsing + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include "hfsplus_fs.h" + +/* Initialize an options object to reasonable defaults */ +void fill_defaults(struct hfsplus_sb_info *opts) +{ + if (!opts) + return; + + opts->creator = HFSPLUS_DEF_CR_TYPE; + opts->type = HFSPLUS_DEF_CR_TYPE; + opts->umask = current->fs->umask; + opts->uid = current->uid; + opts->gid = current->gid; + opts->part = -1; + opts->session = -1; +} + +/* convert a "four byte character" to a 32 bit int with error checks */ +static int fill_fourchar(u32 *result, char *input) +{ + u32 out; + int i; + + if (!result || !input || !*input || (strlen(input) != 4)) + return 0; + + for (out = 0, i = 0; i < 4; i++) { + out <<= 8; + out |= ((int)(input[i])) & 0xFF; + } + *result = out; + return 1; +} + +/* convert a string to int with error checks */ +static int fill_int(int *result, char *input, int base) +{ + char *tmp = input; + int intval; + + if (!result || !input || !*input) + return 0; + + intval = simple_strtoul(tmp, &tmp, base); + if (*tmp) + return 0; + + *result = intval; + return 1; +} + +/* Parse options from mount. Returns 0 on failure */ +/* input is the options passed to mount() as a string */ +int parse_options(char *input, struct hfsplus_sb_info *results) +{ + char *curropt, *value; + int tmp; + + if (!input) + return 1; + + while ((curropt = strsep(&input,",")) != NULL) { + if (!*curropt) + continue; + + if ((value = strchr(curropt, '=')) != NULL) + *value++ = '\0'; + + if (!strcmp(curropt, "creator")) { + if (!fill_fourchar(&(results->creator), value)) { + printk("HFS+-fs: creator requires a 4 character value\n"); + return 0; + } + } else if (!strcmp(curropt, "type")) { + if (!fill_fourchar(&(results->type), value)) { + printk("HFS+-fs: type requires a 4 character value\n"); + return 0; + } + } else if (!strcmp(curropt, "umask")) { + if (!fill_int(&tmp, value, 8)) { + printk("HFS+-fs: umask requires a value\n"); + return 0; + } + results->umask = (umode_t)tmp; + } else if (!strcmp(curropt, "uid")) { + if (!fill_int(&tmp, value, 0)) { + printk("HFS+-fs: uid requires an argument\n"); + return 0; + } + results->uid = (uid_t)tmp; + } else if (!strcmp(curropt, "gid")) { + if (!fill_int(&tmp, value, 0)) { + printk("HFS+-fs: gid requires an argument\n"); + return 0; + } + results->gid = (gid_t)tmp; + } else if (!strcmp(curropt, "part")) { + if (!fill_int(&results->part, value, 0)) { + printk("HFS+-fs: part requires an argument\n"); + return 0; + } + } else if (!strcmp(curropt, "session")) { + if (!fill_int(&results->session, value, 0)) { + printk("HFS+-fs: session requires an argument\n"); + return 0; + } + } else { + printk("HFS+-fs: unknown option %s\n", curropt); + return 0; + } + } + + return 1; +} diff --git a/fs/hfsplus/part_tbl.c b/fs/hfsplus/part_tbl.c new file mode 100644 index 000000000000..3bc137f10351 --- /dev/null +++ b/fs/hfsplus/part_tbl.c @@ -0,0 +1,133 @@ +/* + * linux/fs/hfs/part_tbl.c + * + * Copyright (C) 1996-1997 Paul H. Hargrove + * This file may be distributed under the terms of the GNU General Public License. + * + * Original code to handle the new style Mac partition table based on + * a patch contributed by Holger Schemel (aeglos@valinor.owl.de). + * + * In function preconditions the term "valid" applied to a pointer to + * a structure means that the pointer is non-NULL and the structure it + * points to has all fields initialized to consistent values. + * + */ + +#include "hfsplus_fs.h" + +/* offsets to various blocks */ +#define HFS_DD_BLK 0 /* Driver Descriptor block */ +#define HFS_PMAP_BLK 1 /* First block of partition map */ +#define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */ + +/* magic numbers for various disk blocks */ +#define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */ +#define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */ +#define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */ +#define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */ +#define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */ + +/* + * The new style Mac partition map + * + * For each partition on the media there is a physical block (512-byte + * block) containing one of these structures. These blocks are + * contiguous starting at block 1. + */ +struct new_pmap { + u16 pmSig; /* signature */ + u16 reSigPad; /* padding */ + u32 pmMapBlkCnt; /* partition blocks count */ + u32 pmPyPartStart; /* physical block start of partition */ + u32 pmPartBlkCnt; /* physical block count of partition */ + u8 pmPartName[32]; /* (null terminated?) string + giving the name of this + partition */ + u8 pmPartType[32]; /* (null terminated?) string + giving the type of this + partition */ + /* a bunch more stuff we don't need */ +} __packed; + +/* + * The old style Mac partition map + * + * The partition map consists for a 2-byte signature followed by an + * array of these structures. The map is terminated with an all-zero + * one of these. + */ +struct old_pmap { + u16 pdSig; /* Signature bytes */ + struct old_pmap_entry { + u32 pdStart; + u32 pdSize; + u32 pdFSID; + } pdEntry[42]; +} __packed; + +/* + * hfs_part_find() + * + * Parse the partition map looking for the + * start and length of the 'part'th HFS partition. + */ +int hfs_part_find(struct super_block *sb, + sector_t *part_start, sector_t *part_size) +{ + struct buffer_head *bh; + u16 *data; + int i, size, res; + + res = -ENOENT; + bh = sb_bread512(sb, *part_start + HFS_PMAP_BLK, data); + if (!bh) + return -EIO; + + switch (be16_to_cpu(*data)) { + case HFS_OLD_PMAP_MAGIC: + { + struct old_pmap *pm; + struct old_pmap_entry *p; + + pm = (struct old_pmap *)bh->b_data; + p = pm->pdEntry; + size = 42; + for (i = 0; i < size; p++, i++) { + if (p->pdStart && p->pdSize && + p->pdFSID == cpu_to_be32(0x54465331)/*"TFS1"*/ && + (HFSPLUS_SB(sb).part < 0 || HFSPLUS_SB(sb).part == i)) { + *part_start += be32_to_cpu(p->pdStart); + *part_size = be32_to_cpu(p->pdSize); + res = 0; + } + } + break; + } + case HFS_NEW_PMAP_MAGIC: + { + struct new_pmap *pm; + + pm = (struct new_pmap *)bh->b_data; + size = be32_to_cpu(pm->pmMapBlkCnt); + for (i = 0; i < size;) { + if (!memcmp(pm->pmPartType,"Apple_HFS", 9) && + (HFSPLUS_SB(sb).part < 0 || HFSPLUS_SB(sb).part == i)) { + *part_start += be32_to_cpu(pm->pmPyPartStart); + *part_size = be32_to_cpu(pm->pmPartBlkCnt); + res = 0; + break; + } + brelse(bh); + bh = sb_bread512(sb, *part_start + HFS_PMAP_BLK + ++i, pm); + if (!bh) + return -EIO; + if (pm->pmSig != cpu_to_be16(HFS_NEW_PMAP_MAGIC)) + break; + } + break; + } + } + brelse(bh); + + return res; +} diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c new file mode 100644 index 000000000000..3040a75bb781 --- /dev/null +++ b/fs/hfsplus/super.c @@ -0,0 +1,485 @@ +/* + * linux/fs/hfsplus/super.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pagemap.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <linux/vfs.h> + +static struct inode *hfsplus_alloc_inode(struct super_block *sb); +static void hfsplus_destroy_inode(struct inode *inode); + +#include "hfsplus_fs.h" + +void hfsplus_inode_check(struct super_block *sb) +{ +#if 0 + u32 cnt = atomic_read(&HFSPLUS_SB(sb).inode_cnt); + u32 last_cnt = HFSPLUS_SB(sb).last_inode_cnt; + + if (cnt <= (last_cnt / 2) || + cnt >= (last_cnt * 2)) { + HFSPLUS_SB(sb).last_inode_cnt = cnt; + printk("inode_check: %u,%u,%u\n", cnt, last_cnt, + HFSPLUS_SB(sb).cat_tree ? HFSPLUS_SB(sb).cat_tree->node_hash_cnt : 0); + } +#endif +} + +static void hfsplus_read_inode(struct inode *inode) +{ + struct hfs_find_data fd; + struct hfsplus_vh *vhdr; + int err; + + atomic_inc(&HFSPLUS_SB(inode->i_sb).inode_cnt); + hfsplus_inode_check(inode->i_sb); + INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list); + init_MUTEX(&HFSPLUS_I(inode).extents_lock); + HFSPLUS_I(inode).flags = 0; + HFSPLUS_I(inode).rsrc_inode = NULL; + + if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) { + read_inode: + hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd); + err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); + if (!err) + err = hfsplus_cat_read_inode(inode, &fd); + hfs_find_exit(&fd); + if (err) + goto bad_inode; + return; + } + vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr; + switch(inode->i_ino) { + case HFSPLUS_ROOT_CNID: + goto read_inode; + case HFSPLUS_EXT_CNID: + hfsplus_inode_read_fork(inode, &vhdr->ext_file); + inode->i_mapping->a_ops = &hfsplus_btree_aops; + break; + case HFSPLUS_CAT_CNID: + hfsplus_inode_read_fork(inode, &vhdr->cat_file); + inode->i_mapping->a_ops = &hfsplus_btree_aops; + break; + case HFSPLUS_ALLOC_CNID: + hfsplus_inode_read_fork(inode, &vhdr->alloc_file); + inode->i_mapping->a_ops = &hfsplus_aops; + break; + case HFSPLUS_START_CNID: + hfsplus_inode_read_fork(inode, &vhdr->start_file); + break; + case HFSPLUS_ATTR_CNID: + hfsplus_inode_read_fork(inode, &vhdr->attr_file); + inode->i_mapping->a_ops = &hfsplus_btree_aops; + break; + default: + goto bad_inode; + } + + return; + + bad_inode: + make_bad_inode(inode); +} + +void hfsplus_write_inode(struct inode *inode, int unused) +{ + struct hfsplus_vh *vhdr; + + dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino); + hfsplus_ext_write_extent(inode); + if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) { + hfsplus_cat_write_inode(inode); + return; + } + vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr; + switch (inode->i_ino) { + case HFSPLUS_ROOT_CNID: + hfsplus_cat_write_inode(inode); + break; + case HFSPLUS_EXT_CNID: + if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) { + HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + inode->i_sb->s_dirt = 1; + } + hfsplus_inode_write_fork(inode, &vhdr->ext_file); + hfs_btree_write(HFSPLUS_SB(inode->i_sb).ext_tree); + break; + case HFSPLUS_CAT_CNID: + if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) { + HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + inode->i_sb->s_dirt = 1; + } + hfsplus_inode_write_fork(inode, &vhdr->cat_file); + hfs_btree_write(HFSPLUS_SB(inode->i_sb).cat_tree); + break; + case HFSPLUS_ALLOC_CNID: + if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) { + HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + inode->i_sb->s_dirt = 1; + } + hfsplus_inode_write_fork(inode, &vhdr->alloc_file); + break; + case HFSPLUS_START_CNID: + if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) { + HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + inode->i_sb->s_dirt = 1; + } + hfsplus_inode_write_fork(inode, &vhdr->start_file); + break; + case HFSPLUS_ATTR_CNID: + if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) { + HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + inode->i_sb->s_dirt = 1; + } + hfsplus_inode_write_fork(inode, &vhdr->attr_file); + hfs_btree_write(HFSPLUS_SB(inode->i_sb).attr_tree); + break; + } +} + +static void hfsplus_clear_inode(struct inode *inode) +{ + dprint(DBG_INODE, "hfsplus_clear_inode: %lu\n", inode->i_ino); + atomic_dec(&HFSPLUS_SB(inode->i_sb).inode_cnt); + if (HFSPLUS_IS_RSRC(inode)) { + HFSPLUS_I(HFSPLUS_I(inode).rsrc_inode).rsrc_inode = NULL; + iput(HFSPLUS_I(inode).rsrc_inode); + } + hfsplus_inode_check(inode->i_sb); +} + +static void hfsplus_write_super(struct super_block *sb) +{ + struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr; + + dprint(DBG_SUPER, "hfsplus_write_super\n"); + sb->s_dirt = 0; + if (sb->s_flags & MS_RDONLY) + /* warn? */ + return; + + vhdr->free_blocks = cpu_to_be32(HFSPLUS_SB(sb).free_blocks); + vhdr->next_alloc = cpu_to_be32(HFSPLUS_SB(sb).next_alloc); + vhdr->next_cnid = cpu_to_be32(HFSPLUS_SB(sb).next_cnid); + vhdr->folder_count = cpu_to_be32(HFSPLUS_SB(sb).folder_count); + vhdr->file_count = cpu_to_be32(HFSPLUS_SB(sb).file_count); + + mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh); + if (HFSPLUS_SB(sb).flags & HFSPLUS_SB_WRITEBACKUP) { + if (HFSPLUS_SB(sb).sect_count) { + struct buffer_head *bh; + u32 block, offset; + + block = HFSPLUS_SB(sb).blockoffset; + block += (HFSPLUS_SB(sb).sect_count - 2) >> (sb->s_blocksize_bits - 9); + offset = ((HFSPLUS_SB(sb).sect_count - 2) << 9) & (sb->s_blocksize - 1); + printk("backup: %u,%u,%u,%u\n", HFSPLUS_SB(sb).blockoffset, + HFSPLUS_SB(sb).sect_count, block, offset); + bh = sb_bread(sb, block); + if (bh) { + vhdr = (struct hfsplus_vh *)(bh->b_data + offset); + if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) { + memcpy(vhdr, HFSPLUS_SB(sb).s_vhdr, sizeof(*vhdr)); + mark_buffer_dirty(bh); + brelse(bh); + } else + printk("backup not found!\n"); + } + } + HFSPLUS_SB(sb).flags &= ~HFSPLUS_SB_WRITEBACKUP; + } +} + +static void hfsplus_put_super(struct super_block *sb) +{ + dprint(DBG_SUPER, "hfsplus_put_super\n"); + if (!(sb->s_flags & MS_RDONLY)) { + struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr; + + vhdr->modify_date = hfsp_now2mt(); + vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT); + vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT); + mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh); + ll_rw_block(WRITE, 1, &HFSPLUS_SB(sb).s_vhbh); + wait_on_buffer(HFSPLUS_SB(sb).s_vhbh); + } + + hfs_btree_close(HFSPLUS_SB(sb).cat_tree); + hfs_btree_close(HFSPLUS_SB(sb).ext_tree); + iput(HFSPLUS_SB(sb).alloc_file); + iput(HFSPLUS_SB(sb).hidden_dir); + brelse(HFSPLUS_SB(sb).s_vhbh); +} + +static int hfsplus_statfs(struct super_block *sb, struct kstatfs *buf) +{ + buf->f_type = HFSPLUS_SUPER_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_blocks = HFSPLUS_SB(sb).total_blocks << HFSPLUS_SB(sb).fs_shift; + buf->f_bfree = HFSPLUS_SB(sb).free_blocks << HFSPLUS_SB(sb).fs_shift; + buf->f_bavail = buf->f_bfree; + buf->f_files = 0xFFFFFFFF; + buf->f_ffree = 0xFFFFFFFF - HFSPLUS_SB(sb).next_cnid; + buf->f_namelen = HFSPLUS_MAX_STRLEN; + + return 0; +} + +int hfsplus_remount(struct super_block *sb, int *flags, char *data) +{ + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + return 0; + if (!(*flags & MS_RDONLY)) { + struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr; + + if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_INCNSTNT)) || + !(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { + printk("HFS+-fs warning: Filesystem was not cleanly unmounted, " + "running fsck.hfsplus is recommended. leaving read-only.\n"); + sb->s_flags |= MS_RDONLY; + *flags |= MS_RDONLY; + } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { + printk("HFS+-fs: Filesystem is marked locked, leaving read-only.\n"); + sb->s_flags |= MS_RDONLY; + *flags |= MS_RDONLY; + } + } + return 0; +} + +static struct super_operations hfsplus_sops = { + .alloc_inode = hfsplus_alloc_inode, + .destroy_inode = hfsplus_destroy_inode, + .read_inode = hfsplus_read_inode, + .write_inode = hfsplus_write_inode, + .clear_inode = hfsplus_clear_inode, + .put_super = hfsplus_put_super, + .write_super = hfsplus_write_super, + .statfs = hfsplus_statfs, + .remount_fs = hfsplus_remount, +}; + +static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) +{ + struct hfsplus_vh *vhdr; + struct hfsplus_sb_info *sbi; + hfsplus_cat_entry entry; + struct hfs_find_data fd; + struct qstr str; + int err = -EINVAL; + + sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL); + if (!sbi) { + err = -ENOMEM; + goto out2; + } + memset(sbi, 0, sizeof(HFSPLUS_SB(sb))); + sb->s_fs_info = sbi; + INIT_HLIST_HEAD(&sbi->rsrc_inodes); + fill_defaults(sbi); + if (!parse_options(data, sbi)) { + if (!silent) + printk("HFS+-fs: unable to parse mount options\n"); + err = -EINVAL; + goto out2; + } + + /* Grab the volume header */ + if (hfsplus_read_wrapper(sb)) { + if (!silent) + printk("HFS+-fs: unable to find HFS+ superblock\n"); + err = -EINVAL; + goto out2; + } + vhdr = HFSPLUS_SB(sb).s_vhdr; + + /* Copy parts of the volume header into the superblock */ + sb->s_magic = be16_to_cpu(vhdr->signature); + if (be16_to_cpu(vhdr->version) != HFSPLUS_CURRENT_VERSION) { + if (!silent) + printk("HFS+-fs: wrong filesystem version\n"); + goto cleanup; + } + HFSPLUS_SB(sb).total_blocks = be32_to_cpu(vhdr->total_blocks); + HFSPLUS_SB(sb).free_blocks = be32_to_cpu(vhdr->free_blocks); + HFSPLUS_SB(sb).next_alloc = be32_to_cpu(vhdr->next_alloc); + HFSPLUS_SB(sb).next_cnid = be32_to_cpu(vhdr->next_cnid); + HFSPLUS_SB(sb).file_count = be32_to_cpu(vhdr->file_count); + HFSPLUS_SB(sb).folder_count = be32_to_cpu(vhdr->folder_count); + HFSPLUS_SB(sb).data_clump_blocks = be32_to_cpu(vhdr->data_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift; + if (!HFSPLUS_SB(sb).data_clump_blocks) + HFSPLUS_SB(sb).data_clump_blocks = 1; + HFSPLUS_SB(sb).rsrc_clump_blocks = be32_to_cpu(vhdr->rsrc_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift; + if (!HFSPLUS_SB(sb).rsrc_clump_blocks) + HFSPLUS_SB(sb).rsrc_clump_blocks = 1; + + /* Set up operations so we can load metadata */ + sb->s_op = &hfsplus_sops; + sb->s_maxbytes = MAX_LFS_FILESIZE; + + if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_INCNSTNT)) || + !(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { + if (!silent) + printk("HFS+-fs warning: Filesystem was not cleanly unmounted, " + "running fsck.hfsplus is recommended. mounting read-only.\n"); + sb->s_flags |= MS_RDONLY; + } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { + if (!silent) + printk("HFS+-fs: Filesystem is marked locked, mounting read-only.\n"); + sb->s_flags |= MS_RDONLY; + } + + /* Load metadata objects (B*Trees) */ + HFSPLUS_SB(sb).ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID); + if (!HFSPLUS_SB(sb).ext_tree) { + if (!silent) + printk("HFS+-fs: failed to load extents file\n"); + goto cleanup; + } + HFSPLUS_SB(sb).cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID); + if (!HFSPLUS_SB(sb).cat_tree) { + if (!silent) + printk("HFS+-fs: failed to load catalog file\n"); + goto cleanup; + } + + HFSPLUS_SB(sb).alloc_file = iget(sb, HFSPLUS_ALLOC_CNID); + if (!HFSPLUS_SB(sb).alloc_file) { + if (!silent) + printk("HFS+-fs: failed to load allocation file\n"); + goto cleanup; + } + + /* Load the root directory */ + sb->s_root = d_alloc_root(iget(sb, HFSPLUS_ROOT_CNID)); + if (!sb->s_root) { + if (!silent) + printk("HFS+-fs: failed to load root directory\n"); + goto cleanup; + } + + str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; + str.name = HFSP_HIDDENDIR_NAME; + hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfsplus_cat_build_key(fd.search_key, HFSPLUS_ROOT_CNID, &str); + if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { + hfs_find_exit(&fd); + if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) + goto cleanup; + HFSPLUS_SB(sb).hidden_dir = iget(sb, be32_to_cpu(entry.folder.id)); + if (!HFSPLUS_SB(sb).hidden_dir) + goto cleanup; + } else + hfs_find_exit(&fd); + + if (sb->s_flags & MS_RDONLY) + goto out; + + /* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused + * all three are registered with Apple for our use + */ + vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION); + vhdr->modify_date = hfsp_now2mt(); + vhdr->write_count = cpu_to_be32(be32_to_cpu(vhdr->write_count) + 1); + vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); + vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); + mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh); + ll_rw_block(WRITE, 1, &HFSPLUS_SB(sb).s_vhbh); + wait_on_buffer(HFSPLUS_SB(sb).s_vhbh); + + if (!HFSPLUS_SB(sb).hidden_dir) { + printk("HFS+: create hidden dir...\n"); + HFSPLUS_SB(sb).hidden_dir = hfsplus_new_inode(sb, S_IFDIR); + hfsplus_create_cat(HFSPLUS_SB(sb).hidden_dir->i_ino, sb->s_root->d_inode, + &str, HFSPLUS_SB(sb).hidden_dir); + mark_inode_dirty(HFSPLUS_SB(sb).hidden_dir); + } +out: + return 0; + +cleanup: + hfsplus_put_super(sb); +out2: + return err; +} + +MODULE_AUTHOR("Brad Boyer"); +MODULE_DESCRIPTION("Extended Macintosh Filesystem"); +MODULE_LICENSE("GPL"); + +static kmem_cache_t *hfsplus_inode_cachep; + +static struct inode *hfsplus_alloc_inode(struct super_block *sb) +{ + struct hfsplus_inode_info *i; + + i = kmem_cache_alloc(hfsplus_inode_cachep, SLAB_KERNEL); + return i ? &i->vfs_inode : NULL; +} + +static void hfsplus_destroy_inode(struct inode *inode) +{ + kmem_cache_free(hfsplus_inode_cachep, &HFSPLUS_I(inode)); +} + +#define HFSPLUS_INODE_SIZE sizeof(struct hfsplus_inode_info) + +static struct super_block *hfsplus_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super); +} + +static struct file_system_type hfsplus_fs_type = { + .owner = THIS_MODULE, + .name = "hfsplus", + .get_sb = hfsplus_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static void hfsplus_init_once(void *p, kmem_cache_t *cachep, unsigned long flags) +{ + struct hfsplus_inode_info *i = p; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&i->vfs_inode); +} + +static int __init init_hfsplus_fs(void) +{ + int err; + + hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache", + HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN, + hfsplus_init_once, NULL); + if (!hfsplus_inode_cachep) + return -ENOMEM; + err = register_filesystem(&hfsplus_fs_type); + if (err) + kmem_cache_destroy(hfsplus_inode_cachep); + return err; +} + +static void __exit exit_hfsplus_fs(void) +{ + unregister_filesystem(&hfsplus_fs_type); + if (kmem_cache_destroy(hfsplus_inode_cachep)) + printk(KERN_INFO "hfsplus_inode_cache: not all structures were freed\n"); +} + +module_init(init_hfsplus_fs) +module_exit(exit_hfsplus_fs) diff --git a/fs/hfsplus/tables.c b/fs/hfsplus/tables.c new file mode 100644 index 000000000000..ce2ed6657715 --- /dev/null +++ b/fs/hfsplus/tables.c @@ -0,0 +1,408 @@ +/* + * linux/fs/hfsplus/tables.c + * + * Various data tables + */ + +#include "hfsplus_fs.h" + +/* + * Unicode case folding table taken from Apple Technote #1150 + * (HFS Plus Volume Format) + */ + +u16 case_fold_table[] = { +/* + * The lower case table consists of a 256-entry high-byte table followed by + * some number of 256-entry subtables. The high-byte table contains either an + * offset to the subtable for characters with that high byte or zero, which + * means that there are no case mappings or ignored characters in that block. + * Ignored characters are mapped to zero. + */ + + // High-byte indices ( == 0 iff no case mapping and no ignorables ) + + + /* 0 */ 0x0100, 0x0200, 0x0000, 0x0300, 0x0400, 0x0500, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 1 */ 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 2 */ 0x0700, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0900, 0x0A00, + + // Table 1 (for high byte 0x00) + + /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF, + /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, + + // Table 2 (for high byte 0x01) + + /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, + 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, + /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, + 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, + /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127, + 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, + /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, + 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140, + /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, + 0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, + /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, + 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F, + /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167, + 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F, + /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, + 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, + /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, + 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259, + /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, + 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275, + /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8, + 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF, + /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292, + 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF, + /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9, + 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF, + /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, + 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF, + /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7, + 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF, + /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, + 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF, + + // Table 3 (for high byte 0x03) + + /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, + 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, + /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, + 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, + /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, + 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, + /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, + 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, + /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, + 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, + /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, + 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, + /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, + 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, + /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, + 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F, + /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, + 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F, + /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF, + /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, + 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, + /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7, + 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF, + /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7, + 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, + + // Table 4 (for high byte 0x04) + + /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407, + 0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F, + /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, + 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F, + /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, + 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F, + /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477, + 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F, + /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, + 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, + /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, + 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F, + /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7, + 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF, + /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7, + 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF, + /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8, + 0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF, + /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7, + 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF, + /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7, + 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF, + /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7, + 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, + + // Table 5 (for high byte 0x05) + + /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, + 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F, + /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, + 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F, + /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, + 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F, + /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, + 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, + 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, + 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F, + /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, + 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, + 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, + 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F, + /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, + 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F, + /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, + 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, + /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7, + 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF, + /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF, + /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7, + 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF, + + // Table 6 (for high byte 0x10) + + /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, + 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, + /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, + 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F, + /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, + 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F, + /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, + 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F, + /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, + 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, + /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, + 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F, + /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, + 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F, + /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, + 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F, + /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, + 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F, + /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, + 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F, + /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, + 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, + 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7, + 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF, + /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, + 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, + 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7, + 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF, + + // Table 7 (for high byte 0x20) + + /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, + 0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000, + /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017, + 0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F, + /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027, + 0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F, + /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037, + 0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F, + /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047, + 0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F, + /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057, + 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F, + /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067, + 0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077, + 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F, + /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, + 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F, + /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097, + 0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F, + /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7, + 0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF, + /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7, + 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF, + /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7, + 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF, + /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, + 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF, + /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7, + 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF, + /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7, + 0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF, + + // Table 8 (for high byte 0x21) + + /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, + 0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F, + /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, + 0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F, + /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127, + 0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F, + /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, + 0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F, + /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, + 0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F, + /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, + 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F, + /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, + 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, + /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, + 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, + /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187, + 0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F, + /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, + 0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F, + /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7, + 0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF, + /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7, + 0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF, + /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7, + 0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF, + /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7, + 0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF, + /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7, + 0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF, + /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7, + 0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF, + + // Table 9 (for high byte 0xFE) + + /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07, + 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, + /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17, + 0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F, + /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27, + 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F, + /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37, + 0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F, + /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47, + 0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F, + /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57, + 0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F, + /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67, + 0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F, + /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77, + 0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F, + /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87, + 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F, + /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97, + 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F, + /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7, + 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF, + /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, + 0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF, + /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, + 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF, + /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7, + 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF, + /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7, + 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF, + /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, + 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000, + + // Table 10 (for high byte 0xFF) + + /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07, + 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, + /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, + 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F, + /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, + 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, + 0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F, + /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, + 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, + 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F, + /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, + 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, + /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, + 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F, + /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, + 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F, + /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, + 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F, + /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7, + 0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF, + /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7, + 0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF, + /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7, + 0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF, + /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7, + 0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF, + /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7, + 0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF, + /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7, + 0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, +}; diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c new file mode 100644 index 000000000000..4821cd239d59 --- /dev/null +++ b/fs/hfsplus/unicode.c @@ -0,0 +1,140 @@ +/* + * linux/fs/hfsplus/unicode.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handler routines for unicode strings + */ + +#include <linux/types.h> +#include <linux/nls.h> +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +/* Fold the case of a unicode char, given the 16 bit value */ +/* Returns folded char, or 0 if ignorable */ +static inline u16 case_fold(u16 c) +{ + u16 tmp; + + tmp = case_fold_table[(c>>8)]; + if (tmp) + tmp = case_fold_table[tmp + (c & 0xFF)]; + else + tmp = c; + return tmp; +} + +/* Compare unicode strings, return values like normal strcmp */ +int hfsplus_unistrcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unistr *s2) +{ + u16 len1, len2, c1, c2; + const hfsplus_unichr *p1, *p2; + + len1 = be16_to_cpu(s1->length); + len2 = be16_to_cpu(s2->length); + p1 = s1->unicode; + p2 = s2->unicode; + + while (1) { + c1 = c2 = 0; + + while (len1 && !c1) { + c1 = case_fold(be16_to_cpu(*p1)); + p1++; + len1--; + } + while (len2 && !c2) { + c2 = case_fold(be16_to_cpu(*p2)); + p2++; + len2--; + } + + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + if (!c1 && !c2) + return 0; + } +} + +int hfsplus_uni2asc(const struct hfsplus_unistr *ustr, char *astr, int *len) +{ + const hfsplus_unichr *ip; + u8 *op; + u16 ustrlen, cc; + int size, tmp; + + op = astr; + ip = ustr->unicode; + ustrlen = be16_to_cpu(ustr->length); + tmp = *len; + while (ustrlen > 0 && tmp > 0) { + cc = be16_to_cpu(*ip); + switch (cc) { + case 0: + cc = 0x2400; + break; + case '/': + cc = ':'; + break; + } + if (cc > 0x7f) { + size = utf8_wctomb(op, cc, tmp); + if (size == -1) { + /* ignore */ + } else { + op += size; + tmp -= size; + } + } else { + *op++ = (u8) cc; + tmp--; + } + ip++; + ustrlen--; + } + *len = (char *)op - astr; + if (ustrlen) + return -ENAMETOOLONG; + return 0; +} + +int hfsplus_asc2uni(struct hfsplus_unistr *ustr, const char *astr, int len) +{ + int tmp; + wchar_t c; + u16 outlen = 0; + + while (outlen <= HFSPLUS_MAX_STRLEN && len > 0) { + if (*astr & 0x80) { + tmp = utf8_mbtowc(&c, astr, len); + if (tmp < 0) { + astr++; + len--; + continue; + } else { + astr += tmp; + len -= tmp; + } + } else { + c = *astr++; + len--; + } + switch (c) { + case 0x2400: + c = 0; + break; + case ':': + c = '/'; + break; + } + ustr->unicode[outlen] = cpu_to_be16(c); + outlen++; + } + ustr->length = cpu_to_be16(outlen); + if (len > 0) + return -ENAMETOOLONG; + return 0; +} diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c new file mode 100644 index 000000000000..ca595223a07e --- /dev/null +++ b/fs/hfsplus/wrapper.c @@ -0,0 +1,171 @@ +/* + * linux/fs/hfsplus/wrapper.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies <roman@ardistech.com> + * + * Handling of HFS wrappers around HFS+ volumes + */ + +#include <linux/fs.h> +#include <linux/blkdev.h> +#include <linux/cdrom.h> +#include <linux/genhd.h> +#include <linux/version.h> +#include <asm/unaligned.h> + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +struct hfsplus_wd { + u32 ablk_size; + u16 ablk_start; + u16 embed_start; + u16 embed_count; +}; + +static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) +{ + u32 extent; + u16 attrib; + + if (be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG)) != HFSPLUS_VOLHEAD_SIG) + return 0; + + attrib = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ATTRIB)); + if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) || + !(attrib & HFSP_WRAP_ATTRIB_SPARED)) + return 0; + + wd->ablk_size = be32_to_cpu(*(u32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE)); + if (wd->ablk_size < HFSPLUS_SECTOR_SIZE) + return 0; + if (wd->ablk_size % HFSPLUS_SECTOR_SIZE) + return 0; + wd->ablk_start = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART)); + + extent = be32_to_cpu(get_unaligned((u32 *)(bufptr + HFSP_WRAPOFF_EMBEDEXT))); + wd->embed_start = (extent >> 16) & 0xFFFF; + wd->embed_count = extent & 0xFFFF; + + return 1; +} + +static int hfsplus_get_last_session(struct super_block *sb, + sector_t *start, sector_t *size) +{ + struct cdrom_multisession ms_info; + struct cdrom_tocentry te; + int res; + + /* default values */ + *start = 0; + *size = sb->s_bdev->bd_inode->i_size >> 9; + + if (HFSPLUS_SB(sb).session >= 0) { + te.cdte_track = HFSPLUS_SB(sb).session; + te.cdte_format = CDROM_LBA; + res = ioctl_by_bdev(sb->s_bdev, CDROMREADTOCENTRY, (unsigned long)&te); + if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) { + *start = (sector_t)te.cdte_addr.lba << 2; + return 0; + } + printk(KERN_ERR "HFS: Invalid session number or type of track\n"); + return -EINVAL; + } + ms_info.addr_format = CDROM_LBA; + res = ioctl_by_bdev(sb->s_bdev, CDROMMULTISESSION, (unsigned long)&ms_info); + if (!res && ms_info.xa_flag) + *start = (sector_t)ms_info.addr.lba << 2; + return 0; +} + +/* Find the volume header and fill in some minimum bits in superblock */ +/* Takes in super block, returns true if good data read */ +int hfsplus_read_wrapper(struct super_block *sb) +{ + struct buffer_head *bh; + struct hfsplus_vh *vhdr; + struct hfsplus_wd wd; + sector_t part_start, part_size; + u32 blocksize; + + blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); + if (!blocksize) + return -EINVAL; + + if (hfsplus_get_last_session(sb, &part_start, &part_size)) + return -EINVAL; + while (1) { + bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr); + if (!bh) + return -EIO; + + if (vhdr->signature == cpu_to_be16(HFSP_WRAP_MAGIC)) { + if (!hfsplus_read_mdb(vhdr, &wd)) + goto error; + wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT; + part_start += wd.ablk_start + wd.embed_start * wd.ablk_size; + part_size = wd.embed_count * wd.ablk_size; + brelse(bh); + bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr); + if (!bh) + return -EIO; + } + if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG)) + break; + brelse(bh); + + /* check for a partition block + * (should do this only for cdrom/loop though) + */ + if (hfs_part_find(sb, &part_start, &part_size)) + return -EINVAL; + } + + blocksize = be32_to_cpu(vhdr->blocksize); + brelse(bh); + + /* block size must be at least as large as a sector + * and a multiple of 2 + */ + if (blocksize < HFSPLUS_SECTOR_SIZE || + ((blocksize - 1) & blocksize)) + return -EINVAL; + HFSPLUS_SB(sb).alloc_blksz = blocksize; + HFSPLUS_SB(sb).alloc_blksz_shift = 0; + while (blocksize >>= 1) + HFSPLUS_SB(sb).alloc_blksz_shift++; + blocksize = min(HFSPLUS_SB(sb).alloc_blksz, (u32)PAGE_SIZE); + + /* align block size to block offset */ + while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) + blocksize >>= 1; + + if (sb_set_blocksize(sb, blocksize) != blocksize) { + printk("HFS+: unable to blocksize to %u!\n", blocksize); + return -EINVAL; + } + + HFSPLUS_SB(sb).blockoffset = part_start >> + (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); + HFSPLUS_SB(sb).sect_count = part_size; + HFSPLUS_SB(sb).fs_shift = HFSPLUS_SB(sb).alloc_blksz_shift - + sb->s_blocksize_bits; + + bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr); + if (!bh) + return -EIO; + + /* should still be the same... */ + if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG) + goto error; + HFSPLUS_SB(sb).s_vhbh = bh; + HFSPLUS_SB(sb).s_vhdr = vhdr; + + return 0; + error: + brelse(bh); + return -EINVAL; +} diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index c2f970fb7061..97cd3526636c 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -26,14 +26,7 @@ * maybe other stuff do to. */ -/* Argh. Some architectures have kernel_thread in asm/processor.h - Some have it in unistd.h and you need to define __KERNEL_SYSCALLS__ - Pass me a baseball bat and the person responsible. - dwmw2 -*/ -#define __KERNEL_SYSCALLS__ #include <linux/time.h> -#include <linux/unistd.h> #include <linux/module.h> #include <linux/init.h> diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 6412a0a9e8e5..621231064418 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -11,14 +11,10 @@ * */ -#define __KERNEL_SYSCALLS__ - #include <linux/kernel.h> #include <linux/jffs2.h> #include <linux/mtd/mtd.h> #include <linux/completion.h> -#include <linux/sched.h> -#include <linux/unistd.h> #include <linux/suspend.h> #include "nodelist.h" diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 3ed42dc5b162..6c95574b2cfc 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -6,13 +6,10 @@ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> */ -#define __KERNEL_SYSCALLS__ - #include <linux/module.h> #include <linux/types.h> #include <linux/time.h> #include <linux/nfs_fs.h> -#include <linux/unistd.h> #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/svc.h> #include <linux/lockd/lockd.h> diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 479a8b550046..47c77137b042 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -12,7 +12,6 @@ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */ -#define __KERNEL_SYSCALLS__ #include <linux/config.h> #include <linux/module.h> #include <linux/init.h> @@ -23,7 +22,6 @@ #include <linux/errno.h> #include <linux/in.h> #include <linux/uio.h> -#include <linux/unistd.h> #include <linux/slab.h> #include <linux/smp.h> #include <linux/smp_lock.h> diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index 700104cb5619..d77c3c6c06f2 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -921,11 +921,7 @@ static int openpromfs_unlink (struct inode *dir, struct dentry *dentry) } /* {{{ init section */ -#ifndef MODULE static int __init check_space (u16 n) -#else -static int check_space (u16 n) -#endif { unsigned long pages; @@ -945,11 +941,7 @@ static int check_space (u16 n) return 0; } -#ifndef MODULE static u16 __init get_nodes (u16 parent, u32 node) -#else -static u16 get_nodes (u16 parent, u32 node) -#endif { char *p; u16 n = last_node++, i; @@ -1035,6 +1027,7 @@ static int openprom_fill_super(struct super_block *s, void *data, int silent) { struct inode * root_inode; + s->s_flags |= MS_NODIRATIME; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = OPENPROM_SUPER_MAGIC; diff --git a/fs/super.c b/fs/super.c index 1508d85ec756..a18c0672275f 100644 --- a/fs/super.c +++ b/fs/super.c @@ -329,7 +329,7 @@ restart: * flags again, which will cause process A to resync everything. Fix that with * a local mutex. * - * FIXME: If wait==0, we only really need to call ->sync_fs if s_dirt is true. + * (Fabian) Avoid sync_fs with clean fs & wait mode 0 */ void sync_filesystems(int wait) { @@ -360,7 +360,7 @@ restart: sb->s_count++; spin_unlock(&sb_lock); down_read(&sb->s_umount); - if (sb->s_root) + if (sb->s_root && (wait || sb->s_dirt)) sb->s_op->sync_fs(sb, wait); drop_super(sb); goto restart; @@ -450,6 +450,14 @@ out: return err; } +/** + * mark_files_ro + * @sb: superblock in question + * + * All files are marked read/only. We don't care about pending + * delete files so this should be used in 'force' mode only + */ + static void mark_files_ro(struct super_block *sb) { struct file *f; @@ -482,7 +490,8 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) shrink_dcache_sb(sb); fsync_super(sb); - /* If we are remounting RDONLY, make sure there are no rw files open */ + /* If we are remounting RDONLY and current sb is read/write, + make sure there are no rw files opened */ if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) { if (force) mark_files_ro(sb); diff --git a/include/asm-arm26/cache.h b/include/asm-arm26/cache.h index d95112e028a6..f52ca1b808cd 100644 --- a/include/asm-arm26/cache.h +++ b/include/asm-arm26/cache.h @@ -8,12 +8,4 @@ #define L1_CACHE_ALIGN(x) (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1)) #define SMP_CACHE_BYTES L1_CACHE_BYTES -#ifdef MODULE -#define __cacheline_aligned __attribute__((__aligned__(L1_CACHE_BYTES))) -#else -#define __cacheline_aligned \ - __attribute__((__aligned__(L1_CACHE_BYTES), \ - __section__(".data.cacheline_aligned"))) -#endif - #endif diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index 4b1870a784cb..0d8d6d5a6757 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -655,24 +655,13 @@ ia64_get_dbr (__u64 regnum) return retval; } -/* XXX remove the handcoded version once we have a sufficiently clever compiler... */ -#ifdef SMART_COMPILER -# define ia64_rotr(w,n) \ - ({ \ - __u64 __ia64_rotr_w = (w), _n = (n); \ - \ - (__ia64_rotr_w >> _n) | (__ia64_rotr_w << (64 - _n)); \ - }) -#else -# define ia64_rotr(w,n) \ - ({ \ - __u64 __ia64_rotr_w; \ - __ia64_rotr_w = ia64_shrp((w), (w), (n)); \ - __ia64_rotr_w; \ - }) -#endif +static inline __u64 +ia64_rotr (__u64 w, __u64 n) +{ + return (w >> n) | (w << (64 - n)); +} -#define ia64_rotl(w,n) ia64_rotr((w),(64)-(n)) +#define ia64_rotl(w,n) ia64_rotr((w), (64) - (n)) /* * Take a mapped kernel address and return the equivalent address diff --git a/include/asm-ia64/sn/pci/pcibr.h b/include/asm-ia64/sn/pci/pcibr.h index f65ac2817010..c46911ae6415 100644 --- a/include/asm-ia64/sn/pci/pcibr.h +++ b/include/asm-ia64/sn/pci/pcibr.h @@ -325,9 +325,27 @@ extern int pcibr_asic_rev(vertex_hdl_t); #define PCIBR 'p' #define _PCIBR(x) ((PCIBR << 8) | (x)) -#define PCIBR_SLOT_STARTUP _PCIBR(1) -#define PCIBR_SLOT_SHUTDOWN _PCIBR(2) -#define PCIBR_SLOT_QUERY _PCIBR(3) +/* + * Bit defintions for variable slot_status in struct + * pcibr_soft_slot_s. They are here so that the user + * hot-plug utility can interpret the slot's power + * status. + */ +#ifdef CONFIG_HOTPLUG_PCI_SGI +#define PCI_SLOT_ENABLE_CMPLT 0x01 +#define PCI_SLOT_ENABLE_INCMPLT 0x02 +#define PCI_SLOT_DISABLE_CMPLT 0x04 +#define PCI_SLOT_DISABLE_INCMPLT 0x08 +#define PCI_SLOT_POWER_ON 0x10 +#define PCI_SLOT_POWER_OFF 0x20 +#define PCI_SLOT_IS_SYS_CRITICAL 0x40 +#define PCI_SLOT_PCIBA_LOADED 0x80 + +#define PCI_SLOT_STATUS_MASK (PCI_SLOT_ENABLE_CMPLT | \ + PCI_SLOT_ENABLE_INCMPLT | \ + PCI_SLOT_DISABLE_CMPLT | \ + PCI_SLOT_DISABLE_INCMPLT) +#define PCI_SLOT_POWER_MASK (PCI_SLOT_POWER_ON | PCI_SLOT_POWER_OFF) /* * Bit defintions for variable slot_status in struct @@ -356,26 +374,20 @@ extern int pcibr_asic_rev(vertex_hdl_t); #define FUNC_IS_SYS_CRITICAL 0x02 /* - * Structures for requesting PCI bridge information and receiving a response + * L1 slot power operations for PCI hot-plug */ -typedef struct pcibr_slot_req_s *pcibr_slot_req_t; -typedef struct pcibr_slot_up_resp_s *pcibr_slot_up_resp_t; -typedef struct pcibr_slot_down_resp_s *pcibr_slot_down_resp_t; -typedef struct pcibr_slot_info_resp_s *pcibr_slot_info_resp_t; -typedef struct pcibr_slot_func_info_resp_s *pcibr_slot_func_info_resp_t; +#define PCI_REQ_SLOT_POWER_ON 1 +#define PCI_L1_QSIZE 128 /* our L1 message buffer size */ + #define L1_QSIZE 128 /* our L1 message buffer size */ -struct pcibr_slot_req_s { - int req_slot; - union { - pcibr_slot_up_resp_t up; - pcibr_slot_down_resp_t down; - pcibr_slot_info_resp_t query; - void *any; - } req_respp; - int req_size; + +enum pcibr_slot_disable_action_e { + PCI_REQ_SLOT_ELIGIBLE, + PCI_REQ_SLOT_DISABLE }; + struct pcibr_slot_up_resp_s { int resp_sub_errno; char resp_l1_msg[L1_QSIZE + 1]; @@ -444,6 +456,45 @@ struct pcibr_slot_info_resp_s { } resp_func[8]; }; +struct pcibr_slot_req_s { + int req_slot; + union { + enum pcibr_slot_disable_action_e up; + struct pcibr_slot_down_resp_s *down; + struct pcibr_slot_info_resp_s *query; + void *any; + } req_respp; + int req_size; +}; + +struct pcibr_slot_enable_resp_s { + int resp_sub_errno; + char resp_l1_msg[PCI_L1_QSIZE + 1]; +}; + +struct pcibr_slot_disable_resp_s { + int resp_sub_errno; + char resp_l1_msg[PCI_L1_QSIZE + 1]; +}; + +struct pcibr_slot_enable_req_s { + pciio_slot_t req_device; + struct pcibr_slot_enable_resp_s req_resp; +}; + +struct pcibr_slot_disable_req_s { + pciio_slot_t req_device; + enum pcibr_slot_disable_action_e req_action; + struct pcibr_slot_disable_resp_s req_resp; +}; + +struct pcibr_slot_info_req_s { + pciio_slot_t req_device; + struct pcibr_slot_info_resp_s req_resp; +}; + +#endif /* CONFIG_HOTPLUG_PCI_SGI */ + /* * PCI specific errors, interpreted by pciconfig command diff --git a/include/asm-ia64/sn/pci/pcibr_private.h b/include/asm-ia64/sn/pci/pcibr_private.h index 0bd907d74c58..bd9b395390ec 100644 --- a/include/asm-ia64/sn/pci/pcibr_private.h +++ b/include/asm-ia64/sn/pci/pcibr_private.h @@ -475,6 +475,12 @@ struct pcibr_soft_s { vertex_hdl_t bs_noslot_conn; /* NO-SLOT connection point */ pcibr_info_t bs_noslot_info; + +#ifdef CONFIG_HOTPLUG_PCI_SGI + /* Linux PCI bus structure pointer */ + struct pci_bus *bs_pci_bus; +#endif + struct pcibr_soft_slot_s { /* information we keep about each CFG slot */ @@ -492,9 +498,14 @@ struct pcibr_soft_s { pciio_slot_t host_slot; vertex_hdl_t slot_conn; +#ifdef CONFIG_HOTPLUG_PCI_SGI /* PCI Hot-Plug status word */ int slot_status; + /* PCI Hot-Plug core structure pointer */ + struct hotplug_slot *bss_hotplug_slot; +#endif /* CONFIG_HOTPLUG_PCI_SGI */ + /* Potentially several connection points * for this slot. bss_ninfo is how many, * and bss_infos is a pointer to diff --git a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h index 287a168f526e..906c7c8d0ef4 100644 --- a/include/asm-ia64/sn/sn_sal.h +++ b/include/asm-ia64/sn/sn_sal.h @@ -58,6 +58,7 @@ #define SN_SAL_MEMPROTECT 0x0200003e #define SN_SAL_SYSCTL_FRU_CAPTURE 0x0200003f +#define SN_SAL_SYSCTL_IOBRICK_PCI_OP 0x02000042 // reentrant /* * Service-specific constants @@ -72,6 +73,16 @@ #define SAL_CONSOLE_INTR_XMIT 1 /* output interrupt */ #define SAL_CONSOLE_INTR_RECV 2 /* input interrupt */ +#ifdef CONFIG_HOTPLUG_PCI_SGI +/* power up / power down / reset a PCI slot or bus */ +#define SAL_SYSCTL_PCI_POWER_UP 0 +#define SAL_SYSCTL_PCI_POWER_DOWN 1 +#define SAL_SYSCTL_PCI_RESET 2 + +/* what type of I/O brick? */ +#define SAL_SYSCTL_IO_XTALK 0 /* connected via a compute node */ + +#endif /* CONFIG_HOTPLUG_PCI_SGI */ /* * SN_SAL_GET_PARTITION_ADDR return constants @@ -641,4 +652,22 @@ ia64_sn_fru_capture(void) return isrv.v0; } +/* + * Performs an operation on a PCI bus or slot -- power up, power down + * or reset. + */ +static inline u64 +ia64_sn_sysctl_iobrick_pci_op(nasid_t n, u64 connection_type, + u64 bus, slotid_t slot, + u64 action) +{ + struct ia64_sal_retval rv = {0, 0, 0, 0}; + + SAL_CALL_NOLOCK(rv, SN_SAL_SYSCTL_IOBRICK_PCI_OP, connection_type, n, action, + bus, (u64) slot, 0, 0); + if (rv.status) + return rv.v0; + return 0; +} + #endif /* _ASM_IA64_SN_SN_SAL_H */ diff --git a/include/asm-m68knommu/machdep.h b/include/asm-m68knommu/machdep.h index 697d878b3aaf..5a9f9c297f79 100644 --- a/include/asm-m68knommu/machdep.h +++ b/include/asm-m68knommu/machdep.h @@ -2,6 +2,7 @@ #define _M68KNOMMU_MACHDEP_H #include <linux/seq_file.h> +#include <linux/interrupt.h> struct pt_regs; struct kbd_repeat; @@ -10,14 +11,14 @@ struct hwclk_time; struct gendisk; struct buffer_head; -extern void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *)); +extern void (*mach_sched_init) (irqreturn_t (*handler)(int, void *, struct pt_regs *)); /* machine dependent keyboard functions */ extern int (*mach_keyb_init) (void); extern int (*mach_kbdrate) (struct kbd_repeat *); extern void (*mach_kbd_leds) (unsigned int); /* machine dependent irq functions */ extern void (*mach_init_IRQ) (void); -extern void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *); +extern irqreturn_t (*(*mach_default_handler)[]) (int, void *, struct pt_regs *); extern int (*mach_request_irq) (unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id); extern void (*mach_free_irq) (unsigned int irq, void *dev_id); diff --git a/include/asm-ppc/cache.h b/include/asm-ppc/cache.h index 6a3f4c05dc67..1fcf0f3e7b87 100644 --- a/include/asm-ppc/cache.h +++ b/include/asm-ppc/cache.h @@ -30,14 +30,6 @@ #define L1_CACHE_ALIGN(x) (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1)) #define L1_CACHE_PAGES 8 -#ifdef MODULE -#define __cacheline_aligned __attribute__((__aligned__(L1_CACHE_BYTES))) -#else -#define __cacheline_aligned \ - __attribute__((__aligned__(L1_CACHE_BYTES), \ - __section__(".data.cacheline_aligned"))) -#endif - #ifndef __ASSEMBLY__ extern void clean_dcache_range(unsigned long start, unsigned long stop); extern void flush_dcache_range(unsigned long start, unsigned long stop); diff --git a/include/asm-s390/byteorder.h b/include/asm-s390/byteorder.h index 43b0645bacd8..91a6f3893bcd 100644 --- a/include/asm-s390/byteorder.h +++ b/include/asm-s390/byteorder.h @@ -67,7 +67,7 @@ static __inline__ __u32 ___arch__swab32(__u32 x) __asm__ __volatile__ ( " lrvr %0,%1" - : "=d" (result) : "d" (x), "m" (x) ); + : "=d" (result) : "d" (x) ); return result; #endif /* __s390x__ */ } diff --git a/include/asm-s390/ccwgroup.h b/include/asm-s390/ccwgroup.h index 373160d73160..d626676ddf08 100644 --- a/include/asm-s390/ccwgroup.h +++ b/include/asm-s390/ccwgroup.h @@ -10,6 +10,7 @@ struct ccwgroup_device { CCWGROUP_OFFLINE, CCWGROUP_ONLINE, } state; + atomic_t onoff; unsigned int count; /* number of attached slave devices */ struct device dev; /* master device */ struct ccw_device *cdev[0]; /* variable number, allocate as needed */ diff --git a/include/asm-s390/cmb.h b/include/asm-s390/cmb.h new file mode 100644 index 000000000000..1bfe2bd630b5 --- /dev/null +++ b/include/asm-s390/cmb.h @@ -0,0 +1,98 @@ +#ifndef S390_CMB_H +#define S390_CMB_H +/** + * struct cmbdata -- channel measurement block data for user space + * + * @size: size of the stored data + * @ssch_rsch_count: XXX + * @sample_count: + * @device_connect_time: + * @function_pending_time: + * @device_disconnect_time: + * @control_unit_queuing_time: + * @device_active_only_time: + * @device_busy_time: + * @initial_command_response_time: + * + * all values are stored as 64 bit for simplicity, especially + * in 32 bit emulation mode. All time values are normalized to + * nanoseconds. + * Currently, two formats are known, which differ by the size of + * this structure, i.e. the last two members are only set when + * the extended channel measurement facility (first shipped in + * z990 machines) is activated. + * Potentially, more fields could be added, which results in a + * new ioctl number. + **/ +struct cmbdata { + __u64 size; + __u64 elapsed_time; + /* basic and exended format: */ + __u64 ssch_rsch_count; + __u64 sample_count; + __u64 device_connect_time; + __u64 function_pending_time; + __u64 device_disconnect_time; + __u64 control_unit_queuing_time; + __u64 device_active_only_time; + /* extended format only: */ + __u64 device_busy_time; + __u64 initial_command_response_time; +}; + +/* enable channel measurement */ +#define BIODASDCMFENABLE _IO(DASD_IOCTL_LETTER,32) +/* enable channel measurement */ +#define BIODASDCMFDISABLE _IO(DASD_IOCTL_LETTER,33) +/* reset channel measurement block */ +#define BIODASDRESETCMB _IO(DASD_IOCTL_LETTER,34) +/* read channel measurement data */ +#define BIODASDREADCMB _IOWR(DASD_IOCTL_LETTER,32,u64) +/* read channel measurement data */ +#define BIODASDREADALLCMB _IOWR(DASD_IOCTL_LETTER,33,struct cmbdata) + +#ifdef __KERNEL__ + +/** + * enable_cmf() - switch on the channel measurement for a specific device + * @cdev: The ccw device to be enabled + * returns 0 for success or a negative error value. + * + * Context: + * non-atomic + **/ +extern int enable_cmf(struct ccw_device *cdev); + +/** + * disable_cmf() - switch off the channel measurement for a specific device + * @cdev: The ccw device to be disabled + * returns 0 for success or a negative error value. + * + * Context: + * non-atomic + **/ +extern int disable_cmf(struct ccw_device *cdev); + +/** + * cmf_read() - read one value from the current channel measurement block + * @cmf: the channel to be read + * @index: the name of the value that is read + * + * Context: + * any + **/ + +extern u64 cmf_read(struct ccw_device *cdev, int index); +/** + * cmf_readall() - read one value from the current channel measurement block + * @cmf: the channel to be read + * @data: a pointer to a data block that will be filled + * + * Context: + * any + **/ +extern int cmf_readall(struct ccw_device *cdev, struct cmbdata*data); +extern void cmf_reset(struct ccw_device *cdev); + +#endif /* __KERNEL__ */ +#endif /* S390_CMB_H */ diff --git a/include/asm-s390/debug.h b/include/asm-s390/debug.h index 2b3964b86283..c266fe9fa0aa 100644 --- a/include/asm-s390/debug.h +++ b/include/asm-s390/debug.h @@ -35,11 +35,7 @@ struct __debug_entry{ #ifdef __KERNEL__ #include <linux/version.h> -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) - #include <asm/spinlock.h> -#else - #include <linux/spinlock.h> -#endif /* LINUX_VERSION_CODE */ +#include <linux/spinlock.h> #include <linux/kernel.h> #include <linux/time.h> #include <linux/proc_fs.h> diff --git a/include/asm-s390/dma-mapping.h b/include/asm-s390/dma-mapping.h index e60fba436834..87edaa9531b3 100644 --- a/include/asm-s390/dma-mapping.h +++ b/include/asm-s390/dma-mapping.h @@ -8,4 +8,18 @@ #ifndef _ASM_DMA_MAPPING_H #define _ASM_DMA_MAPPING_H + +static inline void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int flag) +{ + BUG(); + return 0; +} + +static inline void dma_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + BUG(); +} + #endif /* _ASM_DMA_MAPPING_H */ diff --git a/include/asm-s390/extmem.h b/include/asm-s390/extmem.h new file mode 100644 index 000000000000..3a145e182fb1 --- /dev/null +++ b/include/asm-s390/extmem.h @@ -0,0 +1,19 @@ +/* + * include/asm-s390x/extmem.h + * + * definitions for external memory segment support + * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + */ + +#ifndef _ASM_S390X_DCSS_H +#define _ASM_S390X_DCSS_H +#ifndef __ASSEMBLY__ +#define SEGMENT_SHARED_RW 0 +#define SEGMENT_SHARED_RO 1 +#define SEGMENT_EXCLUSIVE_RW 2 +#define SEGMENT_EXCLUSIVE_RO 3 +extern int segment_load (char *name,int segtype,unsigned long *addr,unsigned long *length); +extern void segment_unload(char *name); +extern void segment_replace(char *name); +#endif +#endif diff --git a/include/asm-s390/idals.h b/include/asm-s390/idals.h index 890bc145f9dd..ad444dc74b9b 100644 --- a/include/asm-s390/idals.h +++ b/include/asm-s390/idals.h @@ -228,7 +228,7 @@ idal_buffer_to_user(struct idal_buffer *ib, void *to, size_t count) left = copy_to_user(to, ib->data[i], IDA_BLOCK_SIZE); if (left) return left + count - IDA_BLOCK_SIZE; - (addr_t) to += IDA_BLOCK_SIZE; + to = (void *) to + IDA_BLOCK_SIZE; count -= IDA_BLOCK_SIZE; } return copy_to_user(to, ib->data[i], count); @@ -248,7 +248,7 @@ idal_buffer_from_user(struct idal_buffer *ib, const void *from, size_t count) left = copy_from_user(ib->data[i], from, IDA_BLOCK_SIZE); if (left) return left + count - IDA_BLOCK_SIZE; - (addr_t) from += IDA_BLOCK_SIZE; + from = (void *) from + IDA_BLOCK_SIZE; count -= IDA_BLOCK_SIZE; } return copy_from_user(ib->data[i], from, count); diff --git a/include/asm-s390/pgalloc.h b/include/asm-s390/pgalloc.h index 39697ffa96f0..ec34d58cf463 100644 --- a/include/asm-s390/pgalloc.h +++ b/include/asm-s390/pgalloc.h @@ -21,6 +21,8 @@ #define check_pgt_cache() do {} while (0) +extern void diag10(unsigned long addr); + /* * Allocate and free page tables. The xxx_kernel() versions are * used to allocate a kernel page table - this turns on ASN bits diff --git a/include/asm-s390/qdio.h b/include/asm-s390/qdio.h index 5e8eee3ed6b0..0ddf0a8ef8de 100644 --- a/include/asm-s390/qdio.h +++ b/include/asm-s390/qdio.h @@ -17,6 +17,7 @@ #include <linux/interrupt.h> #include <asm/cio.h> +#include <asm/ccwdev.h> #define QDIO_NAME "qdio " diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h index 1b9e99a994ee..7e613240088e 100644 --- a/include/asm-s390/smp.h +++ b/include/asm-s390/smp.h @@ -29,6 +29,9 @@ typedef struct __u16 cpu; } sigp_info; +extern int smp_call_function_on(void (*func) (void *info), void *info, + int nonatomic, int wait, int cpu); + extern cpumask_t cpu_online_map; extern cpumask_t cpu_possible_map; @@ -61,4 +64,9 @@ extern __inline__ __u16 hard_smp_processor_id(void) #define cpu_logical_map(cpu) (cpu) #endif + +#ifndef CONFIG_SMP +#define smp_call_function_on(func,info,nonatomic,wait,cpu) ({ 0; }) +#endif + #endif diff --git a/include/asm-s390/timer.h b/include/asm-s390/timer.h new file mode 100644 index 000000000000..bb49c6999055 --- /dev/null +++ b/include/asm-s390/timer.h @@ -0,0 +1,50 @@ +/* + * include/asm-s390/timer.h + * + * (C) Copyright IBM Corp. 2003 + * Virtual CPU timer + * + * Author: Jan Glauber (jang@de.ibm.com) + */ + +#ifndef _ASM_S390_TIMER_H +#define _ASM_S390_TIMER_H + +#include <linux/timer.h> + +#define VTIMER_MAX_SLICE (0x7ffffffffffff000LL) + +struct vtimer_list { + struct list_head entry; + + int cpu; + __u64 expires; + __u64 interval; + + spinlock_t lock; + unsigned long magic; + + void (*function)(unsigned long, struct pt_regs*); + unsigned long data; +}; + +/* the offset value will wrap after ca. 71 years */ +struct vtimer_queue { + struct list_head list; + spinlock_t lock; + __u64 to_expire; /* current event expire time */ + __u64 offset; /* list offset to zero */ + __u64 idle; /* temp var for idle */ +}; + +void set_vtimer(__u64 expires); + +extern void init_virt_timer(struct vtimer_list *timer); +extern void add_virt_timer(void *new); +extern void add_virt_timer_periodic(void *new); +extern int mod_virt_timer(struct vtimer_list *timer, __u64 expires); +extern int del_virt_timer(struct vtimer_list *timer); + +int stop_timers(void); + +#endif diff --git a/include/asm-s390/unistd.h b/include/asm-s390/unistd.h index 1c70b1c69754..0b364bd416ae 100644 --- a/include/asm-s390/unistd.h +++ b/include/asm-s390/unistd.h @@ -533,7 +533,6 @@ static inline _syscall1(int,dup,int,fd) static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) static inline _syscall3(int,open,const char *,file,int,flag,int,mode) static inline _syscall1(int,close,int,fd) -static inline _syscall1(int,_exit,int,exitcode) static inline _syscall2(long,stat,char *,filename,struct stat *,statbuf) static inline pid_t waitpid(int pid, int *wait_stat, int flags) diff --git a/include/asm-sh/cache.h b/include/asm-sh/cache.h index f8cf61f9869a..9decb1ced217 100644 --- a/include/asm-sh/cache.h +++ b/include/asm-sh/cache.h @@ -21,14 +21,6 @@ #define L1_CACHE_ALIGN(x) (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1)) -#ifdef MODULE -#define __cacheline_aligned __attribute__((__aligned__(L1_CACHE_BYTES))) -#else -#define __cacheline_aligned \ - __attribute__((__aligned__(L1_CACHE_BYTES), \ - __section__(".data.cacheline_aligned"))) -#endif - #define L1_CACHE_SHIFT_MAX 5 /* largest L1 which this arch supports */ struct cache_info { diff --git a/include/asm-sparc/cache.h b/include/asm-sparc/cache.h index 86fd491b42a6..e6316fd7e1a4 100644 --- a/include/asm-sparc/cache.h +++ b/include/asm-sparc/cache.h @@ -17,14 +17,6 @@ #define SMP_CACHE_BYTES 32 -#ifdef MODULE -#define __cacheline_aligned __attribute__((__aligned__(SMP_CACHE_BYTES))) -#else -#define __cacheline_aligned \ - __attribute__((__aligned__(SMP_CACHE_BYTES), \ - __section__(".data.cacheline_aligned"))) -#endif - /* Direct access to the instruction cache is provided through and * alternate address space. The IDC bit must be off in the ICCR on * HyperSparcs for these accesses to work. The code below does not do diff --git a/include/asm-sparc64/cache.h b/include/asm-sparc64/cache.h index c4ba581b7af1..ade5ec3bfd5a 100644 --- a/include/asm-sparc64/cache.h +++ b/include/asm-sparc64/cache.h @@ -14,12 +14,4 @@ #define SMP_CACHE_BYTES_SHIFT 6 #define SMP_CACHE_BYTES (1 << SMP_CACHE_BYTES_SHIFT) /* L2 cache line size. */ -#ifdef MODULE -#define __cacheline_aligned __attribute__((__aligned__(SMP_CACHE_BYTES))) -#else -#define __cacheline_aligned \ - __attribute__((__aligned__(SMP_CACHE_BYTES), \ - __section__(".data.cacheline_aligned"))) -#endif - #endif diff --git a/include/linux/cache.h b/include/linux/cache.h index 3db3832f35cb..4d767b93738a 100644 --- a/include/linux/cache.h +++ b/include/linux/cache.h @@ -26,13 +26,9 @@ #endif #ifndef __cacheline_aligned -#ifdef MODULE -#define __cacheline_aligned ____cacheline_aligned -#else #define __cacheline_aligned \ __attribute__((__aligned__(SMP_CACHE_BYTES), \ __section__(".data.cacheline_aligned"))) -#endif #endif /* __cacheline_aligned */ #ifndef __cacheline_aligned_in_smp diff --git a/include/linux/dvb/video.h b/include/linux/dvb/video.h index 6727f422da61..09a4286fb02b 100644 --- a/include/linux/dvb/video.h +++ b/include/linux/dvb/video.h @@ -81,9 +81,11 @@ typedef enum { struct video_event { int32_t type; #define VIDEO_EVENT_SIZE_CHANGED 1 +#define VIDEO_EVENT_FRAME_RATE_CHANGED 2 time_t timestamp; union { video_size_t size; + unsigned int frame_rate; /* in frames per 1000sec */ } u; }; @@ -194,6 +196,7 @@ typedef uint16_t video_attributes_t; #define VIDEO_GET_NAVI _IOR('o', 52, video_navi_pack_t) #define VIDEO_SET_ATTRIBUTES _IO('o', 53) #define VIDEO_GET_SIZE _IOR('o', 55, video_size_t) +#define VIDEO_GET_FRAME_RATE _IOR('o', 56, unsigned int) #endif /*_DVBVIDEO_H_*/ diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 416017dc3927..462f7234695f 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -38,12 +38,6 @@ extern int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh); extern int eth_header_parse(struct sk_buff *skb, unsigned char *haddr); -extern struct net_device *__init_etherdev(struct net_device *dev, int sizeof_priv); -static inline struct net_device *init_etherdev(struct net_device *dev, - int sizeof_priv) -{ - return __init_etherdev(dev, sizeof_priv); -} extern struct net_device *alloc_etherdev(int sizeof_priv); static inline void eth_copy_and_sum (struct sk_buff *dest, unsigned char *src, int len, int base) diff --git a/include/linux/hfs_fs.h b/include/linux/hfs_fs.h deleted file mode 100644 index 6cddc472d859..000000000000 --- a/include/linux/hfs_fs.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - * linux/include/linux/hfs_fs.h - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * The source code distribution of the Columbia AppleTalk Package for - * UNIX, version 6.0, (CAP) was used as a specification of the - * location and format of files used by CAP's Aufs. No code from CAP - * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in - * the sense of intellectual property law. - * - * The source code distributions of Netatalk, versions 1.3.3b2 and - * 1.4b2, were used as a specification of the location and format of - * files used by Netatalk's afpd. No code from Netatalk appears in - * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the - * sense of intellectual property law. - */ - -#ifndef _LINUX_HFS_FS_H -#define _LINUX_HFS_FS_H - -#include <linux/hfs_sysdep.h> - -/* magic numbers for Apple Double header files */ -#define HFS_DBL_MAGIC 0x00051607 -#define HFS_SNGL_MAGIC 0x00051600 -#define HFS_HDR_VERSION_1 0x00010000 -#define HFS_HDR_VERSION_2 0x00020000 - -/* magic numbers for various internal structures */ -#define HFS_INO_MAGIC 0x4821 -#define HFS_SB_MAGIC 0x4822 - -/* The space used for the AppleDouble or AppleSingle headers */ -#define HFS_DBL_HDR_LEN 1024 - -/* The space used for the Netatalk header */ -#define HFS_NAT_HDR_LEN 1024 /* 589 for an exact match */ - -/* Macros to extract CNID and file "type" from the Linux inode number */ -#define HFS_CNID(X) ((X) & 0x3FFFFFFF) -#define HFS_ITYPE(X) ((X) & 0xC0000000) - -/* Macros to enumerate types */ -#define HFS_ITYPE_TO_INT(X) ((X) >> 30) -#define HFS_INT_TO_ITYPE(X) ((X) << 30) - -/* generic ITYPEs */ -#define HFS_ITYPE_0 0x00000000 -#define HFS_ITYPE_1 0x40000000 -#define HFS_ITYPE_2 0x80000000 -#define HFS_ITYPE_3 0xC0000000 -#define HFS_ITYPE_NORM HFS_ITYPE_0 /* "normal" directory or file */ - -/* ITYPEs for CAP */ -#define HFS_CAP_NORM HFS_ITYPE_0 /* data fork or normal directory */ -#define HFS_CAP_DATA HFS_ITYPE_0 /* data fork of file */ -#define HFS_CAP_NDIR HFS_ITYPE_0 /* normal directory */ -#define HFS_CAP_FNDR HFS_ITYPE_1 /* finder info for file or dir */ -#define HFS_CAP_RSRC HFS_ITYPE_2 /* resource fork of file */ -#define HFS_CAP_RDIR HFS_ITYPE_2 /* .resource directory */ -#define HFS_CAP_FDIR HFS_ITYPE_3 /* .finderinfo directory */ - -/* ITYPEs for Apple Double */ -#define HFS_DBL_NORM HFS_ITYPE_0 /* data fork or directory */ -#define HFS_DBL_DATA HFS_ITYPE_0 /* data fork of file */ -#define HFS_DBL_DIR HFS_ITYPE_0 /* directory */ -#define HFS_DBL_HDR HFS_ITYPE_1 /* AD header of file or dir */ - -/* ITYPEs for netatalk */ -#define HFS_NAT_NORM HFS_ITYPE_0 /* data fork or directory */ -#define HFS_NAT_DATA HFS_ITYPE_0 /* data fork of file */ -#define HFS_NAT_NDIR HFS_ITYPE_0 /* normal directory */ -#define HFS_NAT_HDR HFS_ITYPE_1 /* AD header of file or dir */ -#define HFS_NAT_HDIR HFS_ITYPE_2 /* directory holding AD headers */ - -/* ITYPEs for Apple Single */ -#define HFS_SGL_NORM HFS_ITYPE_0 /* AppleSingle file or directory */ -#define HFS_SGL_SNGL HFS_ITYPE_0 /* AppleSingle file */ -#define HFS_SGL_DIR HFS_ITYPE_0 /* directory */ -#define HFS_SGL_DINF HFS_ITYPE_1 /* %DirInfo for directory */ - -/* IDs for elements of an AppleDouble or AppleSingle header */ -#define HFS_HDR_DATA 1 /* data fork */ -#define HFS_HDR_RSRC 2 /* resource fork */ -#define HFS_HDR_FNAME 3 /* full (31-character) name */ -#define HFS_HDR_COMNT 4 /* comment */ -#define HFS_HDR_BWICN 5 /* b/w icon */ -#define HFS_HDR_CICON 6 /* color icon info */ -#define HFS_HDR_OLDI 7 /* old file info */ -#define HFS_HDR_DATES 8 /* file dates info */ -#define HFS_HDR_FINFO 9 /* Finder info */ -#define HFS_HDR_MACI 10 /* Macintosh info */ -#define HFS_HDR_PRODOSI 11 /* ProDOS info */ -#define HFS_HDR_MSDOSI 12 /* MSDOS info */ -#define HFS_HDR_SNAME 13 /* short name */ -#define HFS_HDR_AFPI 14 /* AFP file info */ -#define HFS_HDR_DID 15 /* directory id */ -#define HFS_HDR_MAX 16 - -/* - * There are three time systems. All three are based on seconds since - * a particular time/date. - * Unix: unsigned lil-endian since 00:00 GMT, Jan. 1, 1970 - * mac: unsigned big-endian since 00:00 GMT, Jan. 1, 1904 - * header: SIGNED big-endian since 00:00 GMT, Jan. 1, 2000 - * - */ -#define hfs_h_to_mtime(ARG) htonl((hfs_s32)ntohl(ARG)+3029529600U) -#define hfs_m_to_htime(ARG) ((hfs_s32)htonl(ntohl(ARG)-3029529600U)) -#define hfs_h_to_utime(ARG) ((hfs_s32)hfs_to_utc(ntohl(ARG)+946684800U)) -#define hfs_u_to_htime(ARG) ((hfs_s32)htonl(hfs_from_utc(ARG)-946684800U)) -#define hfs_u_to_mtime(ARG) htonl(hfs_from_utc(ARG)+2082844800U) -#define hfs_m_to_utime(ARG) (hfs_to_utc(ntohl(ARG)-2082844800U)) - -/*======== Data structures kept in memory ========*/ - -/* - * A descriptor for a single entry within the header of an - * AppleDouble or AppleSingle header file. - * An array of these make up a table of contents for the file. - */ -struct hfs_hdr_descr { - hfs_u32 id; /* The Apple assigned ID for the entry type */ - hfs_u32 offset; /* The offset to reach the entry */ - hfs_u32 length; /* The length of the entry */ -}; - -/* - * The info needed to reconstruct a given header layout - */ -struct hfs_hdr_layout { - hfs_u32 magic; /* AppleSingle or AppleDouble */ - hfs_u32 version; /* 0x00010000 or 0x00020000 */ - hfs_u16 entries; /* How many entries used */ - struct hfs_hdr_descr - descr[HFS_HDR_MAX]; /* Descriptors */ - struct hfs_hdr_descr - *order[HFS_HDR_MAX]; /* 'descr' ordered by offset */ -}; - -/* header layout for netatalk's v1 appledouble file format */ -struct hfs_nat_hdr { - hfs_lword_t magic; - hfs_lword_t version; - hfs_byte_t homefs[16]; - hfs_word_t entries; - hfs_byte_t descrs[12*5]; - hfs_byte_t real_name[255]; /* id=3 */ - hfs_byte_t comment[200]; /* id=4 XXX: not yet implemented */ - hfs_byte_t old_info[16]; /* id=7 */ - hfs_u8 finderinfo[32]; /* id=9 */ -}; - -/* - * Default header layout for Netatalk and AppleDouble - */ -struct hfs_dbl_hdr { - hfs_lword_t magic; - hfs_lword_t version; - hfs_byte_t filler[16]; - hfs_word_t entries; - hfs_byte_t descrs[12*HFS_HDR_MAX]; - hfs_byte_t real_name[255]; /* id=3 */ - hfs_byte_t comment[200]; /* id=4 XXX: not yet implemented */ - hfs_u32 create_time; /* \ */ - hfs_u32 modify_time; /* | id=8 (or 7) */ - hfs_u32 backup_time; /* | */ - hfs_u32 access_time; /* / (attributes with id=7) */ - hfs_u8 finderinfo[32]; /* id=9 */ - hfs_u32 fileinfo; /* id=10 */ - hfs_u32 cnid; /* id=15 */ - hfs_u8 short_name[12]; /* id=13 */ - hfs_u8 prodosi[8]; /* id=11 */ -}; - - -/* finder metadata for CAP */ -struct hfs_cap_info { - hfs_byte_t fi_fndr[32]; /* Finder's info */ - hfs_word_t fi_attr; /* AFP attributes (f=file/d=dir) */ -#define HFS_AFP_INV 0x001 /* Invisible bit (f/d) */ -#define HFS_AFP_EXPFOLDER 0x002 /* exported folder (d) */ -#define HFS_AFP_MULTI 0x002 /* Multiuser bit (f) */ -#define HFS_AFP_SYS 0x004 /* System bit (f/d) */ -#define HFS_AFP_DOPEN 0x008 /* data fork already open (f) */ -#define HFS_AFP_MOUNTED 0x008 /* mounted folder (d) */ -#define HFS_AFP_ROPEN 0x010 /* resource fork already open (f) */ -#define HFS_AFP_INEXPFOLDER 0x010 /* folder in shared area (d) */ -#define HFS_AFP_WRI 0x020 /* Write inhibit bit (readonly) (f) */ -#define HFS_AFP_BACKUP 0x040 /* backup needed bit (f/d) */ -#define HFS_AFP_RNI 0x080 /* Rename inhibit bit (f/d) */ -#define HFS_AFP_DEI 0x100 /* Delete inhibit bit (f/d) */ -#define HFS_AFP_NOCOPY 0x400 /* Copy protect bit (f) */ -#define HFS_AFP_RDONLY ( HFS_AFP_WRI|HFS_AFP_RNI|HFS_AFP_DEI) - hfs_byte_t fi_magic1; /* Magic number: */ -#define HFS_CAP_MAGIC1 0xFF - hfs_byte_t fi_version; /* Version of this structure: */ -#define HFS_CAP_VERSION 0x10 - hfs_byte_t fi_magic; /* Another magic number: */ -#define HFS_CAP_MAGIC 0xDA - hfs_byte_t fi_bitmap; /* Bitmap of which names are valid: */ -#define HFS_CAP_SHORTNAME 0x01 -#define HFS_CAP_LONGNAME 0x02 - hfs_byte_t fi_shortfilename[12+1]; /* "short name" (unused) */ - hfs_byte_t fi_macfilename[32+1]; /* Original (Macintosh) name */ - hfs_byte_t fi_comln; /* Length of comment (always 0) */ - hfs_byte_t fi_comnt[200]; /* Finder comment (unused) */ - /* optional: used by aufs only if compiled with USE_MAC_DATES */ - hfs_byte_t fi_datemagic; /* Magic number for dates extension: */ -#define HFS_CAP_DMAGIC 0xDA - hfs_byte_t fi_datevalid; /* Bitmap of which dates are valid: */ -#define HFS_CAP_MDATE 0x01 -#define HFS_CAP_CDATE 0x02 - hfs_lword_t fi_ctime; /* Creation date (in AFP format) */ - hfs_lword_t fi_mtime; /* Modify date (in AFP format) */ - hfs_lword_t fi_utime; /* Un*x time of last mtime change */ - hfs_byte_t pad; -}; - -#ifdef __KERNEL__ - -typedef ssize_t hfs_rwret_t; -typedef size_t hfs_rwarg_t; - -#include <asm/uaccess.h> - -/* Some forward declarations */ -struct hfs_fork; -struct hfs_cat_key; -struct hfs_cat_entry; -extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *, - const struct hfs_cat_key *); - -/* dir.c */ -extern int hfs_create(struct inode *, struct dentry *, int, struct nameidata *); -extern int hfs_mkdir(struct inode *, struct dentry *, int); -extern int hfs_unlink(struct inode *, struct dentry *); -extern int hfs_rmdir(struct inode *, struct dentry *); -extern int hfs_rename(struct inode *, struct dentry *, - struct inode *, struct dentry *); - -/* dir_cap.c */ -extern const struct hfs_name hfs_cap_reserved1[]; -extern const struct hfs_name hfs_cap_reserved2[]; -extern struct inode_operations hfs_cap_ndir_inode_operations; -extern struct inode_operations hfs_cap_fdir_inode_operations; -extern struct inode_operations hfs_cap_rdir_inode_operations; -extern struct file_operations hfs_cap_dir_operations; -extern void hfs_cap_drop_dentry(struct dentry *, const ino_t); - -/* dir_dbl.c */ -extern const struct hfs_name hfs_dbl_reserved1[]; -extern const struct hfs_name hfs_dbl_reserved2[]; -extern struct inode_operations hfs_dbl_dir_inode_operations; -extern struct file_operations hfs_dbl_dir_operations; -extern void hfs_dbl_drop_dentry(struct dentry *, const ino_t); - -/* dir_nat.c */ -extern const struct hfs_name hfs_nat_reserved1[]; -extern const struct hfs_name hfs_nat_reserved2[]; -extern struct inode_operations hfs_nat_ndir_inode_operations; -extern struct inode_operations hfs_nat_hdir_inode_operations; -extern struct file_operations hfs_nat_dir_operations; -extern void hfs_nat_drop_dentry(struct dentry *, const ino_t); - -/* file.c */ -extern hfs_s32 hfs_do_read(struct inode *, struct hfs_fork *, hfs_u32, - char __user *, hfs_u32); -extern hfs_s32 hfs_do_write(struct inode *, struct hfs_fork *, hfs_u32, - const char __user *, hfs_u32); -extern void hfs_file_fix_mode(struct hfs_cat_entry *entry); -extern struct inode_operations hfs_file_inode_operations; -extern struct file_operations hfs_file_operations; - -/* file_cap.c */ -extern struct inode_operations hfs_cap_info_inode_operations; -extern struct file_operations hfs_cap_info_operations; - -/* file_hdr.c */ -extern struct inode_operations hfs_hdr_inode_operations; -extern struct file_operations hfs_hdr_operations; -extern const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout; -extern const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout; -extern const struct hfs_hdr_layout hfs_nat_hdr_layout; -extern const struct hfs_hdr_layout hfs_nat2_hdr_layout; -extern const struct hfs_hdr_layout hfs_sngl_hdr_layout; -extern void hdr_truncate(struct inode *,size_t); - -/* inode.c */ -extern void hfs_put_inode(struct inode *); -extern int hfs_notify_change(struct dentry *, struct iattr *); -extern int hfs_notify_change_cap(struct dentry *, struct iattr *); -extern int hfs_notify_change_hdr(struct dentry *, struct iattr *); -extern struct inode *hfs_iget(struct hfs_cat_entry *, ino_t, struct dentry *); - -extern void hfs_cap_ifill(struct inode *, ino_t, const int); -extern void hfs_dbl_ifill(struct inode *, ino_t, const int); -extern void hfs_nat_ifill(struct inode *, ino_t, const int); -extern void hfs_sngl_ifill(struct inode *, ino_t, const int); - -/* super.c */ -extern int hfs_fill_super(struct super_block *,void *,int); - -/* trans.c */ -extern void hfs_colon2mac(struct hfs_name *, const char *, int); -extern void hfs_prcnt2mac(struct hfs_name *, const char *, int); -extern void hfs_triv2mac(struct hfs_name *, const char *, int); -extern void hfs_latin2mac(struct hfs_name *, const char *, int); -extern int hfs_mac2cap(char *, const struct hfs_name *); -extern int hfs_mac2nat(char *, const struct hfs_name *); -extern int hfs_mac2latin(char *, const struct hfs_name *); -extern int hfs_mac2seven(char *, const struct hfs_name *); -extern int hfs_mac2eight(char *, const struct hfs_name *); -extern int hfs_mac2alpha(char *, const struct hfs_name *); -extern int hfs_mac2triv(char *, const struct hfs_name *); -extern void hfs_tolower(unsigned char *, int); - -#include <linux/hfs_fs_i.h> -#include <linux/hfs_fs_sb.h> - -static inline struct hfs_inode_info *HFS_I(struct inode *inode) -{ - return container_of(inode, struct hfs_inode_info, vfs_inode); -} - -static inline struct hfs_sb_info *HFS_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline void hfs_nameout(struct inode *dir, struct hfs_name *out, - const char *in, int len) { - HFS_SB(dir->i_sb)->s_nameout(out, in, len); -} - -static inline int hfs_namein(struct inode *dir, char *out, - const struct hfs_name *in) { - int len = HFS_SB(dir->i_sb)->s_namein(out, in); - if (HFS_SB(dir->i_sb)->s_lowercase) { - hfs_tolower(out, len); - } - return len; -} - -#endif /* __KERNEL__ */ -#endif diff --git a/include/linux/hfs_fs_i.h b/include/linux/hfs_fs_i.h deleted file mode 100644 index 119cc5200197..000000000000 --- a/include/linux/hfs_fs_i.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * linux/include/linux/hfs_fs_i.h - * - * Copyright (C) 1995, 1996 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file defines the type (struct hfs_inode_info) and the two - * subordinate types hfs_extent and hfs_file. - */ - -#ifndef _LINUX_HFS_FS_I_H -#define _LINUX_HFS_FS_I_H - -/* - * struct hfs_inode_info - * - * The HFS-specific part of a Linux (struct inode) - */ -struct hfs_inode_info { - int magic; /* A magic number */ - - loff_t mmu_private; - struct hfs_cat_entry *entry; - - /* For a regular or header file */ - struct hfs_fork *fork; - int convert; - - /* For a directory */ - ino_t file_type; - char dir_size; - - /* For header files */ - const struct hfs_hdr_layout *default_layout; - struct hfs_hdr_layout *layout; - - /* to deal with localtime ugliness */ - int tz_secondswest; - - /* for dentry cleanup */ - void (*d_drop_op)(struct dentry *, const ino_t); - struct inode vfs_inode; -}; - -#endif diff --git a/include/linux/hfs_fs_sb.h b/include/linux/hfs_fs_sb.h deleted file mode 100644 index 037ebd428b91..000000000000 --- a/include/linux/hfs_fs_sb.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * linux/include/linux/hfs_fs_sb.h - * - * Copyright (C) 1995-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file defines the type (struct hfs_sb_info) which contains the - * HFS-specific information in the in-core superblock. - */ - -#ifndef _LINUX_HFS_FS_SB_H -#define _LINUX_HFS_FS_SB_H - -/* forward declaration: */ -struct hfs_name; - -typedef int (*hfs_namein_fn) (char *, const struct hfs_name *); -typedef void (*hfs_nameout_fn) (struct hfs_name *, const char *, int); -typedef void (*hfs_ifill_fn) (struct inode *, ino_t, const int); - -/* - * struct hfs_sb_info - * - * The HFS-specific part of a Linux (struct super_block) - */ -struct hfs_sb_info { - int magic; /* A magic number */ - struct hfs_mdb *s_mdb; /* The HFS MDB */ - int s_quiet; /* Silent failure when - changing owner or mode? */ - int s_lowercase; /* Map names to lowercase? */ - int s_afpd; /* AFPD compatible mode? */ - int s_version; /* version info */ - hfs_namein_fn s_namein; /* The function used to - map Mac filenames to - Linux filenames */ - hfs_nameout_fn s_nameout; /* The function used to - map Linux filenames - to Mac filenames */ - hfs_ifill_fn s_ifill; /* The function used - to fill in inode fields */ - const struct hfs_name *s_reserved1; /* Reserved names */ - const struct hfs_name *s_reserved2; /* Reserved names */ - __u32 s_type; /* Type for new files */ - __u32 s_creator; /* Creator for new files */ - umode_t s_umask; /* The umask applied to the - permissions on all files */ - uid_t s_uid; /* The uid of all files */ - gid_t s_gid; /* The gid of all files */ - char s_conv; /* Type of text conversion */ -}; - -#endif diff --git a/include/linux/hfs_sysdep.h b/include/linux/hfs_sysdep.h deleted file mode 100644 index 4c4e3eba0963..000000000000 --- a/include/linux/hfs_sysdep.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * linux/include/linux/hfs_sysdep.h - * - * Copyright (C) 1996-1997 Paul H. Hargrove - * This file may be distributed under the terms of the GNU General Public License. - * - * This file contains constants, types and inline - * functions for various system dependent things. - * - * "XXX" in a comment is a note to myself to consider changing something. - * - * In function preconditions the term "valid" applied to a pointer to - * a structure means that the pointer is non-NULL and the structure it - * points to has all fields initialized to consistent values. - */ - -#ifndef _HFS_SYSDEP_H -#define _HFS_SYSDEP_H - -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/buffer_head.h> - -#include <asm/byteorder.h> -#include <asm/unaligned.h> - -extern struct timezone sys_tz; - -/* Typedefs for integer types by size and signedness */ -typedef __u8 hfs_u8; -typedef __u16 hfs_u16; -typedef __u32 hfs_u32; -typedef __s8 hfs_s8; -typedef __s16 hfs_s16; -typedef __s32 hfs_s32; - -/* Typedefs for unaligned integer types */ -typedef unsigned char hfs_byte_t; -typedef unsigned char hfs_word_t[2]; -typedef unsigned char hfs_lword_t[4]; - -/* these funny looking things are GCC variable argument macros */ -#define hfs_warn(format, args...) printk(KERN_WARNING format , ## args) -#define hfs_error(format, args...) printk(KERN_ERR format , ## args) - - -#if defined(DEBUG_ALL) || defined(DEBUG_MEM) -extern long int hfs_alloc; -#endif - -static inline void *hfs_malloc(unsigned int size) { -#if defined(DEBUG_ALL) || defined(DEBUG_MEM) - hfs_warn("%ld bytes allocation at %s:%u\n", - (hfs_alloc += size), __FILE__, __LINE__); -#endif - return kmalloc(size, GFP_KERNEL); -} - -static inline void hfs_free(void *ptr, unsigned int size) { - kfree(ptr); -#if defined(DEBUG_ALL) || defined(DEBUG_MEM) - hfs_warn("%ld bytes allocation at %s:%u\n", - (hfs_alloc -= ptr ? size : 0), __FILE__, __LINE__); -#endif -} - - -/* handle conversion between times. - * - * NOTE: hfs+ doesn't need this. also, we don't use tz_dsttime as that's - * not a good thing to do. instead, we depend upon tz_minuteswest - * having the correct daylight savings correction. - */ -static inline hfs_u32 hfs_from_utc(hfs_s32 time) -{ - return time - sys_tz.tz_minuteswest*60; -} - -static inline hfs_s32 hfs_to_utc(hfs_u32 time) -{ - return time + sys_tz.tz_minuteswest*60; -} - -static inline hfs_u32 hfs_time(void) { - return htonl(hfs_from_utc(get_seconds())+2082844800U); -} - - -/* - * hfs_wait_queue - */ -typedef wait_queue_head_t hfs_wait_queue; - -static inline void hfs_init_waitqueue(hfs_wait_queue *queue) { - init_waitqueue_head(queue); -} - -static inline void hfs_sleep_on(hfs_wait_queue *queue) { - sleep_on(queue); -} - -static inline void hfs_wake_up(hfs_wait_queue *queue) { - wake_up(queue); -} - -static inline void hfs_relinquish(void) { - schedule(); -} - - -/* - * hfs_sysmdb - */ -typedef struct super_block *hfs_sysmdb; - -static inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) { - sys_mdb->s_dirt = 1; -} - -static inline const char *hfs_mdb_name(hfs_sysmdb sys_mdb) { - return sys_mdb->s_id; -} - - -/* - * hfs_sysentry - */ -typedef struct dentry *hfs_sysentry[4]; - -/* - * hfs_buffer - */ -typedef struct buffer_head *hfs_buffer; - -#define HFS_BAD_BUFFER NULL - -/* In sysdep.c, since it needs HFS_SECTOR_SIZE */ -extern hfs_buffer hfs_buffer_get(hfs_sysmdb, int, int); - -static inline int hfs_buffer_ok(hfs_buffer buffer) { - return (buffer != NULL); -} - -static inline void hfs_buffer_put(hfs_buffer buffer) { - brelse(buffer); -} - -static inline void hfs_buffer_dirty(hfs_buffer buffer) { - mark_buffer_dirty(buffer); -} - -static inline void hfs_buffer_sync(hfs_buffer buffer) { - if (buffer_dirty(buffer)) - sync_dirty_buffer(buffer); -} - -static inline void *hfs_buffer_data(const hfs_buffer buffer) { - return buffer->b_data; -} - - -/* - * bit operations - */ - -#undef BITNR -#if defined(__BIG_ENDIAN) -# define BITNR(X) ((X)^31) -# if !defined(__constant_htonl) -# define __constant_htonl(x) (x) -# endif -# if !defined(__constant_htons) -# define __constant_htons(x) (x) -# endif -#elif defined(__LITTLE_ENDIAN) -# define BITNR(X) ((X)^7) -# if !defined(__constant_htonl) -# define __constant_htonl(x) \ - ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \ - (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ - (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \ - (((unsigned long int)(x) & 0xff000000U) >> 24))) -# endif -# if !defined(__constant_htons) -# define __constant_htons(x) \ - ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \ - (((unsigned short int)(x) & 0xff00) >> 8))) -# endif -#else -# error "Don't know if bytes are big- or little-endian!" -#endif - -static inline int hfs_clear_bit(int bitnr, hfs_u32 *lword) { - return test_and_clear_bit(BITNR(bitnr), (unsigned long *)lword); -} - -static inline int hfs_set_bit(int bitnr, hfs_u32 *lword) { - return test_and_set_bit(BITNR(bitnr), (unsigned long *)lword); -} - -static inline int hfs_test_bit(int bitnr, const hfs_u32 *lword) { - /* the kernel should declare the second arg of test_bit as const */ - return test_bit(BITNR(bitnr), (unsigned long *)lword); -} - -#undef BITNR - -/* - * HFS structures have fields aligned to 16-bit boundaries. - * So, 16-bit get/put are easy while 32-bit get/put need - * some care on architectures like the DEC Alpha. - * - * In what follows: - * ns = 16-bit integer in network byte-order w/ 16-bit alignment - * hs = 16-bit integer in host byte-order w/ 16-bit alignment - * nl = 32-bit integer in network byte-order w/ unknown alignment - * hl = 32-bit integer in host byte-order w/ unknown alignment - * anl = 32-bit integer in network byte-order w/ 32-bit alignment - * ahl = 32-bit integer in host byte-order w/ 32-bit alignment - * Example: hfs_get_hl() gets an unaligned 32-bit integer converting - * it to host byte-order. - */ -#define hfs_get_hs(addr) ntohs(*((hfs_u16 *)(addr))) -#define hfs_get_ns(addr) (*((hfs_u16 *)(addr))) -#define hfs_get_hl(addr) ntohl(get_unaligned((hfs_u32 *)(addr))) -#define hfs_get_nl(addr) get_unaligned((hfs_u32 *)(addr)) -#define hfs_get_ahl(addr) ntohl(*((hfs_u32 *)(addr))) -#define hfs_get_anl(addr) (*((hfs_u32 *)(addr))) -#define hfs_put_hs(val, addr) ((void)(*((hfs_u16 *)(addr)) = ntohs(val))) -#define hfs_put_ns(val, addr) ((void)(*((hfs_u16 *)(addr)) = (val))) -#define hfs_put_hl(val, addr) put_unaligned(htonl(val), (hfs_u32 *)(addr)) -#define hfs_put_nl(val, addr) put_unaligned((val), (hfs_u32 *)(addr)) -#define hfs_put_ahl(val, addr) ((void)(*((hfs_u32 *)(addr)) = ntohl(val))) -#define hfs_put_anl(val, addr) ((void)(*((hfs_u32 *)(addr)) = (val))) - -#endif diff --git a/include/linux/isicom.h b/include/linux/isicom.h index c24383215978..a5a3eea548e8 100644 --- a/include/linux/isicom.h +++ b/include/linux/isicom.h @@ -73,7 +73,6 @@ typedef struct { #define PORT_COUNT (BOARD_COUNT*16) #define SERIAL_TYPE_NORMAL 1 -#define SERIAL_TYPE_CALLOUT 2 /* character sizes */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ebc9426684ba..fd5ae72d8ae9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -525,11 +525,6 @@ extern struct net_device *__dev_get_by_flags(unsigned short flags, unsigned short mask); extern struct net_device *dev_get_by_name(const char *name); extern struct net_device *__dev_get_by_name(const char *name); -extern struct net_device *__dev_alloc(const char *name, int *err); -static inline __deprecated struct net_device *dev_alloc(const char *name, int *err) -{ - return __dev_alloc(name, err); -} extern int dev_alloc_name(struct net_device *dev, const char *name); extern int dev_open(struct net_device *dev); extern int dev_close(struct net_device *dev); diff --git a/include/linux/serial.h b/include/linux/serial.h index 465f19a0d53d..9a07d00106f9 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -131,7 +131,6 @@ struct serial_uart_config { /* Internal flags used only by kernel/chr_drv/serial.c */ #define ASYNC_INITIALIZED 0x80000000 /* Serial port was initialized */ -#define ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ #define ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ #define ASYNC_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ #define ASYNC_CLOSING 0x08000000 /* Serial port is closing */ diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index f0b98e5a5473..ba9506f322b4 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -275,6 +275,5 @@ void tty_set_operations(struct tty_driver *driver, struct tty_operations *op); /* serial subtype definitions */ #define SERIAL_TYPE_NORMAL 1 -#define SERIAL_TYPE_CALLOUT 2 #endif /* #ifdef _LINUX_TTY_DRIVER_H */ diff --git a/init/main.c b/init/main.c index 740f14b1241f..74fce35f2598 100644 --- a/init/main.c +++ b/init/main.c @@ -350,7 +350,7 @@ static void __init setup_per_cpu_areas(void) static void __init smp_init(void) { unsigned int i; - unsigned j = 0; + unsigned j = 1; /* FIXME: This should be done in userspace --RR */ for (i = 0; i < NR_CPUS; i++) { diff --git a/kernel/compat.c b/kernel/compat.c index fb89b3233b04..f69dcd644a22 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -441,6 +441,9 @@ long compat_timer_settime(timer_t timer_id, int flags, long err; mm_segment_t oldfs; struct itimerspec newts, oldts; + + if (!new) + return -EINVAL; if (get_compat_itimerspec(&newts, new)) return -EFAULT; oldfs = get_fs(); diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index a2819d415cb7..3e9ec77a133a 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -616,7 +616,9 @@ asmlinkage void do_magic_resume_2(void) PRINTK( "ok\n" ); #ifdef SUSPEND_CONSOLE + acquire_console_sem(); update_screen(fg_console); /* Hmm, is this the problem? */ + release_console_sem(); #endif } diff --git a/kernel/signal.c b/kernel/signal.c index e0675f1d5e9c..7cbb9c60dd00 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -10,12 +10,9 @@ * to allow signals to be sent reliably. */ -#define __KERNEL_SYSCALLS__ - #include <linux/config.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/unistd.h> #include <linux/smp_lock.h> #include <linux/init.h> #include <linux/sched.h> diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index aca6800b9bd1..7bfd15c28e03 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -29,8 +29,6 @@ * $Id: core.c,v 1.20 2002/08/04 21:23:58 maxk Exp $ */ -#define __KERNEL_SYSCALLS__ - #include <linux/config.h> #include <linux/module.h> diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 6e381222edec..d0cb7053e203 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -31,8 +31,6 @@ * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $ */ -#define __KERNEL_SYSCALLS__ - #include <linux/config.h> #include <linux/module.h> #include <linux/errno.h> diff --git a/net/core/dev.c b/net/core/dev.c index 94c16c74dc99..94f1d3603fc8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -810,40 +810,6 @@ int dev_change_name(struct net_device *dev, char *newname) } /** - * dev_alloc - allocate a network device and name - * @name: name format string - * @err: error return pointer - * - * Passed a format string, eg. "lt%d", it will allocate a network device - * and space for the name. %NULL is returned if no memory is available. - * If the allocation succeeds then the name is assigned and the - * device pointer returned. %NULL is returned if the name allocation - * failed. The cause of an error is returned as a negative errno code - * in the variable @err points to. - * - * This call is deprecated in favor of alloc_netdev because - * the caller must hold the @dev_base or RTNL locks when doing this in - * order to avoid duplicate name allocations. - */ - -struct net_device *__dev_alloc(const char *name, int *err) -{ - struct net_device *dev = kmalloc(sizeof(*dev), GFP_KERNEL); - - if (!dev) - *err = -ENOBUFS; - else { - memset(dev, 0, sizeof(*dev)); - *err = dev_alloc_name(dev, name); - if (*err < 0) { - kfree(dev); - dev = NULL; - } - } - return dev; -} - -/** * netdev_state_change - device changes state * @dev: device to cause notification * @@ -3232,7 +3198,6 @@ EXPORT_SYMBOL(__dev_remove_pack); EXPORT_SYMBOL(__skb_linearize); EXPORT_SYMBOL(call_netdevice_notifiers); EXPORT_SYMBOL(dev_add_pack); -EXPORT_SYMBOL(__dev_alloc); EXPORT_SYMBOL(dev_alloc_name); EXPORT_SYMBOL(dev_close); EXPORT_SYMBOL(dev_get_by_flags); diff --git a/net/core/netfilter.c b/net/core/netfilter.c index cf2c4159ff8d..d132da324705 100644 --- a/net/core/netfilter.c +++ b/net/core/netfilter.c @@ -27,9 +27,6 @@ #include <net/route.h> #include <linux/ip.h> -#define __KERNEL_SYSCALLS__ -#include <linux/unistd.h> - /* In this code, we can be waiting indefinitely for userspace to * service a packet if a hook returns NF_QUEUE. We could keep a count * of skbuffs queued for userspace, and not deregister a hook unless diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 47e9600da135..1799921a2f65 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -11,12 +11,10 @@ #include <linux/module.h> -#define __KERNEL_SYSCALLS__ #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/mempool.h> -#include <linux/unistd.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/spinlock.h> diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index a56d0fdbd761..03ae390e2316 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -6,13 +6,11 @@ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */ -#define __KERNEL_SYSCALLS__ #include <linux/linkage.h> #include <linux/sched.h> #include <linux/errno.h> #include <linux/net.h> #include <linux/in.h> -#include <linux/unistd.h> #include <linux/mm.h> #include <linux/sunrpc/types.h> diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 84ccc9d8e05b..605bcc921d34 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -43,8 +43,6 @@ * (C) 1999 Trond Myklebust <trond.myklebust@fys.uio.no> */ -#define __KERNEL_SYSCALLS__ - #include <linux/types.h> #include <linux/slab.h> #include <linux/capability.h> @@ -56,7 +54,6 @@ #include <linux/mm.h> #include <linux/udp.h> #include <linux/tcp.h> -#include <linux/unistd.h> #include <linux/sunrpc/clnt.h> #include <linux/file.h> #include <linux/workqueue.h> diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c index 3e45d5e4bb21..fcdce75c6a38 100644 --- a/net/wanrouter/wanmain.c +++ b/net/wanrouter/wanmain.c @@ -191,8 +191,8 @@ static void __exit wanrouter_cleanup (void) * slap it first in directory and make it module_init(). The only reason * for subsys_initcall() here is that net goes after drivers (why, BTW?) */ -subsys_initcall(wanrouter_init) -module_exit(wanrouter_cleanup) +subsys_initcall(wanrouter_init); +module_exit(wanrouter_cleanup); /* * Kernel APIs |
