diff options
149 files changed, 6176 insertions, 3797 deletions
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 08687e45e19..1a7f53068ec 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -11,7 +11,7 @@ DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml videobook.xml \ procfs-guide.xml writing_usb_driver.xml \ kernel-api.xml filesystems.xml lsm.xml usb.xml \ gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \ - genericirq.xml + genericirq.xml s390-drivers.xml ### # The build process is as follows (targets): diff --git a/Documentation/DocBook/s390-drivers.tmpl b/Documentation/DocBook/s390-drivers.tmpl new file mode 100644 index 00000000000..254e769282a --- /dev/null +++ b/Documentation/DocBook/s390-drivers.tmpl @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []> + +<book id="s390drivers"> + <bookinfo> + <title>Writing s390 channel device drivers</title> + + <authorgroup> + <author> + <firstname>Cornelia</firstname> + <surname>Huck</surname> + <affiliation> + <address> + <email>cornelia.huck@de.ibm.com</email> + </address> + </affiliation> + </author> + </authorgroup> + + <copyright> + <year>2007</year> + <holder>IBM Corp.</holder> + </copyright> + + <legalnotice> + <para> + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + </para> + + <para> + 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. + </para> + + <para> + 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 + </para> + + <para> + For more details see the file COPYING in the source + distribution of Linux. + </para> + </legalnotice> + </bookinfo> + +<toc></toc> + + <chapter id="intro"> + <title>Introduction</title> + <para> + This document describes the interfaces available for device drivers that + drive s390 based channel attached devices. This includes interfaces for + interaction with the hardware and interfaces for interacting with the + common driver core. Those interfaces are provided by the s390 common I/O + layer. + </para> + <para> + The document assumes a familarity with the technical terms associated + with the s390 channel I/O architecture. For a description of this + architecture, please refer to the "z/Architecture: Principles of + Operation", IBM publication no. SA22-7832. + </para> + <para> + While most I/O devices on a s390 system are typically driven through the + channel I/O mechanism described here, there are various other methods + (like the diag interface). These are out of the scope of this document. + </para> + <para> + Some additional information can also be found in the kernel source + under Documentation/s390/driver-model.txt. + </para> + </chapter> + <chapter id="ccw"> + <title>The ccw bus</title> + <para> + The ccw bus typically contains the majority of devices available to + a s390 system. Named after the channel command word (ccw), the basic + command structure used to address its devices, the ccw bus contains + so-called channel attached devices. They are addressed via subchannels, + visible on the css bus. A device driver, however, will never interact + with the subchannel directly, but only via the device on the ccw bus, + the ccw device. + </para> + <sect1 id="channelIO"> + <title>I/O functions for channel-attached devices</title> + <para> + Some hardware structures have been translated into C structures for use + by the common I/O layer and device drivers. For more information on + the hardware structures represented here, please consult the Principles + of Operation. + </para> +!Iinclude/asm-s390/cio.h + </sect1> + <sect1 id="ccwdev"> + <title>ccw devices</title> + <para> + Devices that want to initiate channel I/O need to attach to the ccw bus. + Interaction with the driver core is done via the common I/O layer, which + provides the abstractions of ccw devices and ccw device drivers. + </para> + <para> + The functions that initiate or terminate channel I/O all act upon a + ccw device structure. Device drivers must not bypass those functions + or strange side effects may happen. + </para> +!Iinclude/asm-s390/ccwdev.h +!Edrivers/s390/cio/device.c +!Edrivers/s390/cio/device_ops.c + </sect1> + <sect1 id="cmf"> + <title>The channel-measurement facility</title> + <para> + The channel-measurement facility provides a means to collect + measurement data which is made available by the channel subsystem + for each channel attached device. + </para> +!Iinclude/asm-s390/cmb.h +!Edrivers/s390/cio/cmf.c + </sect1> + </chapter> + + <chapter id="ccwgroup"> + <title>The ccwgroup bus</title> + <para> + The ccwgroup bus only contains artificial devices, created by the user. + Many networking devices (e.g. qeth) are in fact composed of several + ccw devices (like read, write and data channel for qeth). The + ccwgroup bus provides a mechanism to create a meta-device which + contains those ccw devices as slave devices and can be associated + with the netdevice. + </para> + <sect1 id="ccwgroupdevices"> + <title>ccw group devices</title> +!Iinclude/asm-s390/ccwgroup.h +!Edrivers/s390/cio/ccwgroup.c + </sect1> + </chapter> + +</book> diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt index 8ee10ec8829..e79ee2db183 100644 --- a/Documentation/filesystems/ntfs.txt +++ b/Documentation/filesystems/ntfs.txt @@ -407,7 +407,7 @@ raiddev /dev/md0 device /dev/hda5 raid-disk 0 device /dev/hdb1 - raid-disl 1 + raid-disk 1 For linear raid, just change the raid-level above to "raid-level linear", for mirrors, change it to "raid-level 1", and for stripe sets with parity, change @@ -457,6 +457,8 @@ ChangeLog Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. +2.1.29: + - Fix a deadlock when mounting read-write. 2.1.28: - Fix a deadlock. 2.1.27: diff --git a/Documentation/s390/00-INDEX b/Documentation/s390/00-INDEX new file mode 100644 index 00000000000..3a2b96302ec --- /dev/null +++ b/Documentation/s390/00-INDEX @@ -0,0 +1,26 @@ +00-INDEX + - this file. +3270.ChangeLog + - ChangeLog for the UTS Global 3270-support patch (outdated). +3270.txt + - how to use the IBM 3270 display system support. +cds.txt + - s390 common device support (common I/O layer). +CommonIO + - common I/O layer command line parameters, procfs and debugfs entries +config3270.sh + - example configuration for 3270 devices. +DASD + - information on the DASD disk device driver. +Debugging390.txt + - hints for debugging on s390 systems. +driver-model.txt + - information on s390 devices and the driver model. +monreader.txt + - information on accessing the z/VM monitor stream from Linux. +s390dbf.txt + - information on using the s390 debug feature. +TAPE + - information on the driver for channel-attached tapes. +zfcpdump + - information on the s390 SCSI dump tool. diff --git a/Documentation/s390/CommonIO b/Documentation/s390/CommonIO index 22f82f21bc6..86320aa3fb0 100644 --- a/Documentation/s390/CommonIO +++ b/Documentation/s390/CommonIO @@ -1,5 +1,5 @@ -S/390 common I/O-Layer - command line parameters and /proc entries -================================================================== +S/390 common I/O-Layer - command line parameters, procfs and debugfs entries +============================================================================ Command line parameters ----------------------- @@ -7,9 +7,9 @@ Command line parameters * cio_msg = yes | no Determines whether information on found devices and sensed device - characteristics should be shown during startup, i. e. messages of the types - "Detected device 0.0.4711 on subchannel 0.0.0042" and "SenseID: Device - 0.0.4711 reports: ...". + characteristics should be shown during startup or when new devices are + found, i. e. messages of the types "Detected device 0.0.4711 on subchannel + 0.0.0042" and "SenseID: Device 0.0.4711 reports: ...". Default is off. @@ -26,8 +26,10 @@ Command line parameters An ignored device can be un-ignored later; see the "/proc entries"-section for details. - 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). + The devices must be given either as bus ids (0.x.abcd) or as hexadecimal + device numbers (0xabcd or abcd, for 2.4 backward compatibility). If you + give a device number 0xabcd, it will be interpreted as 0.0.abcd. + You can use the 'all' keyword to ignore all devices. The '!' operator will cause the I/O-layer to _not_ ignore a device. The command line is parsed from left to right. @@ -81,31 +83,36 @@ Command line parameters will add 0.0.a000-0.0.accc and 0.0.af00-0.0.afff to the list of ignored devices. - The devices can be specified either by bus id (0.0.abcd) or, for 2.4 backward - compatibility, by the device number in hexadecimal (0xabcd or abcd). + The devices can be specified either by bus id (0.x.abcd) or, for 2.4 backward + compatibility, by the device number in hexadecimal (0xabcd or abcd). Device + numbers given as 0xabcd will be interpreted as 0.0.abcd. + +* For some of the information present in the /proc filesystem in 2.4 (namely, + /proc/subchannels and /proc/chpids), see driver-model.txt. + Information formerly in /proc/irq_count is now in /proc/interrupts. + +debugfs entries +--------------- -* /proc/s390dbf/cio_*/ (S/390 debug feature) +* /sys/kernel/debug/s390dbf/cio_*/ (S/390 debug feature) Some views generated by the debug feature to hold various debug outputs. - - /proc/s390dbf/cio_crw/sprintf + - /sys/kernel/debug/s390dbf/cio_crw/sprintf Messages from the processing of pending channel report words (machine check - handling), which will also show when CONFIG_DEBUG_CRW is defined. + handling). - - /proc/s390dbf/cio_msg/sprintf - Various debug messages from the common I/O-layer; generally, messages which - will also show when CONFIG_DEBUG_IO is defined. + - /sys/kernel/debug/s390dbf/cio_msg/sprintf + Various debug messages from the common I/O-layer, including messages + printed when cio_msg=yes. - - /proc/s390dbf/cio_trace/hex_ascii + - /sys/kernel/debug/s390dbf/cio_trace/hex_ascii Logs the calling of functions in the common I/O-layer and, if applicable, which subchannel they were called for, as well as dumps of some data structures (like irb in an error case). The level of logging can be changed to be more or less verbose by piping to - /proc/s390dbf/cio_*/level a number between 0 and 6; see the documentation on - the S/390 debug feature (Documentation/s390/s390dbf.txt) for details. - -* For some of the information present in the /proc filesystem in 2.4 (namely, - /proc/subchannels and /proc/chpids), see driver-model.txt. - Information formerly in /proc/irq_count is now in /proc/interrupts. + /sys/kernel/debug/s390dbf/cio_*/level a number between 0 and 6; see the + documentation on the S/390 debug feature (Documentation/s390/s390dbf.txt) + for details. diff --git a/Documentation/s390/cds.txt b/Documentation/s390/cds.txt index 58919d6a593..3081927cc2d 100644 --- a/Documentation/s390/cds.txt +++ b/Documentation/s390/cds.txt @@ -286,10 +286,10 @@ first: timeout value -EIO: the common I/O layer terminated the request due to an error state -If the concurrent sense flag in the extended status word in the irb is set, the -field irb->scsw.count describes the number of device specific sense bytes -available in the extended control word irb->scsw.ecw[0]. No device sensing by -the device driver itself is required. +If the concurrent sense flag in the extended status word (esw) in the irb is +set, the field erw.scnt in the esw describes the number of device specific +sense bytes available in the extended control word irb->scsw.ecw[]. No device +sensing by the device driver itself is required. The device interrupt handler can use the following definitions to investigate the primary unit check source coded in sense byte 0 : diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index 62391fb1f61..ac61cf43a7d 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -547,8 +547,7 @@ static void __cpuinit appldata_online_cpu(int cpu) spin_unlock(&appldata_timer_lock); } -static void -appldata_offline_cpu(int cpu) +static void __cpuinit appldata_offline_cpu(int cpu) { del_virt_timer(&per_cpu(appldata_timer, cpu)); if (atomic_dec_and_test(&appldata_expire_count)) { @@ -560,9 +559,9 @@ appldata_offline_cpu(int cpu) spin_unlock(&appldata_timer_lock); } -static int __cpuinit -appldata_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) +static int __cpuinit appldata_cpu_notify(struct notifier_block *self, + unsigned long action, + void *hcpu) { switch (action) { case CPU_ONLINE: @@ -608,63 +607,15 @@ static int __init appldata_init(void) register_hotcpu_notifier(&appldata_nb); appldata_sysctl_header = register_sysctl_table(appldata_dir_table); -#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 P_DEBUG("Base interface initialized.\n"); return 0; } -/* - * appldata_exit() - * - * stop timer, unregister /proc entries - */ -static void __exit appldata_exit(void) -{ - struct list_head *lh; - struct appldata_ops *ops; - int rc, i; +__initcall(appldata_init); - P_DEBUG("Unloading module ...\n"); - /* - * ops list should be empty, but just in case something went wrong... - */ - spin_lock(&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, - ops->mod_lvl); - if (rc != 0) { - P_ERROR("STOP DIAG 0xDC for %s failed, " - "return code: %d\n", ops->name, rc); - } - } - spin_unlock(&appldata_ops_lock); - - for_each_online_cpu(i) - appldata_offline_cpu(i); - - appldata_timer_active = 0; - - unregister_sysctl_table(appldata_sysctl_header); - - destroy_workqueue(appldata_wq); - 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); EXPORT_SYMBOL_GPL(appldata_diag); diff --git a/arch/s390/kernel/audit.c b/arch/s390/kernel/audit.c index d1c76fe10f2..f4932c22ebe 100644 --- a/arch/s390/kernel/audit.c +++ b/arch/s390/kernel/audit.c @@ -2,6 +2,7 @@ #include <linux/types.h> #include <linux/audit.h> #include <asm/unistd.h> +#include "audit.h" static unsigned dir_class[] = { #include <asm-generic/audit_dir_write.h> @@ -40,7 +41,6 @@ int audit_classify_arch(int arch) int audit_classify_syscall(int abi, unsigned syscall) { #ifdef CONFIG_COMPAT - extern int s390_classify_syscall(unsigned); if (abi == AUDIT_ARCH_S390) return s390_classify_syscall(syscall); #endif @@ -61,11 +61,6 @@ int audit_classify_syscall(int abi, unsigned syscall) static int __init audit_classes_init(void) { #ifdef CONFIG_COMPAT - extern __u32 s390_dir_class[]; - extern __u32 s390_write_class[]; - extern __u32 s390_read_class[]; - extern __u32 s390_chattr_class[]; - extern __u32 s390_signal_class[]; audit_register_class(AUDIT_CLASS_WRITE_32, s390_write_class); audit_register_class(AUDIT_CLASS_READ_32, s390_read_class); audit_register_class(AUDIT_CLASS_DIR_WRITE_32, s390_dir_class); diff --git a/arch/s390/kernel/audit.h b/arch/s390/kernel/audit.h new file mode 100644 index 00000000000..12b56f4b5a7 --- /dev/null +++ b/arch/s390/kernel/audit.h @@ -0,0 +1,15 @@ +#ifndef __ARCH_S390_KERNEL_AUDIT_H +#define __ARCH_S390_KERNEL_AUDIT_H + +#include <linux/types.h> + +#ifdef CONFIG_COMPAT +extern int s390_classify_syscall(unsigned); +extern __u32 s390_dir_class[]; +extern __u32 s390_write_class[]; +extern __u32 s390_read_class[]; +extern __u32 s390_chattr_class[]; +extern __u32 s390_signal_class[]; +#endif /* CONFIG_COMPAT */ + +#endif /* __ARCH_S390_KERNEL_AUDIT_H */ diff --git a/arch/s390/kernel/compat_audit.c b/arch/s390/kernel/compat_audit.c index 0569f5126e4..d6487bf879e 100644 --- a/arch/s390/kernel/compat_audit.c +++ b/arch/s390/kernel/compat_audit.c @@ -1,5 +1,6 @@ #undef __s390x__ #include <asm/unistd.h> +#include "audit.h" unsigned s390_dir_class[] = { #include <asm-generic/audit_dir_write.h> diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c index 6c89f30c8e3..d8c1131e081 100644 --- a/arch/s390/kernel/cpcmd.c +++ b/arch/s390/kernel/cpcmd.c @@ -2,7 +2,7 @@ * arch/s390/kernel/cpcmd.c * * S390 version - * Copyright (C) 1999,2005 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright IBM Corp. 1999,2007 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), * Christian Borntraeger (cborntra@de.ibm.com), */ @@ -21,6 +21,49 @@ static DEFINE_SPINLOCK(cpcmd_lock); static char cpcmd_buf[241]; +static int diag8_noresponse(int cmdlen) +{ + register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf; + register unsigned long reg3 asm ("3") = cmdlen; + + asm volatile( +#ifndef CONFIG_64BIT + " diag %1,%0,0x8\n" +#else /* CONFIG_64BIT */ + " sam31\n" + " diag %1,%0,0x8\n" + " sam64\n" +#endif /* CONFIG_64BIT */ + : "+d" (reg3) : "d" (reg2) : "cc"); + return reg3; +} + +static int diag8_response(int cmdlen, char *response, int *rlen) +{ + register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf; + register unsigned long reg3 asm ("3") = (addr_t) response; + register unsigned long reg4 asm ("4") = cmdlen | 0x40000000L; + register unsigned long reg5 asm ("5") = *rlen; + + asm volatile( +#ifndef CONFIG_64BIT + " diag %2,%0,0x8\n" + " brc 8,1f\n" + " ar %1,%4\n" +#else /* CONFIG_64BIT */ + " sam31\n" + " diag %2,%0,0x8\n" + " sam64\n" + " brc 8,1f\n" + " agr %1,%4\n" +#endif /* CONFIG_64BIT */ + "1:\n" + : "+d" (reg4), "+d" (reg5) + : "d" (reg2), "d" (reg3), "d" (*rlen) : "cc"); + *rlen = reg5; + return reg4; +} + /* * __cpcmd has some restrictions over cpcmd * - the response buffer must reside below 2GB (if any) @@ -28,59 +71,27 @@ static char cpcmd_buf[241]; */ int __cpcmd(const char *cmd, char *response, int rlen, int *response_code) { - unsigned cmdlen; - int return_code, return_len; + int cmdlen; + int rc; + int response_len; cmdlen = strlen(cmd); BUG_ON(cmdlen > 240); memcpy(cpcmd_buf, cmd, cmdlen); ASCEBC(cpcmd_buf, cmdlen); - if (response != NULL && rlen > 0) { - register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf; - register unsigned long reg3 asm ("3") = (addr_t) response; - register unsigned long reg4 asm ("4") = cmdlen | 0x40000000L; - register unsigned long reg5 asm ("5") = rlen; - + if (response) { memset(response, 0, rlen); - asm volatile( -#ifndef CONFIG_64BIT - " diag %2,%0,0x8\n" - " brc 8,1f\n" - " ar %1,%4\n" -#else /* CONFIG_64BIT */ - " sam31\n" - " diag %2,%0,0x8\n" - " sam64\n" - " brc 8,1f\n" - " agr %1,%4\n" -#endif /* CONFIG_64BIT */ - "1:\n" - : "+d" (reg4), "+d" (reg5) - : "d" (reg2), "d" (reg3), "d" (rlen) : "cc"); - return_code = (int) reg4; - return_len = (int) reg5; - EBCASC(response, rlen); + response_len = rlen; + rc = diag8_response(cmdlen, response, &rlen); + EBCASC(response, response_len); } else { - register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf; - register unsigned long reg3 asm ("3") = cmdlen; - return_len = 0; - asm volatile( -#ifndef CONFIG_64BIT - " diag %1,%0,0x8\n" -#else /* CONFIG_64BIT */ - " sam31\n" - " diag %1,%0,0x8\n" - " sam64\n" -#endif /* CONFIG_64BIT */ - : "+d" (reg3) : "d" (reg2) : "cc"); - return_code = (int) reg3; + rc = diag8_noresponse(cmdlen); } - if (response_code != NULL) - *response_code = return_code; - return return_len; + if (response_code) + *response_code = rc; + return rlen; } - EXPORT_SYMBOL(__cpcmd); int cpcmd(const char *cmd, char *response, int rlen, int *response_code) @@ -109,5 +120,4 @@ int cpcmd(const char *cmd, char *response, int rlen, int *response_code) } return len; } - EXPORT_SYMBOL(cpcmd); diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index 50d2235df73..c14a336f630 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -1162,6 +1162,7 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr) unsigned int value; char separator; char *ptr; + int i; ptr = buffer; insn = find_insn(code); @@ -1169,7 +1170,8 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr) ptr += sprintf(ptr, "%.5s\t", insn->name); /* Extract the operands. */ separator = 0; - for (ops = formats[insn->format] + 1; *ops != 0; ops++) { + for (ops = formats[insn->format] + 1, i = 0; + *ops != 0 && i < 6; ops++, i++) { operand = operands + *ops; value = extract_operand(code, operand); if ((operand->flags & OPERAND_INDEX) && value == 0) @@ -1241,7 +1243,6 @@ void show_code(struct pt_regs *regs) } /* Find a starting point for the disassembly. */ while (start < 32) { - hops = 0; for (i = 0, hops = 0; start + i < 32 && hops < 3; hops++) { if (!find_insn(code + start + i)) break; diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 8b8f136d9cc..66b51901c87 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -735,10 +735,10 @@ void do_reipl(void) case REIPL_METHOD_CCW_VM: reipl_get_ascii_loadparm(loadparm); if (strlen(loadparm) == 0) - sprintf(buf, "IPL %X", + sprintf(buf, "IPL %X CLEAR", reipl_block_ccw->ipl_info.ccw.devno); else - sprintf(buf, "IPL %X LOADPARM '%s'", + sprintf(buf, "IPL %X CLEAR LOADPARM '%s'", reipl_block_ccw->ipl_info.ccw.devno, loadparm); __cpcmd(buf, NULL, 0, NULL); break; diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index b4622a3889b..849120e3e28 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -2,6 +2,7 @@ * Written by Martin Schwidefsky (schwidefsky@de.ibm.com) */ +#include <asm/page.h> #include <asm-generic/vmlinux.lds.h> #ifndef CONFIG_64BIT @@ -18,121 +19,142 @@ jiffies = jiffies_64; SECTIONS { - . = 0x00000000; - _text = .; /* Text and read-only data */ - .text : { - *(.text.head) + . = 0x00000000; + .text : { + _text = .; /* Text and read-only data */ + *(.text.head) TEXT_TEXT - SCHED_TEXT - LOCK_TEXT - KPROBES_TEXT - *(.fixup) - *(.gnu.warning) + SCHED_TEXT + LOCK_TEXT + KPROBES_TEXT + *(.fixup) + *(.gnu.warning) } = 0x0700 - _etext = .; /* End of text section */ + _etext = .; /* End of text section */ - RODATA + RODATA #ifdef CONFIG_SHARED_KERNEL - . = ALIGN(1048576); /* VM shared segments are 1MB aligned */ + . = ALIGN(0x100000); /* VM shared segments are 1MB aligned */ #endif - . = ALIGN(4096); - _eshared = .; /* End of shareable data */ - - . = ALIGN(16); /* Exception table */ - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - NOTES - - BUG_TABLE - - .data : { /* Data */ - DATA_DATA - CONSTRUCTORS - } - - . = ALIGN(4096); - __nosave_begin = .; - .data_nosave : { *(.data.nosave) } - . = ALIGN(4096); - __nosave_end = .; - - . = ALIGN(4096); - .data.page_aligned : { *(.data.idt) } - - . = ALIGN(256); - .data.cacheline_aligned : { *(.data.cacheline_aligned) } - - . = ALIGN(256); - .data.read_mostly : { *(.data.read_mostly) } - _edata = .; /* End of data section */ - - . = ALIGN(8192); /* init_task */ - .data.init_task : { *(.data.init_task) } - - /* will be freed after init */ - . = ALIGN(4096); /* Init code and data */ - __init_begin = .; - .init.text : { - _sinittext = .; - *(.init.text) - _einittext = .; - } - /* - * .exit.text is discarded at runtime, not link time, - * to deal with references from __bug_table - */ - .exit.text : { *(.exit.text) } - - .init.data : { *(.init.data) } - . = ALIGN(256); - __setup_start = .; - .init.setup : { *(.init.setup) } - __setup_end = .; - __initcall_start = .; - .initcall.init : { - INITCALLS - } - __initcall_end = .; - __con_initcall_start = .; - .con_initcall.init : { *(.con_initcall.init) } - __con_initcall_end = .; - SECURITY_INIT + . = ALIGN(PAGE_SIZE); + _eshared = .; /* End of shareable data */ + + . = ALIGN(16); /* Exception table */ + __ex_table : { + __start___ex_table = .; + *(__ex_table) + __stop___ex_table = .; + } + + NOTES + BUG_TABLE + + .data : { /* Data */ + DATA_DATA + CONSTRUCTORS + } + + . = ALIGN(PAGE_SIZE); + .data_nosave : { + __nosave_begin = .; + *(.data.nosave) + } + . = ALIGN(PAGE_SIZE); + __nosave_end = .; + + . = ALIGN(PAGE_SIZE); + .data.page_aligned : { + *(.data.idt) + } + + . = ALIGN(0x100); + .data.cacheline_aligned : { + *(.data.cacheline_aligned) + } + + . = ALIGN(0x100); + .data.read_mostly : { + *(.data.read_mostly) + } + _edata = .; /* End of data section */ + + . = ALIGN(2 * PAGE_SIZE); /* init_task */ + .data.init_task : { + *(.data.init_task) + } + + /* will be freed after init */ + . = ALIGN(PAGE_SIZE); /* Init code and data */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + /* + * .exit.text is discarded at runtime, not link time, + * to deal with references from __bug_table + */ + .exit.text : { + *(.exit.text) + } + + .init.data : { + *(.init.data) + } + . = ALIGN(0x100); + .init.setup : { + __setup_start = .; + *(.init.setup) + __setup_end = .; + } + .initcall.init : { + __initcall_start = .; + INITCALLS + __initcall_end = .; + } + + .con_initcall.init : { + __con_initcall_start = .; + *(.con_initcall.init) + __con_initcall_end = .; + } + SECURITY_INIT #ifdef CONFIG_BLK_DEV_INITRD - . = ALIGN(256); - __initramfs_start = .; - .init.ramfs : { *(.init.initramfs) } - . = ALIGN(2); - __initramfs_end = .; + . = ALIGN(0x100); + .init.ramfs : { + __initramfs_start = .; + *(.init.ramfs) + . = ALIGN(2); + __initramfs_end = .; + } #endif - PERCPU(4096) - . = ALIGN(4096); - __init_end = .; - /* freed after init ends here */ - - __bss_start = .; /* BSS */ - .bss : { *(.bss) } - . = ALIGN(2); - __bss_stop = .; - - _end = . ; - - /* Sections to be discarded */ - /DISCARD/ : { - *(.exit.data) *(.exitcall.exit) - } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } + + PERCPU(PAGE_SIZE) + . = ALIGN(PAGE_SIZE); + __init_end = .; /* freed after init ends here */ + + /* BSS */ + .bss : { + __bss_start = .; + *(.bss) + . = ALIGN(2); + __bss_stop = .; + } + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exit.data) + *(.exitcall.exit) + } + + /* Debugging sections. */ + STABS_DEBUG + DWARF_DEBUG } diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 54055194e9a..4c1ac341ec8 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -468,7 +468,7 @@ typedef struct { __u64 refselmk; __u64 refcmpmk; __u64 reserved; -} __attribute__ ((packed)) pfault_refbk_t; +} __attribute__ ((packed, aligned(8))) pfault_refbk_t; int pfault_init(void) { diff --git a/block/Kconfig b/block/Kconfig index 2484e0e9d89..e10895647f7 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -62,6 +62,10 @@ config BLK_DEV_BSG protocols (e.g. Task Management Functions and SMP in Serial Attached SCSI). +config BLOCK_COMPAT + bool + default y + endif # BLOCK source block/Kconfig.iosched diff --git a/block/Makefile b/block/Makefile index 3cfe7cebaa6..826108190f0 100644 --- a/block/Makefile +++ b/block/Makefile @@ -11,4 +11,4 @@ obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o -obj-$(CONFIG_COMPAT) += compat_ioctl.o +obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 4245b7f80a4..ca4d7f0d09b 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -361,8 +361,7 @@ config BLK_DEV_RAM_SIZE default "4096" help The default value is 4096 kilobytes. Only change this if you know - what are you doing. If you are using IBM S/390, then set this to - 8192. + what are you doing. config BLK_DEV_RAM_BLOCKSIZE int "Default RAM disk block size (bytes)" diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index b04a178e502..f8b79783c8b 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -20,7 +20,6 @@ #include <linux/isapnp.h> #include <linux/interrupt.h> -extern const char *CardType[]; static const char *avm_pci_rev = "$Revision: 1.29.2.4 $"; #define AVM_FRITZ_PCI 1 @@ -726,100 +725,15 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -#ifdef CONFIG_PCI -static struct pci_dev *dev_avm __devinitdata = NULL; -#endif -#ifdef __ISAPNP__ -static struct pnp_card *pnp_avm_c __devinitdata = NULL; -#endif - -int __devinit -setup_avm_pcipnp(struct IsdnCard *card) +static int __devinit avm_setup_rest(struct IsdnCardState *cs) { u_int val, ver; - struct IsdnCardState *cs = card->cs; - char tmp[64]; - strcpy(tmp, avm_pci_rev); - printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp)); - if (cs->typ != ISDN_CTYPE_FRITZPCI) - return (0); - if (card->para[1]) { - /* old manual method */ - cs->hw.avm.cfg_reg = card->para[1]; - cs->irq = card->para[0]; - cs->subtyp = AVM_FRITZ_PNP; - goto ready; - } -#ifdef __ISAPNP__ - if (isapnp_present()) { - struct pnp_dev *pnp_avm_d = NULL; - if ((pnp_avm_c = pnp_find_card( - ISAPNP_VENDOR('A', 'V', 'M'), - ISAPNP_FUNCTION(0x0900), pnp_avm_c))) { - if ((pnp_avm_d = pnp_find_dev(pnp_avm_c, - ISAPNP_VENDOR('A', 'V', 'M'), - ISAPNP_FUNCTION(0x0900), pnp_avm_d))) { - int err; - - pnp_disable_dev(pnp_avm_d); - err = pnp_activate_dev(pnp_avm_d); - if (err<0) { - printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", - __FUNCTION__, err); - return(0); - } - cs->hw.avm.cfg_reg = - pnp_port_start(pnp_avm_d, 0); - cs->irq = pnp_irq(pnp_avm_d, 0); - if (!cs->irq) { - printk(KERN_ERR "FritzPnP:No IRQ\n"); - return(0); - } - if (!cs->hw.avm.cfg_reg) { - printk(KERN_ERR "FritzPnP:No IO address\n"); - return(0); - } - cs->subtyp = AVM_FRITZ_PNP; - goto ready; - } - } - } else { - printk(KERN_INFO "FritzPnP: no ISA PnP present\n"); - } -#endif -#ifdef CONFIG_PCI - if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, - PCI_DEVICE_ID_AVM_A1, dev_avm))) { - if (pci_enable_device(dev_avm)) - return(0); - cs->irq = dev_avm->irq; - if (!cs->irq) { - printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); - return(0); - } - cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1); - if (!cs->hw.avm.cfg_reg) { - printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); - return(0); - } - cs->subtyp = AVM_FRITZ_PCI; - } else { - printk(KERN_WARNING "FritzPCI: No PCI card found\n"); - return(0); - } - cs->irq_flags |= IRQF_SHARED; -#else - printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); - return (0); -#endif /* CONFIG_PCI */ -ready: cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10; if (!request_region(cs->hw.avm.cfg_reg, 32, (cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) { printk(KERN_WARNING - "HiSax: %s config port %x-%x already in use\n", - CardType[card->typ], + "HiSax: Fritz!PCI/PNP config port %x-%x already in use\n", cs->hw.avm.cfg_reg, cs->hw.avm.cfg_reg + 31); return (0); @@ -860,3 +774,137 @@ ready: ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:"); return (1); } + +#ifndef __ISAPNP__ + +static int __devinit avm_pnp_setup(struct IsdnCardState *cs) +{ + return(1); /* no-op: success */ +} + +#else + +static struct pnp_card *pnp_avm_c __devinitdata = NULL; + +static int __devinit avm_pnp_setup(struct IsdnCardState *cs) +{ + struct pnp_dev *pnp_avm_d = NULL; + + if (!isapnp_present()) + return(1); /* no-op: success */ + + if ((pnp_avm_c = pnp_find_card( + ISAPNP_VENDOR('A', 'V', 'M'), + ISAPNP_FUNCTION(0x0900), pnp_avm_c))) { + if ((pnp_avm_d = pnp_find_dev(pnp_avm_c, + ISAPNP_VENDOR('A', 'V', 'M'), + ISAPNP_FUNCTION(0x0900), pnp_avm_d))) { + int err; + + pnp_disable_dev(pnp_avm_d); + err = pnp_activate_dev(pnp_avm_d); + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", + __FUNCTION__, err); + return(0); + } + cs->hw.avm.cfg_reg = + pnp_port_start(pnp_avm_d, 0); + cs->irq = pnp_irq(pnp_avm_d, 0); + if (!cs->irq) { + printk(KERN_ERR "FritzPnP:No IRQ\n"); + return(0); + } + if (!cs->hw.avm.cfg_reg) { + printk(KERN_ERR "FritzPnP:No IO address\n"); + return(0); + } + cs->subtyp = AVM_FRITZ_PNP; + + return (2); /* goto 'ready' label */ + } + } + + return (1); +} + +#endif /* __ISAPNP__ */ + +#ifndef CONFIG_PCI + +static int __devinit avm_pci_setup(struct IsdnCardState *cs) +{ + return(1); /* no-op: success */ +} + +#else + +static struct pci_dev *dev_avm __devinitdata = NULL; + +static int __devinit avm_pci_setup(struct IsdnCardState *cs) +{ + if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, + PCI_DEVICE_ID_AVM_A1, dev_avm))) { + + if (pci_enable_device(dev_avm)) + return(0); + + cs->irq = dev_avm->irq; + if (!cs->irq) { + printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); + return(0); + } + + cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1); + if (!cs->hw.avm.cfg_reg) { + printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); + return(0); + } + + cs->subtyp = AVM_FRITZ_PCI; + } else { + printk(KERN_WARNING "FritzPCI: No PCI card found\n"); + return(0); + } + + cs->irq_flags |= IRQF_SHARED; + + return (1); +} + +#endif /* CONFIG_PCI */ + +int __devinit +setup_avm_pcipnp(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + int rc; + + strcpy(tmp, avm_pci_rev); + printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp)); + + if (cs->typ != ISDN_CTYPE_FRITZPCI) + return (0); + + if (card->para[1]) { + /* old manual method */ + cs->hw.avm.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = AVM_FRITZ_PNP; + goto ready; + } + + rc = avm_pnp_setup(cs); + if (rc < 1) + return (0); + if (rc == 2) + goto ready; + + rc = avm_pci_setup(cs); + if (rc < 1) + return (0); + +ready: + return avm_setup_rest(cs); +} diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c index 6339bb443f6..99ef3b43fcd 100644 --- a/drivers/isdn/hisax/bkm_a8.c +++ b/drivers/isdn/hisax/bkm_a8.c @@ -20,8 +20,6 @@ #include <linux/pci.h> #include "bkm_ax.h" -#ifdef CONFIG_PCI - #define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */ extern const char *CardType[]; @@ -279,12 +277,9 @@ static u_char pci_bus __devinitdata = 0; static u_char pci_device_fn __devinitdata = 0; static u_char pci_irq __devinitdata = 0; -#endif /* CONFIG_PCI */ - int __devinit setup_sct_quadro(struct IsdnCard *card) { -#ifdef CONFIG_PCI struct IsdnCardState *cs = card->cs; char tmp[64]; u_int found = 0; @@ -442,7 +437,4 @@ setup_sct_quadro(struct IsdnCard *card) sct_quadro_subtypes[cs->subtyp], readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID)); return (1); -#else - printk(KERN_ERR "HiSax: bkm_a8 only supported on PCI Systems\n"); -#endif /* CONFIG_PCI */ } diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 6eebeb441bf..82674507874 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -25,8 +25,6 @@ #include <linux/pci.h> #include <linux/isapnp.h> -extern const char *CardType[]; - static const char *Diva_revision = "$Revision: 1.33.2.6 $"; #define byteout(addr,val) outb(val,addr) @@ -906,225 +904,15 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -static struct pci_dev *dev_diva __devinitdata = NULL; -static struct pci_dev *dev_diva_u __devinitdata = NULL; -static struct pci_dev *dev_diva201 __devinitdata = NULL; -static struct pci_dev *dev_diva202 __devinitdata = NULL; - -#ifdef __ISAPNP__ -static struct isapnp_device_id diva_ids[] __devinitdata = { - { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), - ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), - (unsigned long) "Diva picola" }, - { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), - ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x51), - (unsigned long) "Diva picola" }, - { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), - ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), - (unsigned long) "Diva 2.0" }, - { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), - ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x71), - (unsigned long) "Diva 2.0" }, - { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), - ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), - (unsigned long) "Diva 2.01" }, - { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), - ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0xA1), - (unsigned long) "Diva 2.01" }, - { 0, } -}; - -static struct isapnp_device_id *ipid __devinitdata = &diva_ids[0]; -static struct pnp_card *pnp_c __devinitdata = NULL; -#endif - - -int __devinit -setup_diva(struct IsdnCard *card) +static int __devinit setup_diva_common(struct IsdnCardState *cs) { - int bytecnt = 8; + int bytecnt; u_char val; - struct IsdnCardState *cs = card->cs; - char tmp[64]; - - strcpy(tmp, Diva_revision); - printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp)); - if (cs->typ != ISDN_CTYPE_DIEHLDIVA) - return(0); - cs->hw.diva.status = 0; - if (card->para[1]) { - cs->hw.diva.ctrl_reg = 0; - cs->hw.diva.cfg_reg = card->para[1]; - val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR, - cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID); - printk(KERN_INFO "Diva: IPAC version %x\n", val); - if ((val == 1) || (val==2)) { - cs->subtyp = DIVA_IPAC_ISA; - cs->hw.diva.ctrl = 0; - cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA; - cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA; - cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR; - cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR; - test_and_set_bit(HW_IPAC, &cs->HW_Flags); - } else { - cs->subtyp = DIVA_ISA; - cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL; - cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA; - cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA; - cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR; - cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR; - } - cs->irq = card->para[0]; - } else { -#ifdef __ISAPNP__ - if (isapnp_present()) { - struct pnp_dev *pnp_d; - while(ipid->card_vendor) { - if ((pnp_c = pnp_find_card(ipid->card_vendor, - ipid->card_device, pnp_c))) { - pnp_d = NULL; - if ((pnp_d = pnp_find_dev(pnp_c, - ipid->vendor, ipid->function, pnp_d))) { - int err; - - printk(KERN_INFO "HiSax: %s detected\n", - (char *)ipid->driver_data); - pnp_disable_dev(pnp_d); - err = pnp_activate_dev(pnp_d); - if (err<0) { - printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", - __FUNCTION__, err); - return(0); - } - card->para[1] = pnp_port_start(pnp_d, 0); - card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1]) { - printk(KERN_ERR "Diva PnP:some resources are missing %ld/%lx\n", - card->para[0], card->para[1]); - pnp_disable_dev(pnp_d); - return(0); - } - cs->hw.diva.cfg_reg = card->para[1]; - cs->irq = card->para[0]; - if (ipid->function == ISAPNP_FUNCTION(0xA1)) { - cs->subtyp = DIVA_IPAC_ISA; - cs->hw.diva.ctrl = 0; - cs->hw.diva.isac = - card->para[1] + DIVA_IPAC_DATA; - cs->hw.diva.hscx = - card->para[1] + DIVA_IPAC_DATA; - cs->hw.diva.isac_adr = - card->para[1] + DIVA_IPAC_ADR; - cs->hw.diva.hscx_adr = - card->para[1] + DIVA_IPAC_ADR; - test_and_set_bit(HW_IPAC, &cs->HW_Flags); - } else { - cs->subtyp = DIVA_ISA; - cs->hw.diva.ctrl = - card->para[1] + DIVA_ISA_CTRL; - cs->hw.diva.isac = - card->para[1] + DIVA_ISA_ISAC_DATA; - cs->hw.diva.hscx = - card->para[1] + DIVA_HSCX_DATA; - cs->hw.diva.isac_adr = - card->para[1] + DIVA_ISA_ISAC_ADR; - cs->hw.diva.hscx_adr = - card->para[1] + DIVA_HSCX_ADR; - } - goto ready; - } else { - printk(KERN_ERR "Diva PnP: PnP error card found, no device\n"); - return(0); - } - } - ipid++; - pnp_c=NULL; - } - if (!ipid->card_vendor) { - printk(KERN_INFO "Diva PnP: no ISAPnP card found\n"); - } - } -#endif -#ifdef CONFIG_PCI - cs->subtyp = 0; - if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON, - PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) { - if (pci_enable_device(dev_diva)) - return(0); - cs->subtyp = DIVA_PCI; - cs->irq = dev_diva->irq; - cs->hw.diva.cfg_reg = pci_resource_start(dev_diva, 2); - } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON, - PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) { - if (pci_enable_device(dev_diva_u)) - return(0); - cs->subtyp = DIVA_PCI; - cs->irq = dev_diva_u->irq; - cs->hw.diva.cfg_reg = pci_resource_start(dev_diva_u, 2); - } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON, - PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) { - if (pci_enable_device(dev_diva201)) - return(0); - cs->subtyp = DIVA_IPAC_PCI; - cs->irq = dev_diva201->irq; - cs->hw.diva.pci_cfg = - (ulong) ioremap(pci_resource_start(dev_diva201, 0), 4096); - cs->hw.diva.cfg_reg = - (ulong) ioremap(pci_resource_start(dev_diva201, 1), 4096); - } else if ((dev_diva202 = pci_find_device(PCI_VENDOR_ID_EICON, - PCI_DEVICE_ID_EICON_DIVA202, dev_diva202))) { - if (pci_enable_device(dev_diva202)) - return(0); - cs->subtyp = DIVA_IPACX_PCI; - cs->irq = dev_diva202->irq; - cs->hw.diva.pci_cfg = - (ulong) ioremap(pci_resource_start(dev_diva202, 0), 4096); - cs->hw.diva.cfg_reg = - (ulong) ioremap(pci_resource_start(dev_diva202, 1), 4096); - } else { - printk(KERN_WARNING "Diva: No PCI card found\n"); - return(0); - } - - if (!cs->irq) { - printk(KERN_WARNING "Diva: No IRQ for PCI card found\n"); - iounmap_diva(cs); - return(0); - } - - if (!cs->hw.diva.cfg_reg) { - printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); - iounmap_diva(cs); - return(0); - } - cs->irq_flags |= IRQF_SHARED; -#else - printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n"); - printk(KERN_WARNING "Diva: unable to config DIVA PCI\n"); - return (0); -#endif /* CONFIG_PCI */ - if ((cs->subtyp == DIVA_IPAC_PCI) || - (cs->subtyp == DIVA_IPACX_PCI) ) { - cs->hw.diva.ctrl = 0; - cs->hw.diva.isac = 0; - cs->hw.diva.hscx = 0; - cs->hw.diva.isac_adr = 0; - cs->hw.diva.hscx_adr = 0; - test_and_set_bit(HW_IPAC, &cs->HW_Flags); - bytecnt = 0; - } else { - cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL; - cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA; - cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA; - cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR; - cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR; - bytecnt = 32; - } - } -#ifdef __ISAPNP__ -ready: -#endif + if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA)) + bytecnt = 8; + else + bytecnt = 32; printk(KERN_INFO "Diva: %s card configured at %#lx IRQ %d\n", @@ -1145,7 +933,7 @@ ready: if (!request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn")) { printk(KERN_WARNING "HiSax: %s config port %lx-%lx already in use\n", - CardType[card->typ], + "diva", cs->hw.diva.cfg_reg, cs->hw.diva.cfg_reg + bytecnt); iounmap_diva(cs); @@ -1206,3 +994,290 @@ ready: } return (1); } + +#ifdef CONFIG_ISA + +static int __devinit setup_diva_isa(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + u_char val; + + if (!card->para[1]) + return (-1); /* card not found; continue search */ + + cs->hw.diva.ctrl_reg = 0; + cs->hw.diva.cfg_reg = card->para[1]; + val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR, + cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID); + printk(KERN_INFO "Diva: IPAC version %x\n", val); + if ((val == 1) || (val==2)) { + cs->subtyp = DIVA_IPAC_ISA; + cs->hw.diva.ctrl = 0; + cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA; + cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA; + cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR; + cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + cs->subtyp = DIVA_ISA; + cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL; + cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA; + cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR; + cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR; + } + cs->irq = card->para[0]; + + return (1); /* card found */ +} + +#else /* if !CONFIG_ISA */ + +static int __devinit setup_diva_isa(struct IsdnCard *card) +{ + return (-1); /* card not found; continue search */ +} + +#endif /* CONFIG_ISA */ + +#ifdef __ISAPNP__ +static struct isapnp_device_id diva_ids[] __devinitdata = { + { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), + ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), + (unsigned long) "Diva picola" }, + { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), + ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x51), + (unsigned long) "Diva picola" }, + { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), + ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), + (unsigned long) "Diva 2.0" }, + { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), + ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x71), + (unsigned long) "Diva 2.0" }, + { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), + ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), + (unsigned long) "Diva 2.01" }, + { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), + ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0xA1), + (unsigned long) "Diva 2.01" }, + { 0, } +}; + +static struct isapnp_device_id *ipid __devinitdata = &diva_ids[0]; +static struct pnp_card *pnp_c __devinitdata = NULL; + +static int __devinit setup_diva_isapnp(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + struct pnp_dev *pnp_d; + + if (!isapnp_present()) + return (-1); /* card not found; continue search */ + + while(ipid->card_vendor) { + if ((pnp_c = pnp_find_card(ipid->card_vendor, + ipid->card_device, pnp_c))) { + pnp_d = NULL; + if ((pnp_d = pnp_find_dev(pnp_c, + ipid->vendor, ipid->function, pnp_d))) { + int err; + + printk(KERN_INFO "HiSax: %s detected\n", + (char *)ipid->driver_data); + pnp_disable_dev(pnp_d); + err = pnp_activate_dev(pnp_d); + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", + __FUNCTION__, err); + return(0); + } + card->para[1] = pnp_port_start(pnp_d, 0); + card->para[0] = pnp_irq(pnp_d, 0); + if (!card->para[0] || !card->para[1]) { + printk(KERN_ERR "Diva PnP:some resources are missing %ld/%lx\n", + card->para[0], card->para[1]); + pnp_disable_dev(pnp_d); + return(0); + } + cs->hw.diva.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (ipid->function == ISAPNP_FUNCTION(0xA1)) { + cs->subtyp = DIVA_IPAC_ISA; + cs->hw.diva.ctrl = 0; + cs->hw.diva.isac = + card->para[1] + DIVA_IPAC_DATA; + cs->hw.diva.hscx = + card->para[1] + DIVA_IPAC_DATA; + cs->hw.diva.isac_adr = + card->para[1] + DIVA_IPAC_ADR; + cs->hw.diva.hscx_adr = + card->para[1] + DIVA_IPAC_ADR; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + cs->subtyp = DIVA_ISA; + cs->hw.diva.ctrl = + card->para[1] + DIVA_ISA_CTRL; + cs->hw.diva.isac = + card->para[1] + DIVA_ISA_ISAC_DATA; + cs->hw.diva.hscx = + card->para[1] + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = + card->para[1] + DIVA_ISA_ISAC_ADR; + cs->hw.diva.hscx_adr = + card->para[1] + DIVA_HSCX_ADR; + } + return (1); /* card found */ + } else { + printk(KERN_ERR "Diva PnP: PnP error card found, no device\n"); + return(0); + } + } + ipid++; + pnp_c=NULL; + } + + return (-1); /* card not found; continue search */ +} + +#else /* if !ISAPNP */ + +static int __devinit setup_diva_isapnp(struct IsdnCard *card) +{ + return (-1); /* card not found; continue search */ +} + +#endif /* ISAPNP */ + +#ifdef CONFIG_PCI +static struct pci_dev *dev_diva __devinitdata = NULL; +static struct pci_dev *dev_diva_u __devinitdata = NULL; +static struct pci_dev *dev_diva201 __devinitdata = NULL; +static struct pci_dev *dev_diva202 __devinitdata = NULL; + +static int __devinit setup_diva_pci(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + + cs->subtyp = 0; + if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON, + PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) { + if (pci_enable_device(dev_diva)) + return(0); + cs->subtyp = DIVA_PCI; + cs->irq = dev_diva->irq; + cs->hw.diva.cfg_reg = pci_resource_start(dev_diva, 2); + } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON, + PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) { + if (pci_enable_device(dev_diva_u)) + return(0); + cs->subtyp = DIVA_PCI; + cs->irq = dev_diva_u->irq; + cs->hw.diva.cfg_reg = pci_resource_start(dev_diva_u, 2); + } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON, + PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) { + if (pci_enable_device(dev_diva201)) + return(0); + cs->subtyp = DIVA_IPAC_PCI; + cs->irq = dev_diva201->irq; + cs->hw.diva.pci_cfg = + (ulong) ioremap(pci_resource_start(dev_diva201, 0), 4096); + cs->hw.diva.cfg_reg = + (ulong) ioremap(pci_resource_start(dev_diva201, 1), 4096); + } else if ((dev_diva202 = pci_find_device(PCI_VENDOR_ID_EICON, + PCI_DEVICE_ID_EICON_DIVA202, dev_diva202))) { + if (pci_enable_device(dev_diva202)) + return(0); + cs->subtyp = DIVA_IPACX_PCI; + cs->irq = dev_diva202->irq; + cs->hw.diva.pci_cfg = + (ulong) ioremap(pci_resource_start(dev_diva202, 0), 4096); + cs->hw.diva.cfg_reg = + (ulong) ioremap(pci_resource_start(dev_diva202, 1), 4096); + } else { + return (-1); /* card not found; continue search */ + } + + if (!cs->irq) { + printk(KERN_WARNING "Diva: No IRQ for PCI card found\n"); + iounmap_diva(cs); + return(0); + } + + if (!cs->hw.diva.cfg_reg) { + printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); + iounmap_diva(cs); + return(0); + } + cs->irq_flags |= IRQF_SHARED; + + if ((cs->subtyp == DIVA_IPAC_PCI) || + (cs->subtyp == DIVA_IPACX_PCI) ) { + cs->hw.diva.ctrl = 0; + cs->hw.diva.isac = 0; + cs->hw.diva.hscx = 0; + cs->hw.diva.isac_adr = 0; + cs->hw.diva.hscx_adr = 0; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL; + cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA; + cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR; + cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR; + } + + return (1); /* card found */ +} + +#else /* if !CONFIG_PCI */ + +static int __devinit setup_diva_pci(struct IsdnCard *card) +{ + return (-1); /* card not found; continue search */ +} + +#endif /* CONFIG_PCI */ + +int __devinit +setup_diva(struct IsdnCard *card) +{ + int rc, have_card = 0; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Diva_revision); + printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_DIEHLDIVA) + return(0); + cs->hw.diva.status = 0; + + rc = setup_diva_isa(card); + if (!rc) + return rc; + if (rc > 0) { + have_card = 1; + goto ready; + } + + rc = setup_diva_isapnp(card); + if (!rc) + return rc; + if (rc > 0) { + have_card = 1; + goto ready; + } + + rc = setup_diva_pci(card); + if (!rc) + return rc; + if (rc > 0) + have_card = 1; + +ready: + if (!have_card) { + printk(KERN_WARNING "Diva: No ISA, ISAPNP or PCI card found\n"); + return(0); + } + + return setup_diva_common(card->cs); +} diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index fab3e4ea059..0c1351b2384 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -30,8 +30,6 @@ #include <linux/serial.h> #include <linux/serial_reg.h> -extern const char *CardType[]; - static const char *Elsa_revision = "$Revision: 2.32.2.4 $"; static const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", @@ -832,8 +830,75 @@ probe_elsa(struct IsdnCardState *cs) return (CARD_portlist[i]); } -static struct pci_dev *dev_qs1000 __devinitdata = NULL; -static struct pci_dev *dev_qs3000 __devinitdata = NULL; +static int __devinit +setup_elsa_isa(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + u_char val; + + cs->hw.elsa.base = card->para[0]; + printk(KERN_INFO "Elsa: Microlink IO probing\n"); + if (cs->hw.elsa.base) { + if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base, + cs->typ))) { + printk(KERN_WARNING + "Elsa: no Elsa Microlink at %#lx\n", + cs->hw.elsa.base); + return (0); + } + } else + cs->hw.elsa.base = probe_elsa(cs); + + if (!cs->hw.elsa.base) { + printk(KERN_WARNING + "No Elsa Microlink found\n"); + return (0); + } + + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + val = bytein(cs->hw.elsa.cfg); + if (cs->subtyp == ELSA_PC) { + const u_char CARD_IrqTab[8] = + {7, 3, 5, 9, 0, 0, 0, 0}; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2]; + } else if (cs->subtyp == ELSA_PCC8) { + const u_char CARD_IrqTab[8] = + {7, 3, 5, 9, 0, 0, 0, 0}; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4]; + } else { + const u_char CARD_IrqTab[8] = + {15, 10, 15, 3, 11, 5, 11, 9}; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3]; + } + val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE; + if (val < 3) + val |= 8; + val += 'A' - 3; + if (val == 'B' || val == 'C') + val ^= 1; + if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G')) + val = 'C'; + printk(KERN_INFO + "Elsa: %s found at %#lx Rev.:%c IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + val, cs->irq); + val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD; + if (val) { + printk(KERN_WARNING + "Elsa: Microlink S0 bus power bad\n"); + cs->hw.elsa.status |= ELSA_BAD_PWR; + } + + return (1); +} #ifdef __ISAPNP__ static struct isapnp_device_id elsa_ids[] __devinitdata = { @@ -848,233 +913,194 @@ static struct isapnp_device_id elsa_ids[] __devinitdata = { static struct isapnp_device_id *ipid __devinitdata = &elsa_ids[0]; static struct pnp_card *pnp_c __devinitdata = NULL; -#endif +#endif /* __ISAPNP__ */ -int __devinit -setup_elsa(struct IsdnCard *card) +static int __devinit +setup_elsa_isapnp(struct IsdnCard *card) { - int bytecnt; - u_char val; struct IsdnCardState *cs = card->cs; - char tmp[64]; - strcpy(tmp, Elsa_revision); - printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp)); - cs->hw.elsa.ctrl_reg = 0; - cs->hw.elsa.status = 0; - cs->hw.elsa.MFlag = 0; - cs->subtyp = 0; - if (cs->typ == ISDN_CTYPE_ELSA) { - cs->hw.elsa.base = card->para[0]; - printk(KERN_INFO "Elsa: Microlink IO probing\n"); - if (cs->hw.elsa.base) { - if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base, - cs->typ))) { - printk(KERN_WARNING - "Elsa: no Elsa Microlink at %#lx\n", - cs->hw.elsa.base); - return (0); - } - } else - cs->hw.elsa.base = probe_elsa(cs); - if (cs->hw.elsa.base) { - cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; - cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; - cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; - cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; - cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC; - cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; - cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; - cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; - val = bytein(cs->hw.elsa.cfg); - if (cs->subtyp == ELSA_PC) { - const u_char CARD_IrqTab[8] = - {7, 3, 5, 9, 0, 0, 0, 0}; - cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2]; - } else if (cs->subtyp == ELSA_PCC8) { - const u_char CARD_IrqTab[8] = - {7, 3, 5, 9, 0, 0, 0, 0}; - cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4]; - } else { - const u_char CARD_IrqTab[8] = - {15, 10, 15, 3, 11, 5, 11, 9}; - cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3]; - } - val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE; - if (val < 3) - val |= 8; - val += 'A' - 3; - if (val == 'B' || val == 'C') - val ^= 1; - if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G')) - val = 'C'; - printk(KERN_INFO - "Elsa: %s found at %#lx Rev.:%c IRQ %d\n", - Elsa_Types[cs->subtyp], - cs->hw.elsa.base, - val, cs->irq); - val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD; - if (val) { - printk(KERN_WARNING - "Elsa: Microlink S0 bus power bad\n"); - cs->hw.elsa.status |= ELSA_BAD_PWR; - } - } else { - printk(KERN_WARNING - "No Elsa Microlink found\n"); - return (0); - } - } else if (cs->typ == ISDN_CTYPE_ELSA_PNP) { #ifdef __ISAPNP__ - if (!card->para[1] && isapnp_present()) { - struct pnp_dev *pnp_d; - while(ipid->card_vendor) { - if ((pnp_c = pnp_find_card(ipid->card_vendor, - ipid->card_device, pnp_c))) { - pnp_d = NULL; - if ((pnp_d = pnp_find_dev(pnp_c, - ipid->vendor, ipid->function, pnp_d))) { - int err; - - printk(KERN_INFO "HiSax: %s detected\n", - (char *)ipid->driver_data); + if (!card->para[1] && isapnp_present()) { + struct pnp_dev *pnp_d; + while(ipid->card_vendor) { + if ((pnp_c = pnp_find_card(ipid->card_vendor, + ipid->card_device, pnp_c))) { + pnp_d = NULL; + if ((pnp_d = pnp_find_dev(pnp_c, + ipid->vendor, ipid->function, pnp_d))) { + int err; + + printk(KERN_INFO "HiSax: %s detected\n", + (char *)ipid->driver_data); + pnp_disable_dev(pnp_d); + err = pnp_activate_dev(pnp_d); + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", + __FUNCTION__, err); + return(0); + } + card->para[1] = pnp_port_start(pnp_d, 0); + card->para[0] = pnp_irq(pnp_d, 0); + + if (!card->para[0] || !card->para[1]) { + printk(KERN_ERR "Elsa PnP:some resources are missing %ld/%lx\n", + card->para[0], card->para[1]); pnp_disable_dev(pnp_d); - err = pnp_activate_dev(pnp_d); - if (err<0) { - printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", - __FUNCTION__, err); - return(0); - } - card->para[1] = pnp_port_start(pnp_d, 0); - card->para[0] = pnp_irq(pnp_d, 0); - - if (!card->para[0] || !card->para[1]) { - printk(KERN_ERR "Elsa PnP:some resources are missing %ld/%lx\n", - card->para[0], card->para[1]); - pnp_disable_dev(pnp_d); - return(0); - } - if (ipid->function == ISAPNP_FUNCTION(0x133)) - cs->subtyp = ELSA_QS1000; - else - cs->subtyp = ELSA_QS3000; - break; - } else { - printk(KERN_ERR "Elsa PnP: PnP error card found, no device\n"); return(0); } + if (ipid->function == ISAPNP_FUNCTION(0x133)) + cs->subtyp = ELSA_QS1000; + else + cs->subtyp = ELSA_QS3000; + break; + } else { + printk(KERN_ERR "Elsa PnP: PnP error card found, no device\n"); + return(0); } - ipid++; - pnp_c=NULL; - } - if (!ipid->card_vendor) { - printk(KERN_INFO "Elsa PnP: no ISAPnP card found\n"); - return(0); } + ipid++; + pnp_c=NULL; + } + if (!ipid->card_vendor) { + printk(KERN_INFO "Elsa PnP: no ISAPnP card found\n"); + return(0); } -#endif - if (card->para[1] && card->para[0]) { - cs->hw.elsa.base = card->para[1]; - cs->irq = card->para[0]; - if (!cs->subtyp) - cs->subtyp = ELSA_QS1000; - } else { - printk(KERN_ERR "Elsa PnP: no parameter\n"); - } - cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; - cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; - cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; - cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; - cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; - cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; - cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; - printk(KERN_INFO - "Elsa: %s defined at %#lx IRQ %d\n", - Elsa_Types[cs->subtyp], - cs->hw.elsa.base, - cs->irq); - } else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) { + } +#endif /* __ISAPNP__ */ + + if (card->para[1] && card->para[0]) { cs->hw.elsa.base = card->para[1]; cs->irq = card->para[0]; - val = readreg(cs->hw.elsa.base + 0, cs->hw.elsa.base + 2, IPAC_ID); - if ((val == 1) || (val == 2)) { /* IPAC version 1.1/1.2 */ - cs->subtyp = ELSA_PCMCIA_IPAC; - cs->hw.elsa.ale = cs->hw.elsa.base + 0; - cs->hw.elsa.isac = cs->hw.elsa.base + 2; - cs->hw.elsa.hscx = cs->hw.elsa.base + 2; - test_and_set_bit(HW_IPAC, &cs->HW_Flags); - } else { - cs->subtyp = ELSA_PCMCIA; - cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; - cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; - cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; - } - cs->hw.elsa.timer = 0; - cs->hw.elsa.trig = 0; - cs->hw.elsa.ctrl = 0; - cs->irq_flags |= IRQF_SHARED; - printk(KERN_INFO - "Elsa: %s defined at %#lx IRQ %d\n", - Elsa_Types[cs->subtyp], - cs->hw.elsa.base, - cs->irq); - } else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { + if (!cs->subtyp) + cs->subtyp = ELSA_QS1000; + } else { + printk(KERN_ERR "Elsa PnP: no parameter\n"); + } + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; + printk(KERN_INFO + "Elsa: %s defined at %#lx IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); + + return (1); +} + +static void __devinit +setup_elsa_pcmcia(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + u_char val; + + cs->hw.elsa.base = card->para[1]; + cs->irq = card->para[0]; + val = readreg(cs->hw.elsa.base + 0, cs->hw.elsa.base + 2, IPAC_ID); + if ((val == 1) || (val == 2)) { /* IPAC version 1.1/1.2 */ + cs->subtyp = ELSA_PCMCIA_IPAC; + cs->hw.elsa.ale = cs->hw.elsa.base + 0; + cs->hw.elsa.isac = cs->hw.elsa.base + 2; + cs->hw.elsa.hscx = cs->hw.elsa.base + 2; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + cs->subtyp = ELSA_PCMCIA; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + } + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + cs->hw.elsa.ctrl = 0; + cs->irq_flags |= IRQF_SHARED; + printk(KERN_INFO + "Elsa: %s defined at %#lx IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); +} + #ifdef CONFIG_PCI - cs->subtyp = 0; - if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA, - PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) { - if (pci_enable_device(dev_qs1000)) - return(0); - cs->subtyp = ELSA_QS1000PCI; - cs->irq = dev_qs1000->irq; - cs->hw.elsa.cfg = pci_resource_start(dev_qs1000, 1); - cs->hw.elsa.base = pci_resource_start(dev_qs1000, 3); - } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA, - PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) { - if (pci_enable_device(dev_qs3000)) - return(0); - cs->subtyp = ELSA_QS3000PCI; - cs->irq = dev_qs3000->irq; - cs->hw.elsa.cfg = pci_resource_start(dev_qs3000, 1); - cs->hw.elsa.base = pci_resource_start(dev_qs3000, 3); - } else { - printk(KERN_WARNING "Elsa: No PCI card found\n"); +static struct pci_dev *dev_qs1000 __devinitdata = NULL; +static struct pci_dev *dev_qs3000 __devinitdata = NULL; + +static int __devinit +setup_elsa_pci(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + + cs->subtyp = 0; + if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA, + PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) { + if (pci_enable_device(dev_qs1000)) return(0); - } - if (!cs->irq) { - printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n"); + cs->subtyp = ELSA_QS1000PCI; + cs->irq = dev_qs1000->irq; + cs->hw.elsa.cfg = pci_resource_start(dev_qs1000, 1); + cs->hw.elsa.base = pci_resource_start(dev_qs1000, 3); + } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA, + PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) { + if (pci_enable_device(dev_qs3000)) return(0); - } + cs->subtyp = ELSA_QS3000PCI; + cs->irq = dev_qs3000->irq; + cs->hw.elsa.cfg = pci_resource_start(dev_qs3000, 1); + cs->hw.elsa.base = pci_resource_start(dev_qs3000, 3); + } else { + printk(KERN_WARNING "Elsa: No PCI card found\n"); + return(0); + } + if (!cs->irq) { + printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n"); + return(0); + } + + if (!(cs->hw.elsa.base && cs->hw.elsa.cfg)) { + printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); + return(0); + } + if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) { + printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n"); + printk(KERN_WARNING "Elsa: If your system hangs now, read\n"); + printk(KERN_WARNING "Elsa: Documentation/isdn/README.HiSax\n"); + } + cs->hw.elsa.ale = cs->hw.elsa.base; + cs->hw.elsa.isac = cs->hw.elsa.base +1; + cs->hw.elsa.hscx = cs->hw.elsa.base +1; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + cs->irq_flags |= IRQF_SHARED; + printk(KERN_INFO + "Elsa: %s defined at %#lx/0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->hw.elsa.cfg, + cs->irq); + + return (1); +} - if (!(cs->hw.elsa.base && cs->hw.elsa.cfg)) { - printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); - return(0); - } - if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) { - printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n"); - printk(KERN_WARNING "Elsa: If your system hangs now, read\n"); - printk(KERN_WARNING "Elsa: Documentation/isdn/README.HiSax\n"); - } - cs->hw.elsa.ale = cs->hw.elsa.base; - cs->hw.elsa.isac = cs->hw.elsa.base +1; - cs->hw.elsa.hscx = cs->hw.elsa.base +1; - test_and_set_bit(HW_IPAC, &cs->HW_Flags); - cs->hw.elsa.timer = 0; - cs->hw.elsa.trig = 0; - cs->irq_flags |= IRQF_SHARED; - printk(KERN_INFO - "Elsa: %s defined at %#lx/0x%x IRQ %d\n", - Elsa_Types[cs->subtyp], - cs->hw.elsa.base, - cs->hw.elsa.cfg, - cs->irq); #else - printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n"); - printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n"); - return (0); + +static void __devinit +setup_elsa_pci(struct IsdnCard *card) +{ + return (1); +} #endif /* CONFIG_PCI */ - } else - return (0); + +static int __devinit +setup_elsa_common(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + u_char val; + int bytecnt; switch (cs->subtyp) { case ELSA_PC: @@ -1104,8 +1130,7 @@ setup_elsa(struct IsdnCard *card) here, it would fail. */ if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && !request_region(cs->hw.elsa.base, bytecnt, "elsa isdn")) { printk(KERN_WARNING - "HiSax: %s config port %#lx-%#lx already in use\n", - CardType[card->typ], + "HiSax: ELSA config port %#lx-%#lx already in use\n", cs->hw.elsa.base, cs->hw.elsa.base + bytecnt); return (0); @@ -1113,8 +1138,7 @@ setup_elsa(struct IsdnCard *card) if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { if (!request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci")) { printk(KERN_WARNING - "HiSax: %s pci port %x-%x already in use\n", - CardType[card->typ], + "HiSax: ELSA pci port %x-%x already in use\n", cs->hw.elsa.cfg, cs->hw.elsa.cfg + 0x80); release_region(cs->hw.elsa.base, bytecnt); @@ -1186,3 +1210,41 @@ setup_elsa(struct IsdnCard *card) } return (1); } + +int __devinit +setup_elsa(struct IsdnCard *card) +{ + int rc; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Elsa_revision); + printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp)); + cs->hw.elsa.ctrl_reg = 0; + cs->hw.elsa.status = 0; + cs->hw.elsa.MFlag = 0; + cs->subtyp = 0; + + if (cs->typ == ISDN_CTYPE_ELSA) { + rc = setup_elsa_isa(card); + if (!rc) + return (0); + + } else if (cs->typ == ISDN_CTYPE_ELSA_PNP) { + rc = setup_elsa_isapnp(card); + if (!rc) + return (0); + + } else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) + setup_elsa_pcmcia(card); + + else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { + rc = setup_elsa_pci(card); + if (!rc) + return (0); + + } else + return (0); + + return setup_elsa_common(card); +} diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c index ad06f3cc60f..03dfc32166a 100644 --- a/drivers/isdn/hisax/sedlbauer.c +++ b/drivers/isdn/hisax/sedlbauer.c @@ -518,8 +518,6 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -static struct pci_dev *dev_sedl __devinitdata = NULL; - #ifdef __ISAPNP__ static struct isapnp_device_id sedl_ids[] __devinitdata = { { ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x01), @@ -533,15 +531,158 @@ static struct isapnp_device_id sedl_ids[] __devinitdata = { static struct isapnp_device_id *ipid __devinitdata = &sedl_ids[0]; static struct pnp_card *pnp_c __devinitdata = NULL; -#endif + +static int __devinit +setup_sedlbauer_isapnp(struct IsdnCard *card, int *bytecnt) +{ + struct IsdnCardState *cs = card->cs; + struct pnp_dev *pnp_d; + + if (!isapnp_present()) + return -1; + + while(ipid->card_vendor) { + if ((pnp_c = pnp_find_card(ipid->card_vendor, + ipid->card_device, pnp_c))) { + pnp_d = NULL; + if ((pnp_d = pnp_find_dev(pnp_c, + ipid->vendor, ipid->function, pnp_d))) { + int err; + + printk(KERN_INFO "HiSax: %s detected\n", + (char *)ipid->driver_data); + pnp_disable_dev(pnp_d); + err = pnp_activate_dev(pnp_d); + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", + __FUNCTION__, err); + return(0); + } + card->para[1] = pnp_port_start(pnp_d, 0); + card->para[0] = pnp_irq(pnp_d, 0); + + if (!card->para[0] || !card->para[1]) { + printk(KERN_ERR "Sedlbauer PnP:some resources are missing %ld/%lx\n", + card->para[0], card->para[1]); + pnp_disable_dev(pnp_d); + return(0); + } + cs->hw.sedl.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (ipid->function == ISAPNP_FUNCTION(0x2)) { + cs->subtyp = SEDL_SPEED_FAX; + cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; + *bytecnt = 16; + } else { + cs->subtyp = SEDL_SPEED_CARD_WIN; + cs->hw.sedl.chip = SEDL_CHIP_TEST; + } + + return (1); + } else { + printk(KERN_ERR "Sedlbauer PnP: PnP error card found, no device\n"); + return(0); + } + } + ipid++; + pnp_c = NULL; + } + + printk(KERN_INFO "Sedlbauer PnP: no ISAPnP card found\n"); + return -1; +} +#else + +static int __devinit +setup_sedlbauer_isapnp(struct IsdnCard *card, int *bytecnt) +{ + return -1; +} +#endif /* __ISAPNP__ */ + +#ifdef CONFIG_PCI +static struct pci_dev *dev_sedl __devinitdata = NULL; + +static int __devinit +setup_sedlbauer_pci(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + u16 sub_vendor_id, sub_id; + + if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET, + PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) { + if (pci_enable_device(dev_sedl)) + return(0); + cs->irq = dev_sedl->irq; + if (!cs->irq) { + printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n"); + return(0); + } + cs->hw.sedl.cfg_reg = pci_resource_start(dev_sedl, 0); + } else { + printk(KERN_WARNING "Sedlbauer: No PCI card found\n"); + return(0); + } + cs->irq_flags |= IRQF_SHARED; + cs->hw.sedl.bus = SEDL_BUS_PCI; + sub_vendor_id = dev_sedl->subsystem_vendor; + sub_id = dev_sedl->subsystem_device; + printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n", + sub_vendor_id, sub_id); + printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n", + cs->hw.sedl.cfg_reg); + if (sub_id != PCI_SUB_ID_SEDLBAUER) { + printk(KERN_ERR "Sedlbauer: unknown sub id %#x\n", sub_id); + return(0); + } + if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PYRAMID) { + cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; + cs->subtyp = SEDL_SPEEDFAX_PYRAMID; + } else if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PCI) { + cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; + cs->subtyp = SEDL_SPEEDFAX_PCI; + } else if (sub_vendor_id == PCI_SUBVENDOR_HST_SAPHIR3) { + cs->hw.sedl.chip = SEDL_CHIP_IPAC; + cs->subtyp = HST_SAPHIR3; + } else if (sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER_PCI) { + cs->hw.sedl.chip = SEDL_CHIP_IPAC; + cs->subtyp = SEDL_SPEED_PCI; + } else { + printk(KERN_ERR "Sedlbauer: unknown sub vendor id %#x\n", + sub_vendor_id); + return(0); + } + + cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON; + cs->hw.sedl.reset_off = SEDL_ISAR_PCI_ISAR_RESET_OFF; + byteout(cs->hw.sedl.cfg_reg, 0xff); + byteout(cs->hw.sedl.cfg_reg, 0x00); + byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); + byteout(cs->hw.sedl.cfg_reg+ 5, 0); /* disable all IRQ */ + byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on); + mdelay(2); + byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); + mdelay(10); + + return (1); +} + +#else + +static int __devinit +setup_sedlbauer_pci(struct IsdnCard *card) +{ + return (1); +} + +#endif /* CONFIG_PCI */ int __devinit setup_sedlbauer(struct IsdnCard *card) { - int bytecnt, ver, val; + int bytecnt = 8, ver, val, rc; struct IsdnCardState *cs = card->cs; char tmp[64]; - u16 sub_vendor_id, sub_id; strcpy(tmp, Sedlbauer_revision); printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp)); @@ -569,124 +710,21 @@ setup_sedlbauer(struct IsdnCard *card) bytecnt = 16; } } else { -#ifdef __ISAPNP__ - if (isapnp_present()) { - struct pnp_dev *pnp_d; - while(ipid->card_vendor) { - if ((pnp_c = pnp_find_card(ipid->card_vendor, - ipid->card_device, pnp_c))) { - pnp_d = NULL; - if ((pnp_d = pnp_find_dev(pnp_c, - ipid->vendor, ipid->function, pnp_d))) { - int err; - - printk(KERN_INFO "HiSax: %s detected\n", - (char *)ipid->driver_data); - pnp_disable_dev(pnp_d); - err = pnp_activate_dev(pnp_d); - if (err<0) { - printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", - __FUNCTION__, err); - return(0); - } - card->para[1] = pnp_port_start(pnp_d, 0); - card->para[0] = pnp_irq(pnp_d, 0); - - if (!card->para[0] || !card->para[1]) { - printk(KERN_ERR "Sedlbauer PnP:some resources are missing %ld/%lx\n", - card->para[0], card->para[1]); - pnp_disable_dev(pnp_d); - return(0); - } - cs->hw.sedl.cfg_reg = card->para[1]; - cs->irq = card->para[0]; - if (ipid->function == ISAPNP_FUNCTION(0x2)) { - cs->subtyp = SEDL_SPEED_FAX; - cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; - bytecnt = 16; - } else { - cs->subtyp = SEDL_SPEED_CARD_WIN; - cs->hw.sedl.chip = SEDL_CHIP_TEST; - } - goto ready; - } else { - printk(KERN_ERR "Sedlbauer PnP: PnP error card found, no device\n"); - return(0); - } - } - ipid++; - pnp_c = NULL; - } - if (!ipid->card_vendor) { - printk(KERN_INFO "Sedlbauer PnP: no ISAPnP card found\n"); - } - } -#endif -/* Probe for Sedlbauer speed pci */ -#ifdef CONFIG_PCI - if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET, - PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) { - if (pci_enable_device(dev_sedl)) - return(0); - cs->irq = dev_sedl->irq; - if (!cs->irq) { - printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n"); - return(0); - } - cs->hw.sedl.cfg_reg = pci_resource_start(dev_sedl, 0); - } else { - printk(KERN_WARNING "Sedlbauer: No PCI card found\n"); - return(0); - } - cs->irq_flags |= IRQF_SHARED; - cs->hw.sedl.bus = SEDL_BUS_PCI; - sub_vendor_id = dev_sedl->subsystem_vendor; - sub_id = dev_sedl->subsystem_device; - printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n", - sub_vendor_id, sub_id); - printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n", - cs->hw.sedl.cfg_reg); - if (sub_id != PCI_SUB_ID_SEDLBAUER) { - printk(KERN_ERR "Sedlbauer: unknown sub id %#x\n", sub_id); - return(0); - } - if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PYRAMID) { - cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; - cs->subtyp = SEDL_SPEEDFAX_PYRAMID; - } else if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PCI) { - cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; - cs->subtyp = SEDL_SPEEDFAX_PCI; - } else if (sub_vendor_id == PCI_SUBVENDOR_HST_SAPHIR3) { - cs->hw.sedl.chip = SEDL_CHIP_IPAC; - cs->subtyp = HST_SAPHIR3; - } else if (sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER_PCI) { - cs->hw.sedl.chip = SEDL_CHIP_IPAC; - cs->subtyp = SEDL_SPEED_PCI; - } else { - printk(KERN_ERR "Sedlbauer: unknown sub vendor id %#x\n", - sub_vendor_id); - return(0); - } + rc = setup_sedlbauer_isapnp(card, &bytecnt); + if (!rc) + return (0); + if (rc > 0) + goto ready; + + /* Probe for Sedlbauer speed pci */ + rc = setup_sedlbauer_pci(card); + if (!rc) + return (0); + bytecnt = 256; - cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON; - cs->hw.sedl.reset_off = SEDL_ISAR_PCI_ISAR_RESET_OFF; - byteout(cs->hw.sedl.cfg_reg, 0xff); - byteout(cs->hw.sedl.cfg_reg, 0x00); - byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); - byteout(cs->hw.sedl.cfg_reg+ 5, 0); /* disable all IRQ */ - byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on); - mdelay(2); - byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); - mdelay(10); -#else - printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n"); - return (0); -#endif /* CONFIG_PCI */ } -#ifdef __ISAPNP__ ready: -#endif /* In case of the sedlbauer pcmcia card, this region is in use, * reserved for us by the card manager. So we do not check it diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c index d09f6d033f1..4393003ae16 100644 --- a/drivers/isdn/hisax/telespci.c +++ b/drivers/isdn/hisax/telespci.c @@ -295,11 +295,12 @@ setup_telespci(struct IsdnCard *card) #ifdef __BIG_ENDIAN #error "not running on big endian machines now" #endif + strcpy(tmp, telespci_revision); printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_TELESPCI) return (0); -#ifdef CONFIG_PCI + if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) { if (pci_enable_device(dev_tel)) return(0); @@ -317,11 +318,6 @@ setup_telespci(struct IsdnCard *card) printk(KERN_WARNING "TelesPCI: No PCI card found\n"); return(0); } -#else - printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n"); - printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n"); - return (0); -#endif /* CONFIG_PCI */ /* Initialize Zoran PCI controller */ writel(0x00000000, cs->hw.teles0.membase + 0x28); diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c index 3aeceaf9769..39129b94f8b 100644 --- a/drivers/isdn/hisax/w6692.c +++ b/drivers/isdn/hisax/w6692.c @@ -1009,7 +1009,7 @@ setup_w6692(struct IsdnCard *card) printk(KERN_INFO "HiSax: W6692 driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_W6692) return (0); -#ifdef CONFIG_PCI + while (id_list[id_idx].vendor_id) { dev_w6692 = pci_find_device(id_list[id_idx].vendor_id, id_list[id_idx].device_id, @@ -1061,11 +1061,6 @@ setup_w6692(struct IsdnCard *card) cs->hw.w6692.iobase + 255); return (0); } -#else - printk(KERN_WARNING "HiSax: W6692 and NO_PCI_BIOS\n"); - printk(KERN_WARNING "HiSax: W6692 unable to config\n"); - return (0); -#endif /* CONFIG_PCI */ printk(KERN_INFO "HiSax: %s config irq:%d I/O:%x\n", diff --git a/drivers/isdn/hysdn/hysdn_init.c b/drivers/isdn/hysdn/hysdn_init.c index 9e01748a176..b7cc5c2f08c 100644 --- a/drivers/isdn/hysdn/hysdn_init.c +++ b/drivers/isdn/hysdn/hysdn_init.c @@ -20,10 +20,15 @@ #include "hysdn_defs.h" static struct pci_device_id hysdn_pci_tbl[] = { - {PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_METRO}, - {PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2}, - {PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_ERGO}, - {PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO}, + { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, + PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_METRO, 0, 0, BD_METRO }, + { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, + PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2, 0, 0, BD_CHAMP2 }, + { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, + PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_ERGO, 0, 0, BD_ERGO }, + { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, + PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO, 0, 0, BD_ERGO }, + { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(pci, hysdn_pci_tbl); @@ -34,128 +39,7 @@ MODULE_LICENSE("GPL"); static char *hysdn_init_revision = "$Revision: 1.6.6.6 $"; static int cardmax; /* number of found cards */ hysdn_card *card_root = NULL; /* pointer to first card */ - -/**********************************************/ -/* table assigning PCI-sub ids to board types */ -/* the last entry contains all 0 */ -/**********************************************/ -static struct { - unsigned short subid; /* PCI sub id */ - unsigned char cardtyp; /* card type assigned */ -} pci_subid_map[] = { - - { - PCI_SUBDEVICE_ID_HYPERCOPE_METRO, BD_METRO - }, - { - PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2, BD_CHAMP2 - }, - { - PCI_SUBDEVICE_ID_HYPERCOPE_ERGO, BD_ERGO - }, - { - PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO, BD_ERGO - }, - { - 0, 0 - } /* terminating entry */ -}; - - -/*********************************************************************/ -/* search_cards searches for available cards in the pci config data. */ -/* If a card is found, the card structure is allocated and the cards */ -/* ressources are reserved. cardmax is incremented. */ -/*********************************************************************/ -static void -search_cards(void) -{ - struct pci_dev *akt_pcidev = NULL; - hysdn_card *card, *card_last; - int i; - - card_root = NULL; - card_last = NULL; - while ((akt_pcidev = pci_find_device(PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, - akt_pcidev)) != NULL) { - if (pci_enable_device(akt_pcidev)) - continue; - - if (!(card = kzalloc(sizeof(hysdn_card), GFP_KERNEL))) { - printk(KERN_ERR "HYSDN: unable to alloc device mem \n"); - return; - } - card->myid = cardmax; /* set own id */ - card->bus = akt_pcidev->bus->number; - card->devfn = akt_pcidev->devfn; /* slot + function */ - card->subsysid = akt_pcidev->subsystem_device; - card->irq = akt_pcidev->irq; - card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE); - card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE); - card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE); - card->brdtype = BD_NONE; /* unknown */ - card->debug_flags = DEF_DEB_FLAGS; /* set default debug */ - card->faxchans = 0; /* default no fax channels */ - card->bchans = 2; /* and 2 b-channels */ - for (i = 0; pci_subid_map[i].subid; i++) - if (pci_subid_map[i].subid == card->subsysid) { - card->brdtype = pci_subid_map[i].cardtyp; - break; - } - if (card->brdtype != BD_NONE) { - if (ergo_inithardware(card)) { - printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase); - kfree(card); - continue; - } - } else { - printk(KERN_WARNING "HYSDN: unknown card id 0x%04x\n", card->subsysid); - kfree(card); /* release mem */ - continue; - } - cardmax++; - card->next = NULL; /*end of chain */ - if (card_last) - card_last->next = card; /* pointer to next card */ - else - card_root = card; - card_last = card; /* new chain end */ - } /* device found */ -} /* search_cards */ - -/************************************************************************************/ -/* free_resources frees the acquired PCI resources and returns the allocated memory */ -/************************************************************************************/ -static void -free_resources(void) -{ - hysdn_card *card; - - while (card_root) { - card = card_root; - if (card->releasehardware) - card->releasehardware(card); /* free all hardware resources */ - card_root = card_root->next; /* remove card from chain */ - kfree(card); /* return mem */ - - } /* while card_root */ -} /* free_resources */ - -/**************************************************************************/ -/* stop_cards disables (hardware resets) all cards and disables interrupt */ -/**************************************************************************/ -static void -stop_cards(void) -{ - hysdn_card *card; - - card = card_root; /* first in chain */ - while (card) { - if (card->stopcard) - card->stopcard(card); - card = card->next; /* remove card from chain */ - } /* while card */ -} /* stop_cards */ +static hysdn_card *card_last = NULL; /* pointer to first card */ /****************************************************************************/ @@ -191,31 +75,138 @@ hysdn_getrev(const char *revision) /* and the module is added to the list in /proc/modules, otherwise an error */ /* is assumed and the module will not be kept in memory. */ /****************************************************************************/ + +static int __devinit hysdn_pci_init_one(struct pci_dev *akt_pcidev, + const struct pci_device_id *ent) +{ + hysdn_card *card; + int rc; + + rc = pci_enable_device(akt_pcidev); + if (rc) + return rc; + + if (!(card = kzalloc(sizeof(hysdn_card), GFP_KERNEL))) { + printk(KERN_ERR "HYSDN: unable to alloc device mem \n"); + rc = -ENOMEM; + goto err_out; + } + card->myid = cardmax; /* set own id */ + card->bus = akt_pcidev->bus->number; + card->devfn = akt_pcidev->devfn; /* slot + function */ + card->subsysid = akt_pcidev->subsystem_device; + card->irq = akt_pcidev->irq; + card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE); + card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE); + card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE); + card->brdtype = BD_NONE; /* unknown */ + card->debug_flags = DEF_DEB_FLAGS; /* set default debug */ + card->faxchans = 0; /* default no fax channels */ + card->bchans = 2; /* and 2 b-channels */ + card->brdtype = ent->driver_data; + + if (ergo_inithardware(card)) { + printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase); + rc = -EBUSY; + goto err_out_card; + } + + cardmax++; + card->next = NULL; /*end of chain */ + if (card_last) + card_last->next = card; /* pointer to next card */ + else + card_root = card; + card_last = card; /* new chain end */ + + pci_set_drvdata(akt_pcidev, card); + return 0; + +err_out_card: + kfree(card); +err_out: + pci_disable_device(akt_pcidev); + return rc; +} + +static void __devexit hysdn_pci_remove_one(struct pci_dev *akt_pcidev) +{ + hysdn_card *card = pci_get_drvdata(akt_pcidev); + + pci_set_drvdata(akt_pcidev, NULL); + + if (card->stopcard) + card->stopcard(card); + +#ifdef CONFIG_HYSDN_CAPI + hycapi_capi_release(card); +#endif + + if (card->releasehardware) + card->releasehardware(card); /* free all hardware resources */ + + if (card == card_root) { + card_root = card_root->next; + if (!card_root) + card_last = NULL; + } else { + hysdn_card *tmp = card_root; + while (tmp) { + if (tmp->next == card) + tmp->next = card->next; + card_last = tmp; + tmp = tmp->next; + } + } + + kfree(card); + pci_disable_device(akt_pcidev); +} + +static struct pci_driver hysdn_pci_driver = { + .name = "hysdn", + .id_table = hysdn_pci_tbl, + .probe = hysdn_pci_init_one, + .remove = __devexit_p(hysdn_pci_remove_one), +}; + +static int hysdn_have_procfs; + static int __init hysdn_init(void) { char tmp[50]; + int rc; strcpy(tmp, hysdn_init_revision); printk(KERN_NOTICE "HYSDN: module Rev: %s loaded\n", hysdn_getrev(tmp)); strcpy(tmp, hysdn_net_revision); printk(KERN_NOTICE "HYSDN: network interface Rev: %s \n", hysdn_getrev(tmp)); - search_cards(); + + rc = pci_register_driver(&hysdn_pci_driver); + if (rc) + return rc; + printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax); - if (hysdn_procconf_init()) { - free_resources(); /* proc file_sys not created */ - return (-1); - } + if (!hysdn_procconf_init()) + hysdn_have_procfs = 1; + #ifdef CONFIG_HYSDN_CAPI if(cardmax > 0) { if(hycapi_init()) { printk(KERN_ERR "HYCAPI: init failed\n"); - return(-1); + + if (hysdn_have_procfs) + hysdn_procconf_release(); + + pci_unregister_driver(&hysdn_pci_driver); + return -ESPIPE; } } #endif /* CONFIG_HYSDN_CAPI */ - return (0); /* no error */ + + return 0; /* no error */ } /* init_module */ @@ -230,20 +221,15 @@ hysdn_init(void) static void __exit hysdn_exit(void) { + if (hysdn_have_procfs) + hysdn_procconf_release(); + + pci_unregister_driver(&hysdn_pci_driver); + #ifdef CONFIG_HYSDN_CAPI - hysdn_card *card; -#endif /* CONFIG_HYSDN_CAPI */ - stop_cards(); -#ifdef CONFIG_HYSDN_CAPI - card = card_root; /* first in chain */ - while (card) { - hycapi_capi_release(card); - card = card->next; /* remove card from chain */ - } /* while card */ hycapi_cleanup(); #endif /* CONFIG_HYSDN_CAPI */ - hysdn_procconf_release(); - free_resources(); + printk(KERN_NOTICE "HYSDN: module unloaded\n"); } /* cleanup_module */ diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 64fbc9759a3..c65d203a846 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -143,7 +143,7 @@ void mmc_remove_host(struct mmc_host *host) device_del(&host->class_dev); - led_trigger_unregister(host->led); + led_trigger_unregister_simple(host->led); spin_lock(&mmc_host_lock); idr_remove(&mmc_host_idr, host->index); diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index d68accea380..78ed633ceb8 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -2652,10 +2652,10 @@ static int bnx2_poll_work(struct bnx2 *bp, int work_done, int budget) REG_RD(bp, BNX2_HC_COMMAND); } - if (bp->status_blk->status_tx_quick_consumer_index0 != bp->hw_tx_cons) + if (sblk->status_tx_quick_consumer_index0 != bp->hw_tx_cons) bnx2_tx_int(bp); - if (bp->status_blk->status_rx_quick_consumer_index0 != bp->hw_rx_cons) + if (sblk->status_rx_quick_consumer_index0 != bp->hw_rx_cons) work_done += bnx2_rx_int(bp, budget - work_done); return work_done; @@ -2665,6 +2665,7 @@ static int bnx2_poll(struct napi_struct *napi, int budget) { struct bnx2 *bp = container_of(napi, struct bnx2, napi); int work_done = 0; + struct status_block *sblk = bp->status_blk; while (1) { work_done = bnx2_poll_work(bp, work_done, budget); @@ -2672,16 +2673,19 @@ static int bnx2_poll(struct napi_struct *napi, int budget) if (unlikely(work_done >= budget)) break; + /* bp->last_status_idx is used below to tell the hw how + * much work has been processed, so we must read it before + * checking for more work. + */ + bp->last_status_idx = sblk->status_idx; + rmb(); if (likely(!bnx2_has_work(bp))) { - bp->last_status_idx = bp->status_blk->status_idx; - rmb(); - netif_rx_complete(bp->dev, napi); if (likely(bp->flags & USING_MSI_FLAG)) { REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | bp->last_status_idx); - return 0; + break; } REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index e795c33b982..30b1cca8144 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -3576,7 +3576,7 @@ static int tg3_poll_work(struct tg3 *tp, int work_done, int budget) if (sblk->idx[0].tx_consumer != tp->tx_cons) { tg3_tx(tp); if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING)) - return 0; + return work_done; } /* run RX thread, within the bounds set by NAPI. @@ -3593,6 +3593,7 @@ static int tg3_poll(struct napi_struct *napi, int budget) { struct tg3 *tp = container_of(napi, struct tg3, napi); int work_done = 0; + struct tg3_hw_status *sblk = tp->hw_status; while (1) { work_done = tg3_poll_work(tp, work_done, budget); @@ -3603,15 +3604,17 @@ static int tg3_poll(struct napi_struct *napi, int budget) if (unlikely(work_done >= budget)) break; - if (likely(!tg3_has_work(tp))) { - struct tg3_hw_status *sblk = tp->hw_status; - - if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { - tp->last_tag = sblk->status_tag; - rmb(); - } else - sblk->status &= ~SD_STATUS_UPDATED; + if (tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) { + /* tp->last_tag is used in tg3_restart_ints() below + * to tell the hw how much work has been processed, + * so we must read it before checking for more work. + */ + tp->last_tag = sblk->status_tag; + rmb(); + } else + sblk->status &= ~SD_STATUS_UPDATED; + if (likely(!tg3_has_work(tp))) { netif_rx_complete(tp->dev, napi); tg3_restart_ints(tp); break; @@ -3621,9 +3624,10 @@ static int tg3_poll(struct napi_struct *napi, int budget) return work_done; tx_recovery: + /* work_done is guaranteed to be less than budget. */ netif_rx_complete(tp->dev, napi); schedule_work(&tp->reset_task); - return 0; + return work_done; } static void tg3_irq_quiesce(struct tg3 *tp) diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index aeda5268244..d427daeef51 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -53,6 +53,7 @@ #include <linux/genhd.h> #include <linux/hdreg.h> #include <linux/interrupt.h> +#include <linux/log2.h> #include <asm/ccwdev.h> #include <linux/workqueue.h> #include <asm/debug.h> @@ -456,7 +457,7 @@ dasd_free_chunk(struct list_head *chunk_list, void *mem) static inline int dasd_check_blocksize(int bsize) { - if (bsize < 512 || bsize > 4096 || (bsize & (bsize - 1)) != 0) + if (bsize < 512 || bsize > 4096 || !is_power_of_2(bsize)) return -EMEDIUMTYPE; return 0; } diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index 0fbacc8b106..f231bc21b1c 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -230,7 +230,7 @@ static int xpram_make_request(struct request_queue *q, struct bio *bio) } } set_bit(BIO_UPTODATE, &bio->bi_flags); - bio_end_io(bio, 0); + bio_endio(bio, 0); return 0; fail: bio_io_error(bio); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 6000bdee408..0e1f35c9ed9 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -667,6 +667,9 @@ raw3215_probe (struct ccw_device *cdev) struct raw3215_info *raw; int line; + /* Console is special. */ + if (raw3215[0] && (cdev->dev.driver_data == raw3215[0])) + return 0; raw = kmalloc(sizeof(struct raw3215_info) + RAW3215_INBUF_SIZE, GFP_KERNEL|GFP_DMA); if (raw == NULL) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index fd3479119eb..0b040557db0 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -22,6 +22,7 @@ #include <asm/ebcdic.h> #include "raw3270.h" +#include "tty3270.h" #include "ctrlchar.h" #define CON3270_OUTPUT_BUFFER_SIZE 1024 @@ -507,8 +508,6 @@ con3270_write(struct console *co, const char *str, unsigned int count) spin_unlock_irqrestore(&cp->view.lock,flags); } -extern struct tty_driver *tty3270_driver; - static struct tty_driver * con3270_device(struct console *c, int *index) { diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index fa62e694405..25629b92dec 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -93,6 +93,7 @@ static volatile enum sclp_mask_state_t { #define SCLP_RETRY_INTERVAL 30 static void sclp_process_queue(void); +static void __sclp_make_read_req(void); static int sclp_init_mask(int calculate); static int sclp_init(void); @@ -115,7 +116,6 @@ sclp_service_call(sclp_cmdw_t command, void *sccb) return 0; } -static inline void __sclp_make_read_req(void); static void __sclp_queue_read_req(void) @@ -318,8 +318,7 @@ sclp_read_cb(struct sclp_req *req, void *data) } /* Prepare read event data request. Called while sclp_lock is locked. */ -static inline void -__sclp_make_read_req(void) +static void __sclp_make_read_req(void) { struct sccb_header *sccb; diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index 9f244c591ee..da25f8e2415 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -708,16 +708,22 @@ static void tape_3590_med_state_set(struct tape_device *device, c_info = &TAPE_3590_CRYPT_INFO(device); - if (sense->masst == MSENSE_UNASSOCIATED) { + DBF_EVENT(6, "medium state: %x:%x\n", sense->macst, sense->masst); + switch (sense->macst) { + case 0x04: + case 0x05: + case 0x06: tape_med_state_set(device, MS_UNLOADED); TAPE_3590_CRYPT_INFO(device).medium_status = 0; return; - } - if (sense->masst != MSENSE_ASSOCIATED_MOUNT) { - PRINT_ERR("Unknown medium state: %x\n", sense->masst); + case 0x08: + case 0x09: + tape_med_state_set(device, MS_LOADED); + break; + default: + tape_med_state_set(device, MS_UNKNOWN); return; } - tape_med_state_set(device, MS_LOADED); c_info->medium_status |= TAPE390_MEDIUM_LOADED_MASK; if (sense->flags & MSENSE_CRYPT_MASK) { PRINT_INFO("Medium is encrypted (%04x)\n", sense->flags); @@ -835,15 +841,17 @@ tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb) /* Probably result of halt ssch */ return TAPE_IO_PENDING; else if (irb->scsw.dstat == 0x85) - /* Device Ready -> check medium state */ - tape_3590_schedule_work(device, TO_MSEN); - else if (irb->scsw.dstat & DEV_STAT_ATTENTION) + /* Device Ready */ + DBF_EVENT(3, "unsol.irq! tape ready: %08x\n", device->cdev_id); + else if (irb->scsw.dstat & DEV_STAT_ATTENTION) { tape_3590_schedule_work(device, TO_READ_ATTMSG); - else { + } else { DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); PRINT_WARN("Unsolicited IRQ (Device End) caught.\n"); tape_dump_sense(device, NULL, irb); } + /* check medium state */ + tape_3590_schedule_work(device, TO_MSEN); return TAPE_IO_SUCCESS; } diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index bc33068b9ce..70b1980a08b 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -25,8 +25,8 @@ #include <asm/ebcdic.h> #include <asm/uaccess.h> - #include "raw3270.h" +#include "tty3270.h" #include "keyboard.h" #define TTY3270_CHAR_BUF_SIZE 256 @@ -1338,8 +1338,11 @@ tty3270_getpar(struct tty3270 *tp, int ix) static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) { - tp->cx = min_t(int, tp->view.cols - 1, max_t(int, 0, cx)); - cy = min_t(int, tp->view.rows - 3, max_t(int, 0, cy)); + int max_cx = max(0, cx); + int max_cy = max(0, cy); + + tp->cx = min_t(int, tp->view.cols - 1, max_cx); + cy = min_t(int, tp->view.rows - 3, max_cy); if (cy != tp->cy) { tty3270_convert_line(tp, tp->cy); tp->cy = cy; diff --git a/drivers/s390/char/tty3270.h b/drivers/s390/char/tty3270.h new file mode 100644 index 00000000000..799da57f039 --- /dev/null +++ b/drivers/s390/char/tty3270.h @@ -0,0 +1,16 @@ +/* + * drivers/s390/char/tty3270.h + * + * Copyright IBM Corp. 2007 + * + */ + +#ifndef __DRIVERS_S390_CHAR_TTY3270_H +#define __DRIVERS_S390_CHAR_TTY3270_H + +#include <linux/tty.h> +#include <linux/tty_driver.h> + +extern struct tty_driver *tty3270_driver; + +#endif /* __DRIVERS_S390_CHAR_TTY3270_H */ diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 680b9b58b80..6f40facb1c4 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -66,8 +66,8 @@ static int __diag288(enum vmwdt_func func, unsigned int timeout, "0: la %0,0\n" "1:\n" EX_TABLE(0b,1b) - : "=d" (err) : "d"(__func), "d"(__timeout), - "d"(__cmdp), "d"(__cmdl), "0" (-EINVAL) : "1", "cc"); + : "+d" (err) : "d"(__func), "d"(__timeout), + "d"(__cmdp), "d"(__cmdl) : "1", "cc"); return err; } diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 3712ede1672..7073daf7798 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -141,15 +141,16 @@ static int memcpy_real(void *dest, unsigned long src, size_t count) if (count == 0) return 0; - flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */ + flags = __raw_local_irq_stnsm(0xf8UL); /* switch to real mode */ asm volatile ( "0: mvcle %1,%2,0x0\n" "1: jo 0b\n" " lhi %0,0x0\n" "2:\n" EX_TABLE(1b,2b) - : "+d" (rc) - : "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2) + : "+d" (rc), "+d" (_dest), "+d" (_src), "+d" (_len1), + "+d" (_len2), "=m" (*((long*)dest)) + : "m" (*((long*)src)) : "cc", "memory"); __raw_local_irq_ssm(flags); diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index b0a18f5176a..9c3b9ea1e66 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -152,16 +152,24 @@ __ccwgroup_create_symlinks(struct ccwgroup_device *gdev) return 0; } -/* - * try to add a new ccwgroup device for one driver - * argc and argv[] are a list of bus_id's of devices - * belonging to the driver. +/** + * ccwgroup_create() - create and register a ccw group device + * @root: parent device for the new device + * @creator_id: identifier of creating driver + * @cdrv: ccw driver of slave devices + * @argc: number of slave devices + * @argv: bus ids of slave devices + * + * Create and register a new ccw group device as a child of @root. Slave + * devices are obtained from the list of bus ids given in @argv[] and must all + * belong to @cdrv. + * Returns: + * %0 on success and an error code on failure. + * Context: + * non-atomic */ -int -ccwgroup_create(struct device *root, - unsigned int creator_id, - struct ccw_driver *cdrv, - int argc, char *argv[]) +int ccwgroup_create(struct device *root, unsigned int creator_id, + struct ccw_driver *cdrv, int argc, char *argv[]) { struct ccwgroup_device *gdev; int i; @@ -390,8 +398,13 @@ static struct bus_type ccwgroup_bus_type = { .remove = ccwgroup_remove, }; -int -ccwgroup_driver_register (struct ccwgroup_driver *cdriver) +/** + * ccwgroup_driver_register() - register a ccw group driver + * @cdriver: driver to be registered + * + * This function is mainly a wrapper around driver_register(). + */ +int ccwgroup_driver_register(struct ccwgroup_driver *cdriver) { /* register our new driver with the core */ cdriver->driver.bus = &ccwgroup_bus_type; @@ -406,8 +419,13 @@ __ccwgroup_match_all(struct device *dev, void *data) return 1; } -void -ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver) +/** + * ccwgroup_driver_unregister() - deregister a ccw group driver + * @cdriver: driver to be deregistered + * + * This function is mainly a wrapper around driver_unregister(). + */ +void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) { struct device *dev; @@ -427,8 +445,16 @@ ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver) driver_unregister(&cdriver->driver); } -int -ccwgroup_probe_ccwdev(struct ccw_device *cdev) +/** + * ccwgroup_probe_ccwdev() - probe function for slave devices + * @cdev: ccw device to be probed + * + * This is a dummy probe function for ccw devices that are slave devices in + * a ccw group device. + * Returns: + * always %0 + */ +int ccwgroup_probe_ccwdev(struct ccw_device *cdev) { return 0; } @@ -452,8 +478,15 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) return NULL; } -void -ccwgroup_remove_ccwdev(struct ccw_device *cdev) +/** + * ccwgroup_remove_ccwdev() - remove function for slave devices + * @cdev: ccw device to be removed + * + * This is a remove function for ccw devices that are slave devices in a ccw + * group device. It sets the ccw device offline and also deregisters the + * embedding ccw group device. + */ +void ccwgroup_remove_ccwdev(struct ccw_device *cdev) { struct ccwgroup_device *gdev; diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 920dd71e643..42c1f4659ad 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -14,7 +14,7 @@ #include <linux/jiffies.h> #include <linux/wait.h> #include <linux/mutex.h> -#include <asm/errno.h> +#include <linux/errno.h> #include <asm/chpid.h> #include <asm/sclp.h> @@ -55,7 +55,7 @@ static wait_queue_head_t cfg_wait_queue; /* Return channel_path struct for given chpid. */ static inline struct channel_path *chpid_to_chp(struct chp_id chpid) { - return css[chpid.cssid]->chps[chpid.id]; + return channel_subsystems[chpid.cssid]->chps[chpid.id]; } /* Set vary state for given chpid. */ @@ -86,7 +86,7 @@ u8 chp_get_sch_opm(struct subchannel *sch) opm = 0; chp_id_init(&chpid); - for (i=0; i < 8; i++) { + for (i = 0; i < 8; i++) { opm <<= 1; chpid.id = sch->schib.pmcw.chpid[i]; if (chp_get_status(chpid) != 0) @@ -118,7 +118,7 @@ static int s390_vary_chpid(struct chp_id chpid, int on) sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid, chpid.id); - CIO_TRACE_EVENT( 2, dbf_text); + CIO_TRACE_EVENT(2, dbf_text); status = chp_get_status(chpid); if (!on && !status) { @@ -140,9 +140,11 @@ static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off, size_t count) { struct channel_path *chp; + struct device *device; unsigned int size; - chp = to_channelpath(container_of(kobj, struct device, kobj)); + device = container_of(kobj, struct device, kobj); + chp = to_channelpath(device); if (!chp->cmg_chars) return 0; @@ -193,9 +195,11 @@ static ssize_t chp_measurement_read(struct kobject *kobj, { struct channel_path *chp; struct channel_subsystem *css; + struct device *device; unsigned int size; - chp = to_channelpath(container_of(kobj, struct device, kobj)); + device = container_of(kobj, struct device, kobj); + chp = to_channelpath(device); css = to_css(chp->dev.parent); size = sizeof(struct cmg_entry); @@ -353,7 +357,7 @@ static ssize_t chp_shared_show(struct device *dev, static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); -static struct attribute * chp_attrs[] = { +static struct attribute *chp_attrs[] = { &dev_attr_status.attr, &dev_attr_configure.attr, &dev_attr_type.attr, @@ -395,7 +399,7 @@ int chp_new(struct chp_id chpid) /* fill in status, etc. */ chp->chpid = chpid; chp->state = 1; - chp->dev.parent = &css[chpid.cssid]->device; + chp->dev.parent = &channel_subsystems[chpid.cssid]->device; chp->dev.release = chp_release; snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid, chpid.id); @@ -430,18 +434,18 @@ int chp_new(struct chp_id chpid) device_unregister(&chp->dev); goto out_free; } - mutex_lock(&css[chpid.cssid]->mutex); - if (css[chpid.cssid]->cm_enabled) { + mutex_lock(&channel_subsystems[chpid.cssid]->mutex); + if (channel_subsystems[chpid.cssid]->cm_enabled) { ret = chp_add_cmg_attr(chp); if (ret) { sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); device_unregister(&chp->dev); - mutex_unlock(&css[chpid.cssid]->mutex); + mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); goto out_free; } } - css[chpid.cssid]->chps[chpid.id] = chp; - mutex_unlock(&css[chpid.cssid]->mutex); + channel_subsystems[chpid.cssid]->chps[chpid.id] = chp; + mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); return ret; out_free: kfree(chp); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index f2708d65be5..46905345159 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -619,6 +619,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) sch->schib.pmcw.mp = 1; /* multipath mode */ + /* clean up possible residual cmf stuff */ + sch->schib.pmcw.mme = 0; + sch->schib.pmcw.mbfc = 0; + sch->schib.pmcw.mbi = 0; + sch->schib.mba = 0; return 0; out: if (!cio_is_console(schid)) diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 34a796913b0..b960f66843e 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -45,7 +45,8 @@ #include "ioasm.h" #include "chsc.h" -/* parameter to enable cmf during boot, possible uses are: +/* + * 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 @@ -73,18 +74,20 @@ enum cmb_index { * enum cmb_format - types of supported measurement block formats * * @CMF_BASIC: traditional channel measurement blocks supported - * by all machines that we run on + * 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 - **/ + * machine + * @CMF_AUTODETECT: default: use extended format when running on a machine + * supporting extended format, 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) @@ -105,20 +108,21 @@ module_param(format, bool, 0444); * either with the help of a special pool or with kmalloc * @free: free memory allocated with @alloc * @set: enable or disable measurement + * @read: read a measurement entry at an index * @readall: read a measurement block in a common format * @reset: clear the data in the associated measurement block and * reset its time stamp * @align: align an allocated block so that the hardware can use it */ 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*); - void * (*align) (void *); - + 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 *); + void *(*align) (void *); +/* private: */ struct attribute_group *attr_group; }; static struct cmb_operations *cmbops; @@ -130,9 +134,11 @@ struct cmb_data { unsigned long long last_update; /* when last_block was updated */ }; -/* our user interface is designed in terms of nanoseconds, +/* + * Our user interface is designed in terms of nanoseconds, * while the hardware measures total times in its own - * unit.*/ + * unit. + */ static inline u64 time_to_nsec(u32 value) { return ((u64)value) * 128000ull; @@ -159,12 +165,13 @@ static inline u64 time_to_avg_nsec(u32 value, u32 count) return ret; } -/* activate or deactivate the channel monitor. When area is NULL, +/* + * 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) + * to be enabled. + */ +static inline void cmf_activate(void *area, unsigned int onoff) { register void * __gpr2 asm("2"); register long __gpr1 asm("1"); @@ -175,8 +182,8 @@ cmf_activate(void *area, unsigned int onoff) asm("schm" : : "d" (__gpr2), "d" (__gpr1) ); } -static int -set_schib(struct ccw_device *cdev, u32 mme, int mbfc, unsigned long address) +static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc, + unsigned long address) { int ret; int retry; @@ -466,6 +473,7 @@ static void cmf_generic_reset(struct ccw_device *cdev) * * @mem: pointer to CMBs (only in basic measurement mode) * @list: contains a linked list of all subchannels + * @num_channels: number of channels to be measured * @lock: protect concurrent access to @mem and @list */ struct cmb_area { @@ -481,28 +489,36 @@ static struct cmb_area cmb_area = { .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 + * maximum is 4096. */ module_param_named(maxchannels, cmb_area.num_channels, uint, 0444); /** * struct cmb - basic channel measurement block + * @ssch_rsch_count: number of ssch and rsch + * @sample_count: number of samples + * @device_connect_time: time of device connect + * @function_pending_time: time of function pending + * @device_disconnect_time: time of device disconnect + * @control_unit_queuing_time: time of control unit queuing + * @device_active_only_time: time of device active only + * @reserved: unused in basic measurement mode + * + * The measurement block as used by the hardware. The fields are described + * further in z/Architecture Principles of Operation, chapter 17. * - * 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. + * The cmb area made up from these blocks must be a contiguous array and may + * not be reallocated or freed. * Only one cmb area can be present in the system. */ struct cmb { @@ -516,8 +532,9 @@ struct cmb { u32 reserved[2]; }; -/* insert a single device into the cmb_area list - * called with cmb_area.lock held from alloc_cmb +/* + * Insert a single device into the cmb_area list. + * Called with cmb_area.lock held from alloc_cmb. */ static int alloc_cmb_single(struct ccw_device *cdev, struct cmb_data *cmb_data) @@ -532,9 +549,11 @@ static int alloc_cmb_single(struct ccw_device *cdev, goto out; } - /* find first unused cmb in cmb_area.mem. - * this is a little tricky: cmb_area.list - * remains sorted by ->cmb->hw_data pointers */ + /* + * Find first unused cmb in cmb_area.mem. + * This is a little tricky: cmb_area.list + * remains sorted by ->cmb->hw_data pointers. + */ cmb = cmb_area.mem; list_for_each_entry(node, &cmb_area.list, cmb_list) { struct cmb_data *data; @@ -558,8 +577,7 @@ out: return ret; } -static int -alloc_cmb (struct ccw_device *cdev) +static int alloc_cmb(struct ccw_device *cdev) { int ret; struct cmb *mem; @@ -670,7 +688,7 @@ static int set_cmb(struct ccw_device *cdev, u32 mme) return set_schib_wait(cdev, mme, 0, offset); } -static u64 read_cmb (struct ccw_device *cdev, int index) +static u64 read_cmb(struct ccw_device *cdev, int index) { struct cmb *cmb; u32 val; @@ -720,7 +738,7 @@ out: return ret; } -static int readall_cmb (struct ccw_device *cdev, struct cmbdata *data) +static int readall_cmb(struct ccw_device *cdev, struct cmbdata *data) { struct cmb *cmb; struct cmb_data *cmb_data; @@ -793,14 +811,25 @@ static struct cmb_operations cmbops_basic = { .align = align_cmb, .attr_group = &cmf_attr_group, }; - + /* ******** extended cmb handling ********/ /** * struct cmbe - extended channel measurement block + * @ssch_rsch_count: number of ssch and rsch + * @sample_count: number of samples + * @device_connect_time: time of device connect + * @function_pending_time: time of function pending + * @device_disconnect_time: time of device disconnect + * @control_unit_queuing_time: time of control unit queuing + * @device_active_only_time: time of device active only + * @device_busy_time: time of device busy + * @initial_command_response_time: initial command response time + * @reserved: unused * - * cmb as used by the hardware, may be in any 64 bit physical location, - * the fields are described in z/Architecture Principles of Operation, + * The measurement block as used by the hardware. May be in any 64 bit physical + * location. + * The fields are described further in z/Architecture Principles of Operation, * third edition, chapter 17. */ struct cmbe { @@ -816,10 +845,12 @@ struct cmbe { u32 reserved[7]; }; -/* kmalloc only guarantees 8 byte alignment, but we need cmbe +/* + * kmalloc only guarantees 8 byte alignment, but we need cmbe * pointers to be naturally aligned. Make sure to allocate - * enough space for two cmbes */ -static inline struct cmbe* cmbe_align(struct cmbe *c) + * enough space for two cmbes. + */ +static inline struct cmbe *cmbe_align(struct cmbe *c) { unsigned long addr; addr = ((unsigned long)c + sizeof (struct cmbe) - sizeof(long)) & @@ -827,7 +858,7 @@ static inline struct cmbe* cmbe_align(struct cmbe *c) return (struct cmbe*)addr; } -static int alloc_cmbe (struct ccw_device *cdev) +static int alloc_cmbe(struct ccw_device *cdev) { struct cmbe *cmbe; struct cmb_data *cmb_data; @@ -873,7 +904,7 @@ out_free: return ret; } -static void free_cmbe (struct ccw_device *cdev) +static void free_cmbe(struct ccw_device *cdev) { struct cmb_data *cmb_data; @@ -912,7 +943,7 @@ static int set_cmbe(struct ccw_device *cdev, u32 mme) } -static u64 read_cmbe (struct ccw_device *cdev, int index) +static u64 read_cmbe(struct ccw_device *cdev, int index) { struct cmbe *cmb; struct cmb_data *cmb_data; @@ -970,7 +1001,7 @@ out: return ret; } -static int readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) +static int readall_cmbe(struct ccw_device *cdev, struct cmbdata *data) { struct cmbe *cmb; struct cmb_data *cmb_data; @@ -1047,17 +1078,16 @@ static struct cmb_operations cmbops_extended = { .align = align_cmbe, .attr_group = &cmf_attr_group_ext, }; - -static ssize_t -cmb_show_attr(struct device *dev, char *buf, enum cmb_index idx) +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, struct device_attribute *attr, char *buf) +static ssize_t cmb_show_avg_sample_interval(struct device *dev, + struct device_attribute *attr, + char *buf) { struct ccw_device *cdev; long interval; @@ -1079,8 +1109,9 @@ cmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%ld\n", interval); } -static ssize_t -cmb_show_avg_utilization(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t cmb_show_avg_utilization(struct device *dev, + struct device_attribute *attr, + char *buf) { struct cmbdata data; u64 utilization; @@ -1112,14 +1143,16 @@ cmb_show_avg_utilization(struct device *dev, struct device_attribute *attr, char } #define cmf_attr(name) \ -static ssize_t show_ ## name (struct device * dev, struct device_attribute *attr, char * buf) \ -{ return cmb_show_attr((dev), buf, cmb_ ## name); } \ -static DEVICE_ATTR(name, 0444, show_ ## name, NULL); +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, 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, struct device_attribute *attr, char * buf) \ -{ return cmb_show_attr((dev), buf, cmb_ ## name); } \ -static DEVICE_ATTR(avg_ ## name, 0444, show_avg_ ## name, NULL); +static ssize_t show_avg_##name(struct device *dev, \ + struct device_attribute *attr, 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); @@ -1131,7 +1164,8 @@ 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_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[] = { @@ -1172,12 +1206,16 @@ static struct attribute_group cmf_attr_group_ext = { .attrs = cmf_attributes_ext, }; -static ssize_t cmb_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t cmb_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) { return sprintf(buf, "%d\n", to_ccwdev(dev)->private->cmb ? 1 : 0); } -static ssize_t cmb_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t c) +static ssize_t cmb_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t c) { struct ccw_device *cdev; int ret; @@ -1202,9 +1240,16 @@ static ssize_t cmb_enable_store(struct device *dev, struct device_attribute *att 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) +/** + * 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 + */ +int enable_cmf(struct ccw_device *cdev) { int ret; @@ -1225,8 +1270,16 @@ enable_cmf(struct ccw_device *cdev) return ret; } -int -disable_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 + */ +int disable_cmf(struct ccw_device *cdev) { int ret; @@ -1238,14 +1291,32 @@ disable_cmf(struct ccw_device *cdev) return ret; } -u64 -cmf_read(struct ccw_device *cdev, int index) +/** + * cmf_read() - read one value from the current channel measurement block + * @cdev: the channel to be read + * @index: the index of the value to be read + * + * Returns the value read or %0 if the value cannot be read. + * + * Context: + * any + */ +u64 cmf_read(struct ccw_device *cdev, int index) { return cmbops->read(cdev, index); } -int -cmf_readall(struct ccw_device *cdev, struct cmbdata *data) +/** + * cmf_readall() - read the current channel measurement block + * @cdev: the channel to be read + * @data: a pointer to a data block that will be filled + * + * Returns %0 on success, a negative error value otherwise. + * + * Context: + * any + */ +int cmf_readall(struct ccw_device *cdev, struct cmbdata *data) { return cmbops->readall(cdev, data); } @@ -1257,15 +1328,16 @@ int cmf_reenable(struct ccw_device *cdev) return cmbops->set(cdev, 2); } -static int __init -init_cmf(void) +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 the user did not give a parameter, see if we are running on a + * machine supporting extended measurement blocks, otherwise fall back + * to basic mode. + */ if (format == CMF_AUTODETECT) { if (!css_characteristics_avail || !css_general_characteristics.ext_mb) { @@ -1284,7 +1356,7 @@ init_cmf(void) cmbops = &cmbops_basic; break; case CMF_EXTENDED: - format_string = "extended"; + format_string = "extended"; cmbops = &cmbops_extended; break; default: diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 5635e656c1a..5d83dd47146 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/list.h> +#include <linux/reboot.h> #include "css.h" #include "cio.h" @@ -27,7 +28,7 @@ int css_init_done = 0; static int need_reprobe = 0; static int max_ssid = 0; -struct channel_subsystem *css[__MAX_CSSID + 1]; +struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1]; int css_characteristics_avail = 0; @@ -177,7 +178,7 @@ static int css_register_subchannel(struct subchannel *sch) int ret; /* Initialize the subchannel structure */ - sch->dev.parent = &css[0]->device; + sch->dev.parent = &channel_subsystems[0]->device; sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; sch->dev.groups = subch_attr_groups; @@ -606,30 +607,55 @@ static int __init setup_css(int nr) { u32 tod_high; int ret; + struct channel_subsystem *css; - memset(css[nr], 0, sizeof(struct channel_subsystem)); - css[nr]->pseudo_subchannel = - kzalloc(sizeof(*css[nr]->pseudo_subchannel), GFP_KERNEL); - if (!css[nr]->pseudo_subchannel) + css = channel_subsystems[nr]; + memset(css, 0, sizeof(struct channel_subsystem)); + css->pseudo_subchannel = + kzalloc(sizeof(*css->pseudo_subchannel), GFP_KERNEL); + if (!css->pseudo_subchannel) return -ENOMEM; - css[nr]->pseudo_subchannel->dev.parent = &css[nr]->device; - css[nr]->pseudo_subchannel->dev.release = css_subchannel_release; - sprintf(css[nr]->pseudo_subchannel->dev.bus_id, "defunct"); - ret = cio_create_sch_lock(css[nr]->pseudo_subchannel); + css->pseudo_subchannel->dev.parent = &css->device; + css->pseudo_subchannel->dev.release = css_subchannel_release; + sprintf(css->pseudo_subchannel->dev.bus_id, "defunct"); + ret = cio_create_sch_lock(css->pseudo_subchannel); if (ret) { - kfree(css[nr]->pseudo_subchannel); + kfree(css->pseudo_subchannel); return ret; } - mutex_init(&css[nr]->mutex); - css[nr]->valid = 1; - css[nr]->cssid = nr; - sprintf(css[nr]->device.bus_id, "css%x", nr); - css[nr]->device.release = channel_subsystem_release; + mutex_init(&css->mutex); + css->valid = 1; + css->cssid = nr; + sprintf(css->device.bus_id, "css%x", nr); + css->device.release = channel_subsystem_release; tod_high = (u32) (get_clock() >> 32); - css_generate_pgid(css[nr], tod_high); + css_generate_pgid(css, tod_high); return 0; } +static int css_reboot_event(struct notifier_block *this, + unsigned long event, + void *ptr) +{ + int ret, i; + + ret = NOTIFY_DONE; + for (i = 0; i <= __MAX_CSSID; i++) { + struct channel_subsystem *css; + + css = channel_subsystems[i]; + if (css->cm_enabled) + if (chsc_secm(css, 0)) + ret = NOTIFY_BAD; + } + + return ret; +} + +static struct notifier_block css_reboot_notifier = { + .notifier_call = css_reboot_event, +}; + /* * Now that the driver core is running, we can setup our channel subsystem. * The struct subchannel's are created during probing (except for the @@ -670,51 +696,63 @@ init_channel_subsystem (void) } /* Setup css structure. */ for (i = 0; i <= __MAX_CSSID; i++) { - css[i] = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL); - if (!css[i]) { + struct channel_subsystem *css; + + css = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL); + if (!css) { ret = -ENOMEM; goto out_unregister; } + channel_subsystems[i] = css; ret = setup_css(i); if (ret) goto out_free; - ret = device_register(&css[i]->device); + ret = device_register(&css->device); if (ret) goto out_free_all; if (css_characteristics_avail && css_chsc_characteristics.secm) { - ret = device_create_file(&css[i]->device, + ret = device_create_file(&css->device, &dev_attr_cm_enable); if (ret) goto out_device; } - ret = device_register(&css[i]->pseudo_subchannel->dev); + ret = device_register(&css->pseudo_subchannel->dev); if (ret) goto out_file; } + ret = register_reboot_notifier(&css_reboot_notifier); + if (ret) + goto out_pseudo; css_init_done = 1; ctl_set_bit(6, 28); for_each_subchannel(__init_channel_subsystem, NULL); return 0; +out_pseudo: + device_unregister(&channel_subsystems[i]->pseudo_subchannel->dev); out_file: - device_remove_file(&css[i]->device, &dev_attr_cm_enable); + device_remove_file(&channel_subsystems[i]->device, + &dev_attr_cm_enable); out_device: - device_unregister(&css[i]->device); + device_unregister(&channel_subsystems[i]->device); out_free_all: - kfree(css[i]->pseudo_subchannel->lock); - kfree(css[i]->pseudo_subchannel); + kfree(channel_subsystems[i]->pseudo_subchannel->lock); + kfree(channel_subsystems[i]->pseudo_subchannel); out_free: - kfree(css[i]); + kfree(channel_subsystems[i]); out_unregister: while (i > 0) { + struct channel_subsystem *css; + i--; - device_unregister(&css[i]->pseudo_subchannel->dev); + css = channel_subsystems[i]; + device_unregister(&css->pseudo_subchannel->dev); if (css_characteristics_avail && css_chsc_characteristics.secm) - device_remove_file(&css[i]->device, + device_remove_file(&css->device, &dev_attr_cm_enable); - device_unregister(&css[i]->device); + device_unregister(&css->device); } out_bus: bus_unregister(&css_bus_type); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 5d65e83ca66..81215ef3243 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -167,7 +167,7 @@ struct channel_subsystem { #define to_css(dev) container_of(dev, struct channel_subsystem, device) extern struct bus_type css_bus_type; -extern struct channel_subsystem *css[]; +extern struct channel_subsystem *channel_subsystems[]; /* Some helper functions for disconnected state. */ int device_is_disconnected(struct subchannel *); @@ -191,6 +191,5 @@ int sch_is_pseudo_sch(struct subchannel *); extern struct workqueue_struct *slow_path_wq; -int subchannel_add_files (struct device *); extern struct attribute_group *subch_attr_groups[]; #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e44d92eac8e..39f02b48e4c 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -21,6 +21,7 @@ #include <asm/ccwdev.h> #include <asm/cio.h> #include <asm/param.h> /* HZ */ +#include <asm/cmb.h> #include "cio.h" #include "cio_debug.h" @@ -357,8 +358,18 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) cdev->private->dev_id.devno); } -int -ccw_device_set_offline(struct ccw_device *cdev) +/** + * ccw_device_set_offline() - disable a ccw device for I/O + * @cdev: target ccw device + * + * This function calls the driver's set_offline() function for @cdev, if + * given, and then disables @cdev. + * Returns: + * %0 on success and a negative error value on failure. + * Context: + * enabled, ccw device lock not held + */ +int ccw_device_set_offline(struct ccw_device *cdev) { int ret; @@ -396,8 +407,19 @@ ccw_device_set_offline(struct ccw_device *cdev) return ret; } -int -ccw_device_set_online(struct ccw_device *cdev) +/** + * ccw_device_set_online() - enable a ccw device for I/O + * @cdev: target ccw device + * + * This function first enables @cdev and then calls the driver's set_online() + * function for @cdev, if given. If set_online() returns an error, @cdev is + * disabled again. + * Returns: + * %0 on success and a negative error value on failure. + * Context: + * enabled, ccw device lock not held + */ +int ccw_device_set_online(struct ccw_device *cdev) { int ret; @@ -947,8 +969,7 @@ out: wake_up(&ccw_device_init_wq); } -void -ccw_device_call_sch_unregister(struct work_struct *work) +static void ccw_device_call_sch_unregister(struct work_struct *work) { struct ccw_device_private *priv; struct ccw_device *cdev; @@ -1101,6 +1122,7 @@ io_subchannel_probe (struct subchannel *sch) * device, e.g. the console. */ cdev = sch->dev.driver_data; + cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); ccw_device_register(cdev); /* @@ -1326,8 +1348,19 @@ __ccwdev_check_busid(struct device *dev, void *id) } -struct ccw_device * -get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id) +/** + * get_ccwdev_by_busid() - obtain device from a bus id + * @cdrv: driver the device is owned by + * @bus_id: bus id of the device to be searched + * + * This function searches all devices owned by @cdrv for a device with a bus + * id matching @bus_id. + * Returns: + * If a match is found, its reference count of the found device is increased + * and it is returned; else %NULL is returned. + */ +struct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv, + const char *bus_id) { struct device *dev; struct device_driver *drv; @@ -1401,16 +1434,34 @@ ccw_device_remove (struct device *dev) return 0; } +static void ccw_device_shutdown(struct device *dev) +{ + struct ccw_device *cdev; + + cdev = to_ccwdev(dev); + if (cdev->drv && cdev->drv->shutdown) + cdev->drv->shutdown(cdev); + disable_cmf(cdev); +} + struct bus_type ccw_bus_type = { .name = "ccw", .match = ccw_bus_match, .uevent = ccw_uevent, .probe = ccw_device_probe, .remove = ccw_device_remove, + .shutdown = ccw_device_shutdown, }; -int -ccw_driver_register (struct ccw_driver *cdriver) +/** + * ccw_driver_register() - register a ccw driver + * @cdriver: driver to be registered + * + * This function is mainly a wrapper around driver_register(). + * Returns: + * %0 on success and a negative error value on failure. + */ +int ccw_driver_register(struct ccw_driver *cdriver) { struct device_driver *drv = &cdriver->driver; @@ -1420,8 +1471,13 @@ ccw_driver_register (struct ccw_driver *cdriver) return driver_register(drv); } -void -ccw_driver_unregister (struct ccw_driver *cdriver) +/** + * ccw_driver_unregister() - deregister a ccw driver + * @cdriver: driver to be deregistered + * + * This function is mainly a wrapper around driver_unregister(). + */ +void ccw_driver_unregister(struct ccw_driver *cdriver) { driver_unregister(&cdriver->driver); } diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index b66338b7657..0d408960043 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -80,7 +80,6 @@ void io_subchannel_recog_done(struct ccw_device *cdev); int ccw_device_cancel_halt_clear(struct ccw_device *); void ccw_device_do_unreg_rereg(struct work_struct *); -void ccw_device_call_sch_unregister(struct work_struct *); void ccw_device_move_to_orphanage(struct work_struct *); int ccw_device_is_orphan(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 8633dc53769..8867443b806 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -446,7 +446,8 @@ static void __ccw_device_get_common_pgid(struct ccw_device *cdev) if (cdev->private->pgid[last].inf.ps.state1 == SNID_STATE1_RESET) /* No previous pgid found */ - memcpy(&cdev->private->pgid[0], &css[0]->global_pgid, + memcpy(&cdev->private->pgid[0], + &channel_subsystems[0]->global_pgid, sizeof(struct pgid)); else /* Use existing pgid */ @@ -543,51 +544,6 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) } -static void -ccw_device_nopath_notify(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - struct subchannel *sch; - int ret; - unsigned long flags; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - spin_lock_irqsave(cdev->ccwlock, flags); - sch = to_subchannel(cdev->dev.parent); - /* Extra sanity. */ - if (sch->lpm) - goto out_unlock; - if (sch->driver && sch->driver->notify) { - spin_unlock_irqrestore(cdev->ccwlock, flags); - ret = sch->driver->notify(&sch->dev, CIO_NO_PATH); - spin_lock_irqsave(cdev->ccwlock, flags); - } else - ret = 0; - if (!ret) { - if (get_device(&sch->dev)) { - /* Driver doesn't want to keep device. */ - cio_disable_subchannel(sch); - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister); - queue_work(ccw_device_work, - &cdev->private->kick_work); - } else - put_device(&sch->dev); - } - } else { - cio_disable_subchannel(sch); - ccw_device_set_timeout(cdev, 0); - cdev->private->flags.fake_irb = 0; - cdev->private->state = DEV_STATE_DISCONNECTED; - wake_up(&cdev->private->wait_q); - } -out_unlock: - spin_unlock_irqrestore(cdev->ccwlock, flags); -} - void ccw_device_verify_done(struct ccw_device *cdev, int err) { @@ -631,12 +587,9 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) default: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; - if (cdev->online) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, - &cdev->private->kick_work); - } else + if (cdev->online) + dev_fsm_event(cdev, DEV_EVENT_NOTOPER); + else ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } @@ -690,11 +643,7 @@ ccw_device_disband_done(struct ccw_device *cdev, int err) break; default: cdev->private->flags.donotify = 0; - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } + dev_fsm_event(cdev, DEV_EVENT_NOTOPER); ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } @@ -765,59 +714,16 @@ ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) } /* - * Handle not operational event while offline. + * Handle not operational event in non-special state. */ -static void -ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event) +static void ccw_device_generic_notoper(struct ccw_device *cdev, + enum dev_event dev_event) { struct subchannel *sch; cdev->private->state = DEV_STATE_NOT_OPER; sch = to_subchannel(cdev->dev.parent); - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } - wake_up(&cdev->private->wait_q); -} - -/* - * Handle not operational event while online. - */ -static void -ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event) -{ - struct subchannel *sch; - int ret; - - sch = to_subchannel(cdev->dev.parent); - if (sch->driver->notify) { - spin_unlock_irq(cdev->ccwlock); - ret = sch->driver->notify(&sch->dev, - sch->lpm ? CIO_GONE : CIO_NO_PATH); - spin_lock_irq(cdev->ccwlock); - } else - ret = 0; - if (ret) { - ccw_device_set_timeout(cdev, 0); - cdev->private->flags.fake_irb = 0; - cdev->private->state = DEV_STATE_DISCONNECTED; - wake_up(&cdev->private->wait_q); - return; - } - cdev->private->state = DEV_STATE_NOT_OPER; - cio_disable_subchannel(sch); - if (sch->schib.scsw.actl != 0) { - // FIXME: not-oper indication to device driver ? - ccw_device_call_handler(cdev); - } - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } - wake_up(&cdev->private->wait_q); + css_schedule_eval(sch->schid); } /* @@ -915,18 +821,9 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) cdev->private->state = DEV_STATE_TIMEOUT_KILL; 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); - queue_work(ccw_device_notify_work, - &cdev->private->kick_work); - } else - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - } else if (cdev->handler) + if (ret == -ENODEV) + dev_fsm_event(cdev, DEV_EVENT_NOTOPER); + else if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-ETIMEDOUT)); } @@ -1233,7 +1130,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_SENSE_PGID] = { - [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq, [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, @@ -1245,50 +1142,50 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_OFFLINE] = { - [DEV_EVENT_NOTOPER] = ccw_device_offline_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_offline_irq, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_VERIFY] = { - [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, [DEV_EVENT_VERIFY] = ccw_device_delay_verify, }, [DEV_STATE_ONLINE] = { - [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_irq, [DEV_EVENT_TIMEOUT] = ccw_device_online_timeout, [DEV_EVENT_VERIFY] = ccw_device_online_verify, }, [DEV_STATE_W4SENSE] = { - [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_w4sense, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_online_verify, }, [DEV_STATE_DISBAND_PGID] = { - [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_disband_irq, [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_BOXED] = { - [DEV_EVENT_NOTOPER] = ccw_device_offline_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_stlck_done, [DEV_EVENT_TIMEOUT] = ccw_device_stlck_done, [DEV_EVENT_VERIFY] = ccw_device_nop, }, /* states to wait for i/o completion before doing something */ [DEV_STATE_CLEAR_VERIFY] = { - [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_clear_verify, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_TIMEOUT_KILL] = { - [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, + [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_killing_irq, [DEV_EVENT_TIMEOUT] = ccw_device_killing_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, //FIXME diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 14eba854b15..7fd2dadc329 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -25,6 +25,16 @@ #include "device.h" #include "chp.h" +/** + * ccw_device_set_options_mask() - set some options and unset the rest + * @cdev: device for which the options are to be set + * @flags: options to be set + * + * All flags specified in @flags are set, all flags not specified in @flags + * are cleared. + * Returns: + * %0 on success, -%EINVAL on an invalid flag combination. + */ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) { /* @@ -40,6 +50,15 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) return 0; } +/** + * ccw_device_set_options() - set some options + * @cdev: device for which the options are to be set + * @flags: options to be set + * + * All flags specified in @flags are set, the remainder is left untouched. + * Returns: + * %0 on success, -%EINVAL if an invalid flag combination would ensue. + */ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags) { /* @@ -59,6 +78,13 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags) return 0; } +/** + * ccw_device_clear_options() - clear some options + * @cdev: device for which the options are to be cleared + * @flags: options to be cleared + * + * All flags specified in @flags are cleared, the remainder is left untouched. + */ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags) { cdev->private->options.fast &= (flags & CCWDEV_EARLY_NOTIFICATION) == 0; @@ -67,8 +93,22 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags) cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; } -int -ccw_device_clear(struct ccw_device *cdev, unsigned long intparm) +/** + * ccw_device_clear() - terminate I/O request processing + * @cdev: target ccw device + * @intparm: interruption parameter; value is only used if no I/O is + * outstanding, otherwise the intparm associated with the I/O request + * is returned + * + * ccw_device_clear() calls csch on @cdev's subchannel. + * Returns: + * %0 on success, + * -%ENODEV on device not operational, + * -%EINVAL on invalid device state. + * Context: + * Interrupts disabled, ccw device lock held + */ +int ccw_device_clear(struct ccw_device *cdev, unsigned long intparm) { struct subchannel *sch; int ret; @@ -89,10 +129,33 @@ ccw_device_clear(struct ccw_device *cdev, unsigned long intparm) return ret; } -int -ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, - unsigned long intparm, __u8 lpm, __u8 key, - unsigned long flags) +/** + * ccw_device_start_key() - start a s390 channel program with key + * @cdev: target ccw device + * @cpa: logical start address of channel program + * @intparm: user specific interruption parameter; will be presented back to + * @cdev's interrupt handler. Allows a device driver to associate + * the interrupt with a particular I/O request. + * @lpm: defines the channel path to be used for a specific I/O request. A + * value of 0 will make cio use the opm. + * @key: storage key to be used for the I/O + * @flags: additional flags; defines the action to be performed for I/O + * processing. + * + * Start a S/390 channel program. When the interrupt arrives, the + * IRQ handler is called, either immediately, delayed (dev-end missing, + * or sense required) or never (no IRQ handler registered). + * Returns: + * %0, if the operation was successful; + * -%EBUSY, if the device is busy, or status pending; + * -%EACCES, if no path specified in @lpm is operational; + * -%ENODEV, if the device is not operational. + * Context: + * Interrupts disabled, ccw device lock held + */ +int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, + unsigned long intparm, __u8 lpm, __u8 key, + unsigned long flags) { struct subchannel *sch; int ret; @@ -135,11 +198,38 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, return ret; } - -int -ccw_device_start_timeout_key(struct ccw_device *cdev, struct ccw1 *cpa, - unsigned long intparm, __u8 lpm, __u8 key, - unsigned long flags, int expires) +/** + * ccw_device_start_timeout_key() - start a s390 channel program with timeout and key + * @cdev: target ccw device + * @cpa: logical start address of channel program + * @intparm: user specific interruption parameter; will be presented back to + * @cdev's interrupt handler. Allows a device driver to associate + * the interrupt with a particular I/O request. + * @lpm: defines the channel path to be used for a specific I/O request. A + * value of 0 will make cio use the opm. + * @key: storage key to be used for the I/O + * @flags: additional flags; defines the action to be performed for I/O + * processing. + * @expires: timeout value in jiffies + * + * Start a S/390 channel program. When the interrupt arrives, the + * IRQ handler is called, either immediately, delayed (dev-end missing, + * or sense required) or never (no IRQ handler registered). + * This function notifies the device driver if the channel program has not + * completed during the time specified by @expires. If a timeout occurs, the + * channel program is terminated via xsch, hsch or csch, and the device's + * interrupt handler will be called with an irb containing ERR_PTR(-%ETIMEDOUT). + * Returns: + * %0, if the operation was successful; + * -%EBUSY, if the device is busy, or status pending; + * -%EACCES, if no path specified in @lpm is operational; + * -%ENODEV, if the device is not operational. + * Context: + * Interrupts disabled, ccw device lock held + */ +int ccw_device_start_timeout_key(struct ccw_device *cdev, struct ccw1 *cpa, + unsigned long intparm, __u8 lpm, __u8 key, + unsigned long flags, int expires) { int ret; @@ -152,18 +242,67 @@ ccw_device_start_timeout_key(struct ccw_device *cdev, struct ccw1 *cpa, return ret; } -int -ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa, - unsigned long intparm, __u8 lpm, unsigned long flags) +/** + * ccw_device_start() - start a s390 channel program + * @cdev: target ccw device + * @cpa: logical start address of channel program + * @intparm: user specific interruption parameter; will be presented back to + * @cdev's interrupt handler. Allows a device driver to associate + * the interrupt with a particular I/O request. + * @lpm: defines the channel path to be used for a specific I/O request. A + * value of 0 will make cio use the opm. + * @flags: additional flags; defines the action to be performed for I/O + * processing. + * + * Start a S/390 channel program. When the interrupt arrives, the + * IRQ handler is called, either immediately, delayed (dev-end missing, + * or sense required) or never (no IRQ handler registered). + * Returns: + * %0, if the operation was successful; + * -%EBUSY, if the device is busy, or status pending; + * -%EACCES, if no path specified in @lpm is operational; + * -%ENODEV, if the device is not operational. + * Context: + * Interrupts disabled, ccw device lock held + */ +int ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa, + unsigned long intparm, __u8 lpm, unsigned long flags) { return ccw_device_start_key(cdev, cpa, intparm, lpm, PAGE_DEFAULT_KEY, flags); } -int -ccw_device_start_timeout(struct ccw_device *cdev, struct ccw1 *cpa, - unsigned long intparm, __u8 lpm, unsigned long flags, - int expires) +/** + * ccw_device_start_timeout() - start a s390 channel program with timeout + * @cdev: target ccw device + * @cpa: logical start address of channel program + * @intparm: user specific interruption parameter; will be presented back to + * @cdev's interrupt handler. Allows a device driver to associate + * the interrupt with a particular I/O request. + * @lpm: defines the channel path to be used for a specific I/O request. A + * value of 0 will make cio use the opm. + * @flags: additional flags; defines the action to be performed for I/O + * processing. + * @expires: timeout value in jiffies + * + * Start a S/390 channel program. When the interrupt arrives, the + * IRQ handler is called, either immediately, delayed (dev-end missing, + * or sense required) or never (no IRQ handler registered). + * This function notifies the device driver if the channel program has not + * completed during the time specified by @expires. If a timeout occurs, the + * channel program is terminated via xsch, hsch or csch, and the device's + * interrupt handler will be called with an irb containing ERR_PTR(-%ETIMEDOUT). + * Returns: + * %0, if the operation was successful; + * -%EBUSY, if the device is busy, or status pending; + * -%EACCES, if no path specified in @lpm is operational; + * -%ENODEV, if the device is not operational. + * Context: + * Interrupts disabled, ccw device lock held + */ +int ccw_device_start_timeout(struct ccw_device *cdev, struct ccw1 *cpa, + unsigned long intparm, __u8 lpm, + unsigned long flags, int expires) { return ccw_device_start_timeout_key(cdev, cpa, intparm, lpm, PAGE_DEFAULT_KEY, flags, @@ -171,8 +310,23 @@ ccw_device_start_timeout(struct ccw_device *cdev, struct ccw1 *cpa, } -int -ccw_device_halt(struct ccw_device *cdev, unsigned long intparm) +/** + * ccw_device_halt() - halt I/O request processing + * @cdev: target ccw device + * @intparm: interruption parameter; value is only used if no I/O is + * outstanding, otherwise the intparm associated with the I/O request + * is returned + * + * ccw_device_halt() calls hsch on @cdev's subchannel. + * Returns: + * %0 on success, + * -%ENODEV on device not operational, + * -%EINVAL on invalid device state, + * -%EBUSY on device busy or interrupt pending. + * Context: + * Interrupts disabled, ccw device lock held + */ +int ccw_device_halt(struct ccw_device *cdev, unsigned long intparm) { struct subchannel *sch; int ret; @@ -193,8 +347,20 @@ ccw_device_halt(struct ccw_device *cdev, unsigned long intparm) return ret; } -int -ccw_device_resume(struct ccw_device *cdev) +/** + * ccw_device_resume() - resume channel program execution + * @cdev: target ccw device + * + * ccw_device_resume() calls rsch on @cdev's subchannel. + * Returns: + * %0 on success, + * -%ENODEV on device not operational, + * -%EINVAL on invalid device state, + * -%EBUSY on device busy or interrupt pending. + * Context: + * Interrupts disabled, ccw device lock held + */ +int ccw_device_resume(struct ccw_device *cdev) { struct subchannel *sch; @@ -260,11 +426,21 @@ ccw_device_call_handler(struct ccw_device *cdev) return 1; } -/* - * Search for CIW command in extended sense data. +/** + * ccw_device_get_ciw() - Search for CIW command in extended sense data. + * @cdev: ccw device to inspect + * @ct: command type to look for + * + * During SenseID, command information words (CIWs) describing special + * commands available to the device may have been stored in the extended + * sense data. This function searches for CIWs of a specified command + * type in the extended sense data. + * Returns: + * %NULL if no extended sense data has been stored or if no CIW of the + * specified command type could be found, + * else a pointer to the CIW of the specified command type. */ -struct ciw * -ccw_device_get_ciw(struct ccw_device *cdev, __u32 ct) +struct ciw *ccw_device_get_ciw(struct ccw_device *cdev, __u32 ct) { int ciw_cnt; @@ -276,8 +452,14 @@ ccw_device_get_ciw(struct ccw_device *cdev, __u32 ct) return NULL; } -__u8 -ccw_device_get_path_mask(struct ccw_device *cdev) +/** + * ccw_device_get_path_mask() - get currently available paths + * @cdev: ccw device to be queried + * Returns: + * %0 if no subchannel for the device is available, + * else the mask of currently available paths for the ccw device's subchannel. + */ +__u8 ccw_device_get_path_mask(struct ccw_device *cdev) { struct subchannel *sch; @@ -357,8 +539,7 @@ out_unlock: return ret; } -void * -ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) +void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) { struct subchannel *sch; struct chp_id chpid; diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index d8d479876ec..40a3208c7cf 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -1024,9 +1024,9 @@ __qdio_outbound_processing(struct qdio_q *q) } static void -qdio_outbound_processing(struct qdio_q *q) +qdio_outbound_processing(unsigned long q) { - __qdio_outbound_processing(q); + __qdio_outbound_processing((struct qdio_q *) q); } /************************* INBOUND ROUTINES *******************************/ @@ -1449,9 +1449,10 @@ out: } static void -tiqdio_inbound_processing(struct qdio_q *q) +tiqdio_inbound_processing(unsigned long q) { - __tiqdio_inbound_processing(q, atomic_read(&spare_indicator_usecount)); + __tiqdio_inbound_processing((struct qdio_q *) q, + atomic_read(&spare_indicator_usecount)); } static void @@ -1494,9 +1495,9 @@ again: } static void -qdio_inbound_processing(struct qdio_q *q) +qdio_inbound_processing(unsigned long q) { - __qdio_inbound_processing(q); + __qdio_inbound_processing((struct qdio_q *) q); } /************************* MAIN ROUTINES *******************************/ @@ -1760,12 +1761,15 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, q->handler=input_handler; q->dev_st_chg_ind=irq_ptr->dev_st_chg_ind; - q->tasklet.data=(unsigned long)q; /* q->is_thinint_q isn't valid at this time, but - * irq_ptr->is_thinint_irq is */ - q->tasklet.func=(void(*)(unsigned long)) - ((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing: - &qdio_inbound_processing); + * irq_ptr->is_thinint_irq is + */ + if (irq_ptr->is_thinint_irq) + tasklet_init(&q->tasklet, tiqdio_inbound_processing, + (unsigned long) q); + else + tasklet_init(&q->tasklet, qdio_inbound_processing, + (unsigned long) q); /* actually this is not used for inbound queues. yet. */ atomic_set(&q->busy_siga_counter,0); @@ -1836,13 +1840,10 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, q->last_move_ftc=0; q->handler=output_handler; - q->tasklet.data=(unsigned long)q; - q->tasklet.func=(void(*)(unsigned long)) - &qdio_outbound_processing; - q->timer.function=(void(*)(unsigned long)) - &qdio_outbound_processing; - q->timer.data = (long)q; - init_timer(&q->timer); + tasklet_init(&q->tasklet, qdio_outbound_processing, + (unsigned long) q); + setup_timer(&q->timer, qdio_outbound_processing, + (unsigned long) q); atomic_set(&q->busy_siga_counter,0); q->timing.busy_start=0; @@ -3726,7 +3727,7 @@ qdio_performance_stats_store(struct bus_type *bus, const char *buf, size_t count #endif /* CONFIG_64BIT */ } } else { - QDIO_PRINT_WARN("QDIO performance_stats: write 0 or 1 to this file!\n"); + QDIO_PRINT_ERR("QDIO performance_stats: write 0 or 1 to this file!\n"); return -EINVAL; } return count; diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 90bd2201451..d334b0f7a1e 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1231,8 +1231,9 @@ static void ap_reset_domain(void) { int i; - for (i = 0; i < AP_DEVICES; i++) - ap_reset_queue(AP_MKQID(i, ap_domain_index)); + if (ap_domain_index != -1) + for (i = 0; i < AP_DEVICES; i++) + ap_reset_queue(AP_MKQID(i, ap_domain_index)); } static void ap_reset_all(void) diff --git a/drivers/s390/crypto/zcrypt_mono.c b/drivers/s390/crypto/zcrypt_mono.c index 2a9349ad68b..44253fdd413 100644 --- a/drivers/s390/crypto/zcrypt_mono.c +++ b/drivers/s390/crypto/zcrypt_mono.c @@ -45,7 +45,7 @@ /** * The module initialization code. */ -int __init zcrypt_init(void) +static int __init zcrypt_init(void) { int rc; @@ -86,7 +86,7 @@ out: /** * The module termination code. */ -void __exit zcrypt_exit(void) +static void __exit zcrypt_exit(void) { zcrypt_cex2a_exit(); zcrypt_pcixcc_exit(); diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 64948788d30..70b9ddc8cf9 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -277,7 +277,7 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, }; struct { struct type6_hdr hdr; - struct ica_CPRBX cprbx; + struct CPRBX cprbx; } __attribute__((packed)) *msg = ap_msg->message; int rcblen = CEIL4(xcRB->request_control_blk_length); @@ -432,14 +432,17 @@ static int convert_type86_ica(struct zcrypt_device *zdev, } if (service_rc == 8 && service_rs == 770) { PDEBUG("Invalid key length on PCIXCC/CEX2C\n"); - zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD; - return -EAGAIN; + return -EINVAL; } if (service_rc == 8 && service_rs == 783) { PDEBUG("Extended bitlengths not enabled on PCIXCC/CEX2C\n"); zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD; return -EAGAIN; } + if (service_rc == 12 && service_rs == 769) { + PDEBUG("Invalid key on PCIXCC/CEX2C\n"); + return -EINVAL; + } PRINTK("Unknown service rc/rs (PCIXCC/CEX2C): %d/%d\n", service_rc, service_rs); zdev->online = 0; diff --git a/drivers/s390/crypto/zcrypt_pcixcc.h b/drivers/s390/crypto/zcrypt_pcixcc.h index a78ff307fd1..8cb7d7a6973 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.h +++ b/drivers/s390/crypto/zcrypt_pcixcc.h @@ -28,51 +28,6 @@ #ifndef _ZCRYPT_PCIXCC_H_ #define _ZCRYPT_PCIXCC_H_ -/** - * CPRBX - * Note that all shorts and ints are big-endian. - * All pointer fields are 16 bytes long, and mean nothing. - * - * A request CPRB is followed by a request_parameter_block. - * - * The request (or reply) parameter block is organized thus: - * function code - * VUD block - * key block - */ -struct CPRBX { - unsigned short cprb_len; /* CPRB length 220 */ - unsigned char cprb_ver_id; /* CPRB version id. 0x02 */ - unsigned char pad_000[3]; /* Alignment pad bytes */ - unsigned char func_id[2]; /* function id 0x5432 */ - unsigned char cprb_flags[4]; /* Flags */ - unsigned int req_parml; /* request parameter buffer len */ - unsigned int req_datal; /* request data buffer */ - unsigned int rpl_msgbl; /* reply message block length */ - unsigned int rpld_parml; /* replied parameter block len */ - unsigned int rpl_datal; /* reply data block len */ - unsigned int rpld_datal; /* replied data block len */ - unsigned int req_extbl; /* request extension block len */ - unsigned char pad_001[4]; /* reserved */ - unsigned int rpld_extbl; /* replied extension block len */ - unsigned char req_parmb[16]; /* request parm block 'address' */ - unsigned char req_datab[16]; /* request data block 'address' */ - unsigned char rpl_parmb[16]; /* reply parm block 'address' */ - unsigned char rpl_datab[16]; /* reply data block 'address' */ - unsigned char req_extb[16]; /* request extension block 'addr'*/ - unsigned char rpl_extb[16]; /* reply extension block 'addres'*/ - unsigned short ccp_rtcode; /* server return code */ - unsigned short ccp_rscode; /* server reason code */ - unsigned int mac_data_len; /* Mac Data Length */ - unsigned char logon_id[8]; /* Logon Identifier */ - unsigned char mac_value[8]; /* Mac Value */ - unsigned char mac_content_flgs;/* Mac content flag byte */ - unsigned char pad_002; /* Alignment */ - unsigned short domain; /* Domain */ - unsigned char pad_003[12]; /* Domain masks */ - unsigned char pad_004[36]; /* reserved */ -} __attribute__((packed)); - int zcrypt_pcixcc_init(void); void zcrypt_pcixcc_exit(void); diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 1c8f71a5985..c0d1c0eb320 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -28,7 +28,7 @@ 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 void zfcp_ccw_shutdown(struct device *); +static void zfcp_ccw_shutdown(struct ccw_device *); static struct ccw_device_id zfcp_ccw_device_id[] = { {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE, @@ -51,9 +51,7 @@ static struct ccw_driver zfcp_ccw_driver = { .set_online = zfcp_ccw_set_online, .set_offline = zfcp_ccw_set_offline, .notify = zfcp_ccw_notify, - .driver = { - .shutdown = zfcp_ccw_shutdown, - }, + .shutdown = zfcp_ccw_shutdown, }; MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id); @@ -277,12 +275,12 @@ zfcp_ccw_register(void) * Makes sure that QDIO queues are down when the system gets stopped. */ static void -zfcp_ccw_shutdown(struct device *dev) +zfcp_ccw_shutdown(struct ccw_device *cdev) { struct zfcp_adapter *adapter; down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(dev); + adapter = dev_get_drvdata(&cdev->dev); zfcp_erp_adapter_shutdown(adapter, 0); zfcp_erp_wait(adapter); up(&zfcp_data.config_sema); diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 5f3212440f6..ffa3bf75694 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -19,8 +19,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <asm/debug.h> #include <linux/ctype.h> +#include <asm/debug.h> #include "zfcp_ext.h" static u32 dbfsize = 4; @@ -35,17 +35,17 @@ static int zfcp_dbf_stck(char *out_buf, const char *label, unsigned long long stck) { unsigned long long sec; - struct timespec xtime; + struct timespec dbftime; int len = 0; stck -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); sec = stck >> 12; do_div(sec, 1000000); - xtime.tv_sec = sec; + dbftime.tv_sec = sec; stck -= (sec * 1000000) << 12; - xtime.tv_nsec = ((stck * 1000) >> 12); + dbftime.tv_nsec = ((stck * 1000) >> 12); len += sprintf(out_buf + len, "%-24s%011lu:%06lu\n", - label, xtime.tv_sec, xtime.tv_nsec); + label, dbftime.tv_sec, dbftime.tv_nsec); return len; } diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index d8cd75ce2d9..16b4418ab25 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -54,7 +54,7 @@ static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *, int); static int zfcp_erp_strategy_statechange(int, u32, struct zfcp_adapter *, struct zfcp_port *, struct zfcp_unit *, int); -static inline int zfcp_erp_strategy_statechange_detected(atomic_t *, u32); +static int zfcp_erp_strategy_statechange_detected(atomic_t *, u32); static int zfcp_erp_strategy_followup_actions(int, struct zfcp_adapter *, struct zfcp_port *, struct zfcp_unit *, int); @@ -106,8 +106,8 @@ static void zfcp_erp_action_cleanup(int, struct zfcp_adapter *, static void zfcp_erp_action_ready(struct zfcp_erp_action *); static int zfcp_erp_action_exists(struct zfcp_erp_action *); -static inline void zfcp_erp_action_to_ready(struct zfcp_erp_action *); -static inline void zfcp_erp_action_to_running(struct zfcp_erp_action *); +static void zfcp_erp_action_to_ready(struct zfcp_erp_action *); +static void zfcp_erp_action_to_running(struct zfcp_erp_action *); static void zfcp_erp_memwait_handler(unsigned long); @@ -952,7 +952,7 @@ zfcp_erp_memwait_handler(unsigned long data) * action gets an appropriate flag and will be processed * accordingly */ -void zfcp_erp_timeout_handler(unsigned long data) +static void zfcp_erp_timeout_handler(unsigned long data) { struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data; struct zfcp_adapter *adapter = erp_action->adapter; @@ -1491,7 +1491,7 @@ zfcp_erp_strategy_statechange(int action, return retval; } -static inline int +static int zfcp_erp_strategy_statechange_detected(atomic_t * target_status, u32 erp_status) { return @@ -2001,7 +2001,7 @@ zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *erp_action, int close) * returns: 0 - successful setup * !0 - failed setup */ -int +static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action) { int retval; @@ -3248,8 +3248,7 @@ static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit) zfcp_erp_action_dismiss(&unit->erp_action); } -static inline void -zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action) +static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action) { struct zfcp_adapter *adapter = erp_action->adapter; @@ -3258,8 +3257,7 @@ zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action) list_move(&erp_action->list, &erp_action->adapter->erp_running_head); } -static inline void -zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action) +static void zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action) { struct zfcp_adapter *adapter = erp_action->adapter; diff --git a/fs/dlm/dlm_internal.h b/fs/dlm/dlm_internal.h index 74901e981e1..d2fc2384c3b 100644 --- a/fs/dlm/dlm_internal.h +++ b/fs/dlm/dlm_internal.h @@ -491,6 +491,7 @@ struct dlm_ls { uint64_t ls_recover_seq; struct dlm_recover *ls_recover_args; struct rw_semaphore ls_in_recovery; /* block local requests */ + struct rw_semaphore ls_recv_active; /* block dlm_recv */ struct list_head ls_requestqueue;/* queue remote requests */ struct mutex ls_requestqueue_mutex; char *ls_recover_buf; diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c index 2082daf083d..3915b8e1414 100644 --- a/fs/dlm/lock.c +++ b/fs/dlm/lock.c @@ -3638,55 +3638,8 @@ static void receive_lookup_reply(struct dlm_ls *ls, struct dlm_message *ms) dlm_put_lkb(lkb); } -int dlm_receive_message(struct dlm_header *hd, int nodeid, int recovery) +static void _receive_message(struct dlm_ls *ls, struct dlm_message *ms) { - struct dlm_message *ms = (struct dlm_message *) hd; - struct dlm_ls *ls; - int error = 0; - - if (!recovery) - dlm_message_in(ms); - - ls = dlm_find_lockspace_global(hd->h_lockspace); - if (!ls) { - log_print("drop message %d from %d for unknown lockspace %d", - ms->m_type, nodeid, hd->h_lockspace); - return -EINVAL; - } - - /* recovery may have just ended leaving a bunch of backed-up requests - in the requestqueue; wait while dlm_recoverd clears them */ - - if (!recovery) - dlm_wait_requestqueue(ls); - - /* recovery may have just started while there were a bunch of - in-flight requests -- save them in requestqueue to be processed - after recovery. we can't let dlm_recvd block on the recovery - lock. if dlm_recoverd is calling this function to clear the - requestqueue, it needs to be interrupted (-EINTR) if another - recovery operation is starting. */ - - while (1) { - if (dlm_locking_stopped(ls)) { - if (recovery) { - error = -EINTR; - goto out; - } - error = dlm_add_requestqueue(ls, nodeid, hd); - if (error == -EAGAIN) - continue; - else { - error = -EINTR; - goto out; - } - } - - if (dlm_lock_recovery_try(ls)) - break; - schedule(); - } - switch (ms->m_type) { /* messages sent to a master node */ @@ -3761,17 +3714,90 @@ int dlm_receive_message(struct dlm_header *hd, int nodeid, int recovery) log_error(ls, "unknown message type %d", ms->m_type); } - dlm_unlock_recovery(ls); - out: - dlm_put_lockspace(ls); dlm_astd_wake(); - return error; } +/* If the lockspace is in recovery mode (locking stopped), then normal + messages are saved on the requestqueue for processing after recovery is + done. When not in recovery mode, we wait for dlm_recoverd to drain saved + messages off the requestqueue before we process new ones. This occurs right + after recovery completes when we transition from saving all messages on + requestqueue, to processing all the saved messages, to processing new + messages as they arrive. */ -/* - * Recovery related - */ +static void dlm_receive_message(struct dlm_ls *ls, struct dlm_message *ms, + int nodeid) +{ + if (dlm_locking_stopped(ls)) { + dlm_add_requestqueue(ls, nodeid, (struct dlm_header *) ms); + } else { + dlm_wait_requestqueue(ls); + _receive_message(ls, ms); + } +} + +/* This is called by dlm_recoverd to process messages that were saved on + the requestqueue. */ + +void dlm_receive_message_saved(struct dlm_ls *ls, struct dlm_message *ms) +{ + _receive_message(ls, ms); +} + +/* This is called by the midcomms layer when something is received for + the lockspace. It could be either a MSG (normal message sent as part of + standard locking activity) or an RCOM (recovery message sent as part of + lockspace recovery). */ + +void dlm_receive_buffer(struct dlm_header *hd, int nodeid) +{ + struct dlm_message *ms = (struct dlm_message *) hd; + struct dlm_rcom *rc = (struct dlm_rcom *) hd; + struct dlm_ls *ls; + int type = 0; + + switch (hd->h_cmd) { + case DLM_MSG: + dlm_message_in(ms); + type = ms->m_type; + break; + case DLM_RCOM: + dlm_rcom_in(rc); + type = rc->rc_type; + break; + default: + log_print("invalid h_cmd %d from %u", hd->h_cmd, nodeid); + return; + } + + if (hd->h_nodeid != nodeid) { + log_print("invalid h_nodeid %d from %d lockspace %x", + hd->h_nodeid, nodeid, hd->h_lockspace); + return; + } + + ls = dlm_find_lockspace_global(hd->h_lockspace); + if (!ls) { + log_print("invalid h_lockspace %x from %d cmd %d type %d", + hd->h_lockspace, nodeid, hd->h_cmd, type); + + if (hd->h_cmd == DLM_RCOM && type == DLM_RCOM_STATUS) + dlm_send_ls_not_ready(nodeid, rc); + return; + } + + /* this rwsem allows dlm_ls_stop() to wait for all dlm_recv threads to + be inactive (in this ls) before transitioning to recovery mode */ + + down_read(&ls->ls_recv_active); + if (hd->h_cmd == DLM_MSG) + dlm_receive_message(ls, ms, nodeid); + else + dlm_receive_rcom(ls, rc, nodeid); + up_read(&ls->ls_recv_active); + + dlm_put_lockspace(ls); +} static void recover_convert_waiter(struct dlm_ls *ls, struct dlm_lkb *lkb) { @@ -4429,7 +4455,8 @@ int dlm_user_unlock(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, if (lvb_in && ua->lksb.sb_lvbptr) memcpy(ua->lksb.sb_lvbptr, lvb_in, DLM_USER_LVB_LEN); - ua->castparam = ua_tmp->castparam; + if (ua_tmp->castparam) + ua->castparam = ua_tmp->castparam; ua->user_lksb = ua_tmp->user_lksb; error = set_unlock_args(flags, ua, &args); @@ -4474,7 +4501,8 @@ int dlm_user_cancel(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, goto out; ua = (struct dlm_user_args *)lkb->lkb_astparam; - ua->castparam = ua_tmp->castparam; + if (ua_tmp->castparam) + ua->castparam = ua_tmp->castparam; ua->user_lksb = ua_tmp->user_lksb; error = set_unlock_args(flags, ua, &args); diff --git a/fs/dlm/lock.h b/fs/dlm/lock.h index 1720313c22d..ada04680a1e 100644 --- a/fs/dlm/lock.h +++ b/fs/dlm/lock.h @@ -16,7 +16,8 @@ void dlm_print_rsb(struct dlm_rsb *r); void dlm_dump_rsb(struct dlm_rsb *r); void dlm_print_lkb(struct dlm_lkb *lkb); -int dlm_receive_message(struct dlm_header *hd, int nodeid, int recovery); +void dlm_receive_message_saved(struct dlm_ls *ls, struct dlm_message *ms); +void dlm_receive_buffer(struct dlm_header *hd, int nodeid); int dlm_modes_compat(int mode1, int mode2); int dlm_find_rsb(struct dlm_ls *ls, char *name, int namelen, unsigned int flags, struct dlm_rsb **r_ret); diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c index 1dc72105ab1..628eaa669e6 100644 --- a/fs/dlm/lockspace.c +++ b/fs/dlm/lockspace.c @@ -519,6 +519,7 @@ static int new_lockspace(char *name, int namelen, void **lockspace, ls->ls_recover_seq = 0; ls->ls_recover_args = NULL; init_rwsem(&ls->ls_in_recovery); + init_rwsem(&ls->ls_recv_active); INIT_LIST_HEAD(&ls->ls_requestqueue); mutex_init(&ls->ls_requestqueue_mutex); mutex_init(&ls->ls_clear_proc_locks); diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index 9e9d2e82f40..58bf3f5cdbe 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c @@ -334,18 +334,8 @@ static void close_connection(struct connection *con, bool and_other) con->rx_page = NULL; } - /* If we are an 'othercon' then NULL the pointer to us - from the parent and tidy ourself up */ - if (test_bit(CF_IS_OTHERCON, &con->flags)) { - struct connection *parent = __nodeid2con(con->nodeid, 0); - parent->othercon = NULL; - kmem_cache_free(con_cache, con); - } - else { - /* Parent connections get reused */ - con->retries = 0; - mutex_unlock(&con->sock_mutex); - } + con->retries = 0; + mutex_unlock(&con->sock_mutex); } /* We only send shutdown messages to nodes that are not part of the cluster */ @@ -731,6 +721,8 @@ static int tcp_accept_from_sock(struct connection *con) INIT_WORK(&othercon->swork, process_send_sockets); INIT_WORK(&othercon->rwork, process_recv_sockets); set_bit(CF_IS_OTHERCON, &othercon->flags); + } + if (!othercon->sock) { newcon->othercon = othercon; othercon->sock = newsock; newsock->sk->sk_user_data = othercon; @@ -1272,14 +1264,15 @@ static void send_to_sock(struct connection *con) if (len) { ret = sendpage(con->sock, e->page, offset, len, msg_flags); - if (ret == -EAGAIN || ret == 0) + if (ret == -EAGAIN || ret == 0) { + cond_resched(); goto out; + } if (ret <= 0) goto send_error; - } else { + } /* Don't starve people filling buffers */ cond_resched(); - } spin_lock(&con->writequeue_lock); e->offset += ret; diff --git a/fs/dlm/member.c b/fs/dlm/member.c index d09977528f6..e9cdcab306e 100644 --- a/fs/dlm/member.c +++ b/fs/dlm/member.c @@ -18,10 +18,6 @@ #include "rcom.h" #include "config.h" -/* - * Following called by dlm_recoverd thread - */ - static void add_ordered_member(struct dlm_ls *ls, struct dlm_member *new) { struct dlm_member *memb = NULL; @@ -250,18 +246,30 @@ int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out) return error; } -/* - * Following called from lockspace.c - */ +/* Userspace guarantees that dlm_ls_stop() has completed on all nodes before + dlm_ls_start() is called on any of them to start the new recovery. */ int dlm_ls_stop(struct dlm_ls *ls) { int new; /* - * A stop cancels any recovery that's in progress (see RECOVERY_STOP, - * dlm_recovery_stopped()) and prevents any new locks from being - * processed (see RUNNING, dlm_locking_stopped()). + * Prevent dlm_recv from being in the middle of something when we do + * the stop. This includes ensuring dlm_recv isn't processing a + * recovery message (rcom), while dlm_recoverd is aborting and + * resetting things from an in-progress recovery. i.e. we want + * dlm_recoverd to abort its recovery without worrying about dlm_recv + * processing an rcom at the same time. Stopping dlm_recv also makes + * it easy for dlm_receive_message() to check locking stopped and add a + * message to the requestqueue without races. + */ + + down_write(&ls->ls_recv_active); + + /* + * Abort any recovery that's in progress (see RECOVERY_STOP, + * dlm_recovery_stopped()) and tell any other threads running in the + * dlm to quit any processing (see RUNNING, dlm_locking_stopped()). */ spin_lock(&ls->ls_recover_lock); @@ -271,8 +279,14 @@ int dlm_ls_stop(struct dlm_ls *ls) spin_unlock(&ls->ls_recover_lock); /* + * Let dlm_recv run again, now any normal messages will be saved on the + * requestqueue for later. + */ + + up_write(&ls->ls_recv_active); + + /* * This in_recovery lock does two things: - * * 1) Keeps this function from returning until all threads are out * of locking routines and locking is truely stopped. * 2) Keeps any new requests from being processed until it's unlocked @@ -284,9 +298,8 @@ int dlm_ls_stop(struct dlm_ls *ls) /* * The recoverd suspend/resume makes sure that dlm_recoverd (if - * running) has noticed the clearing of RUNNING above and quit - * processing the previous recovery. This will be true for all nodes - * before any nodes start the new recovery. + * running) has noticed RECOVERY_STOP above and quit processing the + * previous recovery. */ dlm_recoverd_suspend(ls); diff --git a/fs/dlm/midcomms.c b/fs/dlm/midcomms.c index a5126e0c68a..f8c69dda16a 100644 --- a/fs/dlm/midcomms.c +++ b/fs/dlm/midcomms.c @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -27,7 +27,6 @@ #include "dlm_internal.h" #include "lowcomms.h" #include "config.h" -#include "rcom.h" #include "lock.h" #include "midcomms.h" @@ -117,19 +116,7 @@ int dlm_process_incoming_buffer(int nodeid, const void *base, offset &= (limit - 1); len -= msglen; - switch (msg->h_cmd) { - case DLM_MSG: - dlm_receive_message(msg, nodeid, 0); - break; - - case DLM_RCOM: - dlm_receive_rcom(msg, nodeid); - break; - - default: - log_print("unknown msg type %x from %u: %u %u %u %u", - msg->h_cmd, nodeid, msglen, len, offset, ret); - } + dlm_receive_buffer(msg, nodeid); } if (msg != (struct dlm_header *) __tmp) diff --git a/fs/dlm/rcom.c b/fs/dlm/rcom.c index 188b91c027e..ae2fd97fa4a 100644 --- a/fs/dlm/rcom.c +++ b/fs/dlm/rcom.c @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -386,7 +386,10 @@ static void receive_rcom_lock_reply(struct dlm_ls *ls, struct dlm_rcom *rc_in) dlm_recover_process_copy(ls, rc_in); } -static int send_ls_not_ready(int nodeid, struct dlm_rcom *rc_in) +/* If the lockspace doesn't exist then still send a status message + back; it's possible that it just doesn't have its global_id yet. */ + +int dlm_send_ls_not_ready(int nodeid, struct dlm_rcom *rc_in) { struct dlm_rcom *rc; struct rcom_config *rf; @@ -446,28 +449,11 @@ static int is_old_reply(struct dlm_ls *ls, struct dlm_rcom *rc) return rv; } -/* Called by dlm_recvd; corresponds to dlm_receive_message() but special +/* Called by dlm_recv; corresponds to dlm_receive_message() but special recovery-only comms are sent through here. */ -void dlm_receive_rcom(struct dlm_header *hd, int nodeid) +void dlm_receive_rcom(struct dlm_ls *ls, struct dlm_rcom *rc, int nodeid) { - struct dlm_rcom *rc = (struct dlm_rcom *) hd; - struct dlm_ls *ls; - - dlm_rcom_in(rc); - - /* If the lockspace doesn't exist then still send a status message - back; it's possible that it just doesn't have its global_id yet. */ - - ls = dlm_find_lockspace_global(hd->h_lockspace); - if (!ls) { - log_print("lockspace %x from %d type %x not found", - hd->h_lockspace, nodeid, rc->rc_type); - if (rc->rc_type == DLM_RCOM_STATUS) - send_ls_not_ready(nodeid, rc); - return; - } - if (dlm_recovery_stopped(ls) && (rc->rc_type != DLM_RCOM_STATUS)) { log_debug(ls, "ignoring recovery message %x from %d", rc->rc_type, nodeid); @@ -477,12 +463,6 @@ void dlm_receive_rcom(struct dlm_header *hd, int nodeid) if (is_old_reply(ls, rc)) goto out; - if (nodeid != rc->rc_header.h_nodeid) { - log_error(ls, "bad rcom nodeid %d from %d", - rc->rc_header.h_nodeid, nodeid); - goto out; - } - switch (rc->rc_type) { case DLM_RCOM_STATUS: receive_rcom_status(ls, rc); @@ -520,6 +500,6 @@ void dlm_receive_rcom(struct dlm_header *hd, int nodeid) DLM_ASSERT(0, printk("rc_type=%x\n", rc->rc_type);); } out: - dlm_put_lockspace(ls); + return; } diff --git a/fs/dlm/rcom.h b/fs/dlm/rcom.h index d7984321ff4..b09abd29ba3 100644 --- a/fs/dlm/rcom.h +++ b/fs/dlm/rcom.h @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -18,7 +18,8 @@ int dlm_rcom_status(struct dlm_ls *ls, int nodeid); int dlm_rcom_names(struct dlm_ls *ls, int nodeid, char *last_name,int last_len); int dlm_send_rcom_lookup(struct dlm_rsb *r, int dir_nodeid); int dlm_send_rcom_lock(struct dlm_rsb *r, struct dlm_lkb *lkb); -void dlm_receive_rcom(struct dlm_header *hd, int nodeid); +void dlm_receive_rcom(struct dlm_ls *ls, struct dlm_rcom *rc, int nodeid); +int dlm_send_ls_not_ready(int nodeid, struct dlm_rcom *rc_in); #endif diff --git a/fs/dlm/recoverd.c b/fs/dlm/recoverd.c index 66575997861..4b89e20eebe 100644 --- a/fs/dlm/recoverd.c +++ b/fs/dlm/recoverd.c @@ -24,19 +24,28 @@ /* If the start for which we're re-enabling locking (seq) has been superseded - by a newer stop (ls_recover_seq), we need to leave locking disabled. */ + by a newer stop (ls_recover_seq), we need to leave locking disabled. + + We suspend dlm_recv threads here to avoid the race where dlm_recv a) sees + locking stopped and b) adds a message to the requestqueue, but dlm_recoverd + enables locking and clears the requestqueue between a and b. */ static int enable_locking(struct dlm_ls *ls, uint64_t seq) { int error = -EINTR; + down_write(&ls->ls_recv_active); + spin_lock(&ls->ls_recover_lock); if (ls->ls_recover_seq == seq) { set_bit(LSFL_RUNNING, &ls->ls_flags); + /* unblocks processes waiting to enter the dlm */ up_write(&ls->ls_in_recovery); error = 0; } spin_unlock(&ls->ls_recover_lock); + + up_write(&ls->ls_recv_active); return error; } diff --git a/fs/dlm/requestqueue.c b/fs/dlm/requestqueue.c index 65008d79c96..0de04f17cce 100644 --- a/fs/dlm/requestqueue.c +++ b/fs/dlm/requestqueue.c @@ -1,7 +1,7 @@ /****************************************************************************** ******************************************************************************* ** -** Copyright (C) 2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -20,7 +20,7 @@ struct rq_entry { struct list_head list; int nodeid; - char request[1]; + char request[0]; }; /* @@ -30,42 +30,39 @@ struct rq_entry { * lockspace is enabled on some while still suspended on others. */ -int dlm_add_requestqueue(struct dlm_ls *ls, int nodeid, struct dlm_header *hd) +void dlm_add_requestqueue(struct dlm_ls *ls, int nodeid, struct dlm_header *hd) { struct rq_entry *e; int length = hd->h_length; - int rv = 0; e = kmalloc(sizeof(struct rq_entry) + length, GFP_KERNEL); if (!e) { - log_print("dlm_add_requestqueue: out of memory\n"); - return 0; + log_print("dlm_add_requestqueue: out of memory len %d", length); + return; } e->nodeid = nodeid; memcpy(e->request, hd, length); - /* We need to check dlm_locking_stopped() after taking the mutex to - avoid a race where dlm_recoverd enables locking and runs - process_requestqueue between our earlier dlm_locking_stopped check - and this addition to the requestqueue. */ - mutex_lock(&ls->ls_requestqueue_mutex); - if (dlm_locking_stopped(ls)) - list_add_tail(&e->list, &ls->ls_requestqueue); - else { - log_debug(ls, "dlm_add_requestqueue skip from %d", nodeid); - kfree(e); - rv = -EAGAIN; - } + list_add_tail(&e->list, &ls->ls_requestqueue); mutex_unlock(&ls->ls_requestqueue_mutex); - return rv; } +/* + * Called by dlm_recoverd to process normal messages saved while recovery was + * happening. Normal locking has been enabled before this is called. dlm_recv + * upon receiving a message, will wait for all saved messages to be drained + * here before processing the message it got. If a new dlm_ls_stop() arrives + * while we're processing these saved messages, it may block trying to suspend + * dlm_recv if dlm_recv is waiting for us in dlm_wait_requestqueue. In that + * case, we don't abort since locking_stopped is still 0. If dlm_recv is not + * waiting for us, then this processing may be aborted due to locking_stopped. + */ + int dlm_process_requestqueue(struct dlm_ls *ls) { struct rq_entry *e; - struct dlm_header *hd; int error = 0; mutex_lock(&ls->ls_requestqueue_mutex); @@ -79,14 +76,7 @@ int dlm_process_requestqueue(struct dlm_ls *ls) e = list_entry(ls->ls_requestqueue.next, struct rq_entry, list); mutex_unlock(&ls->ls_requestqueue_mutex); - hd = (struct dlm_header *) e->request; - error = dlm_receive_message(hd, e->nodeid, 1); - - if (error == -EINTR) { - /* entry is left on requestqueue */ - log_debug(ls, "process_requestqueue abort eintr"); - break; - } + dlm_receive_message_saved(ls, (struct dlm_message *)e->request); mutex_lock(&ls->ls_requestqueue_mutex); list_del(&e->list); @@ -106,10 +96,12 @@ int dlm_process_requestqueue(struct dlm_ls *ls) /* * After recovery is done, locking is resumed and dlm_recoverd takes all the - * saved requests and processes them as they would have been by dlm_recvd. At - * the same time, dlm_recvd will start receiving new requests from remote - * nodes. We want to delay dlm_recvd processing new requests until - * dlm_recoverd has finished processing the old saved requests. + * saved requests and processes them as they would have been by dlm_recv. At + * the same time, dlm_recv will start receiving new requests from remote nodes. + * We want to delay dlm_recv processing new requests until dlm_recoverd has + * finished processing the old saved requests. We don't check for locking + * stopped here because dlm_ls_stop won't stop locking until it's suspended us + * (dlm_recv). */ void dlm_wait_requestqueue(struct dlm_ls *ls) @@ -118,8 +110,6 @@ void dlm_wait_requestqueue(struct dlm_ls *ls) mutex_lock(&ls->ls_requestqueue_mutex); if (list_empty(&ls->ls_requestqueue)) break; - if (dlm_locking_stopped(ls)) - break; mutex_unlock(&ls->ls_requestqueue_mutex); schedule(); } diff --git a/fs/dlm/requestqueue.h b/fs/dlm/requestqueue.h index 6a53ea03335..aba34fc05ee 100644 --- a/fs/dlm/requestqueue.h +++ b/fs/dlm/requestqueue.h @@ -1,7 +1,7 @@ /****************************************************************************** ******************************************************************************* ** -** Copyright (C) 2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -13,7 +13,7 @@ #ifndef __REQUESTQUEUE_DOT_H__ #define __REQUESTQUEUE_DOT_H__ -int dlm_add_requestqueue(struct dlm_ls *ls, int nodeid, struct dlm_header *hd); +void dlm_add_requestqueue(struct dlm_ls *ls, int nodeid, struct dlm_header *hd); int dlm_process_requestqueue(struct dlm_ls *ls); void dlm_wait_requestqueue(struct dlm_ls *ls); void dlm_purge_requestqueue(struct dlm_ls *ls); diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index cd805a66880..93fa427bb5f 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -93,9 +93,10 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, map_bh(bh, inode->i_sb, block); set_buffer_uptodate(bh); + if (!gfs2_is_jdata(ip)) + mark_buffer_dirty(bh); if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) gfs2_trans_add_bh(ip->i_gl, bh, 0); - mark_buffer_dirty(bh); if (release) { unlock_page(page); @@ -1085,6 +1086,33 @@ static int do_shrink(struct gfs2_inode *ip, u64 size) return error; } +static int do_touch(struct gfs2_inode *ip, u64 size) +{ + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + struct buffer_head *dibh; + int error; + + error = gfs2_trans_begin(sdp, RES_DINODE, 0); + if (error) + return error; + + down_write(&ip->i_rw_mutex); + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + goto do_touch_out; + + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + +do_touch_out: + up_write(&ip->i_rw_mutex); + gfs2_trans_end(sdp); + return error; +} + /** * gfs2_truncatei - make a file a given size * @ip: the inode @@ -1105,8 +1133,11 @@ int gfs2_truncatei(struct gfs2_inode *ip, u64 size) if (size > ip->i_di.di_size) error = do_grow(ip, size); - else + else if (size < ip->i_di.di_size) error = do_shrink(ip, size); + else + /* update time stamps */ + error = do_touch(ip, size); return error; } diff --git a/fs/gfs2/daemon.c b/fs/gfs2/daemon.c index 3548d9f31e0..3731ab0771d 100644 --- a/fs/gfs2/daemon.c +++ b/fs/gfs2/daemon.c @@ -35,30 +35,6 @@ The kthread functions used to start these daemons block and flush signals. */ /** - * gfs2_scand - Look for cached glocks and inodes to toss from memory - * @sdp: Pointer to GFS2 superblock - * - * One of these daemons runs, finding candidates to add to sd_reclaim_list. - * See gfs2_glockd() - */ - -int gfs2_scand(void *data) -{ - struct gfs2_sbd *sdp = data; - unsigned long t; - - while (!kthread_should_stop()) { - gfs2_scand_internal(sdp); - t = gfs2_tune_get(sdp, gt_scand_secs) * HZ; - if (freezing(current)) - refrigerator(); - schedule_timeout_interruptible(t); - } - - return 0; -} - -/** * gfs2_glockd - Reclaim unused glock structures * @sdp: Pointer to GFS2 superblock * diff --git a/fs/gfs2/daemon.h b/fs/gfs2/daemon.h index 801007120fb..0de9b355795 100644 --- a/fs/gfs2/daemon.h +++ b/fs/gfs2/daemon.h @@ -10,7 +10,6 @@ #ifndef __DAEMON_DOT_H__ #define __DAEMON_DOT_H__ -int gfs2_scand(void *data); int gfs2_glockd(void *data); int gfs2_recoverd(void *data); int gfs2_logd(void *data); diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 2beb2f401aa..9949bb746a5 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1043,6 +1043,7 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name) error = gfs2_meta_inode_buffer(dip, &dibh); if (!gfs2_assert_withdraw(GFS2_SB(&dip->i_inode), !error)) { + gfs2_trans_add_bh(dip->i_gl, dibh, 1); dip->i_di.di_blocks++; gfs2_set_inode_blocks(&dip->i_inode); gfs2_dinode_out(dip, dibh->b_data); @@ -1501,7 +1502,7 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name) inode = gfs2_inode_lookup(dir->i_sb, be16_to_cpu(dent->de_type), be64_to_cpu(dent->de_inum.no_addr), - be64_to_cpu(dent->de_inum.no_formal_ino)); + be64_to_cpu(dent->de_inum.no_formal_ino), 0); brelse(bh); return inode; } diff --git a/fs/gfs2/eaops.c b/fs/gfs2/eaops.c index 1ab3e9d7388..aa8dbf303f6 100644 --- a/fs/gfs2/eaops.c +++ b/fs/gfs2/eaops.c @@ -200,28 +200,28 @@ static int security_eo_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er) return gfs2_ea_remove_i(ip, er); } -static struct gfs2_eattr_operations gfs2_user_eaops = { +static const struct gfs2_eattr_operations gfs2_user_eaops = { .eo_get = user_eo_get, .eo_set = user_eo_set, .eo_remove = user_eo_remove, .eo_name = "user", }; -struct gfs2_eattr_operations gfs2_system_eaops = { +const struct gfs2_eattr_operations gfs2_system_eaops = { .eo_get = system_eo_get, .eo_set = system_eo_set, .eo_remove = system_eo_remove, .eo_name = "system", }; -static struct gfs2_eattr_operations gfs2_security_eaops = { +static const struct gfs2_eattr_operations gfs2_security_eaops = { .eo_get = security_eo_get, .eo_set = security_eo_set, .eo_remove = security_eo_remove, .eo_name = "security", }; -struct gfs2_eattr_operations *gfs2_ea_ops[] = { +const struct gfs2_eattr_operations *gfs2_ea_ops[] = { NULL, &gfs2_user_eaops, &gfs2_system_eaops, diff --git a/fs/gfs2/eaops.h b/fs/gfs2/eaops.h index 508b4f7a244..da2f7fbbb40 100644 --- a/fs/gfs2/eaops.h +++ b/fs/gfs2/eaops.h @@ -22,9 +22,9 @@ struct gfs2_eattr_operations { unsigned int gfs2_ea_name2type(const char *name, const char **truncated_name); -extern struct gfs2_eattr_operations gfs2_system_eaops; +extern const struct gfs2_eattr_operations gfs2_system_eaops; -extern struct gfs2_eattr_operations *gfs2_ea_ops[]; +extern const struct gfs2_eattr_operations *gfs2_ea_ops[]; #endif /* __EAOPS_DOT_H__ */ diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 3f0974e1afe..a37efe4aae6 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -25,8 +25,10 @@ #include <asm/uaccess.h> #include <linux/seq_file.h> #include <linux/debugfs.h> -#include <linux/module.h> -#include <linux/kallsyms.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/workqueue.h> +#include <linux/jiffies.h> #include "gfs2.h" #include "incore.h" @@ -48,7 +50,6 @@ struct glock_iter { int hash; /* hash bucket index */ struct gfs2_sbd *sdp; /* incore superblock */ struct gfs2_glock *gl; /* current glock struct */ - struct hlist_head *hb_list; /* current hash bucket ptr */ struct seq_file *seq; /* sequence file for debugfs */ char string[512]; /* scratch space */ }; @@ -59,8 +60,13 @@ static int gfs2_dump_lockstate(struct gfs2_sbd *sdp); static int dump_glock(struct glock_iter *gi, struct gfs2_glock *gl); static void gfs2_glock_xmote_th(struct gfs2_glock *gl, struct gfs2_holder *gh); static void gfs2_glock_drop_th(struct gfs2_glock *gl); +static void run_queue(struct gfs2_glock *gl); + static DECLARE_RWSEM(gfs2_umount_flush_sem); static struct dentry *gfs2_root; +static struct task_struct *scand_process; +static unsigned int scand_secs = 5; +static struct workqueue_struct *glock_workqueue; #define GFS2_GL_HASH_SHIFT 15 #define GFS2_GL_HASH_SIZE (1 << GFS2_GL_HASH_SHIFT) @@ -276,6 +282,18 @@ static struct gfs2_glock *gfs2_glock_find(const struct gfs2_sbd *sdp, return gl; } +static void glock_work_func(struct work_struct *work) +{ + struct gfs2_glock *gl = container_of(work, struct gfs2_glock, gl_work.work); + + spin_lock(&gl->gl_spin); + if (test_and_clear_bit(GLF_PENDING_DEMOTE, &gl->gl_flags)) + set_bit(GLF_DEMOTE, &gl->gl_flags); + run_queue(gl); + spin_unlock(&gl->gl_spin); + gfs2_glock_put(gl); +} + /** * gfs2_glock_get() - Get a glock, or create one if one doesn't exist * @sdp: The GFS2 superblock @@ -315,6 +333,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, gl->gl_name = name; atomic_set(&gl->gl_ref, 1); gl->gl_state = LM_ST_UNLOCKED; + gl->gl_demote_state = LM_ST_EXCLUSIVE; gl->gl_hash = hash; gl->gl_owner_pid = 0; gl->gl_ip = 0; @@ -323,10 +342,12 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, gl->gl_req_bh = NULL; gl->gl_vn = 0; gl->gl_stamp = jiffies; + gl->gl_tchange = jiffies; gl->gl_object = NULL; gl->gl_sbd = sdp; gl->gl_aspace = NULL; lops_init_le(&gl->gl_le, &gfs2_glock_lops); + INIT_DELAYED_WORK(&gl->gl_work, glock_work_func); /* If this glock protects actual on-disk data or metadata blocks, create a VFS inode to manage the pages/buffers holding them. */ @@ -440,6 +461,8 @@ static void wait_on_holder(struct gfs2_holder *gh) static void gfs2_demote_wake(struct gfs2_glock *gl) { + BUG_ON(!spin_is_locked(&gl->gl_spin)); + gl->gl_demote_state = LM_ST_EXCLUSIVE; clear_bit(GLF_DEMOTE, &gl->gl_flags); smp_mb__after_clear_bit(); wake_up_bit(&gl->gl_flags, GLF_DEMOTE); @@ -545,12 +568,14 @@ static int rq_demote(struct gfs2_glock *gl) return 0; } set_bit(GLF_LOCK, &gl->gl_flags); - spin_unlock(&gl->gl_spin); if (gl->gl_demote_state == LM_ST_UNLOCKED || - gl->gl_state != LM_ST_EXCLUSIVE) + gl->gl_state != LM_ST_EXCLUSIVE) { + spin_unlock(&gl->gl_spin); gfs2_glock_drop_th(gl); - else + } else { + spin_unlock(&gl->gl_spin); gfs2_glock_xmote_th(gl, NULL); + } spin_lock(&gl->gl_spin); return 0; @@ -679,24 +704,25 @@ static void gfs2_glmutex_unlock(struct gfs2_glock *gl) * practise: LM_ST_SHARED and LM_ST_UNLOCKED */ -static void handle_callback(struct gfs2_glock *gl, unsigned int state, int remote) +static void handle_callback(struct gfs2_glock *gl, unsigned int state, + int remote, unsigned long delay) { + int bit = delay ? GLF_PENDING_DEMOTE : GLF_DEMOTE; + spin_lock(&gl->gl_spin); - if (test_and_set_bit(GLF_DEMOTE, &gl->gl_flags) == 0) { + set_bit(bit, &gl->gl_flags); + if (gl->gl_demote_state == LM_ST_EXCLUSIVE) { gl->gl_demote_state = state; gl->gl_demote_time = jiffies; if (remote && gl->gl_ops->go_type == LM_TYPE_IOPEN && gl->gl_object) { - struct inode *inode = igrab(gl->gl_object); + gfs2_glock_schedule_for_reclaim(gl); spin_unlock(&gl->gl_spin); - if (inode) { - d_prune_aliases(inode); - iput(inode); - } return; } - } else if (gl->gl_demote_state != LM_ST_UNLOCKED) { - gl->gl_demote_state = state; + } else if (gl->gl_demote_state != LM_ST_UNLOCKED && + gl->gl_demote_state != state) { + gl->gl_demote_state = LM_ST_UNLOCKED; } spin_unlock(&gl->gl_spin); } @@ -723,6 +749,7 @@ static void state_change(struct gfs2_glock *gl, unsigned int new_state) } gl->gl_state = new_state; + gl->gl_tchange = jiffies; } /** @@ -760,10 +787,20 @@ static void xmote_bh(struct gfs2_glock *gl, unsigned int ret) if (!gh) { gl->gl_stamp = jiffies; - if (ret & LM_OUT_CANCELED) + if (ret & LM_OUT_CANCELED) { op_done = 0; - else + } else { + spin_lock(&gl->gl_spin); + if (gl->gl_state != gl->gl_demote_state) { + gl->gl_req_bh = NULL; + spin_unlock(&gl->gl_spin); + gfs2_glock_drop_th(gl); + gfs2_glock_put(gl); + return; + } gfs2_demote_wake(gl); + spin_unlock(&gl->gl_spin); + } } else { spin_lock(&gl->gl_spin); list_del_init(&gh->gh_list); @@ -799,7 +836,6 @@ out: gl->gl_req_gh = NULL; gl->gl_req_bh = NULL; clear_bit(GLF_LOCK, &gl->gl_flags); - run_queue(gl); spin_unlock(&gl->gl_spin); } @@ -817,7 +853,7 @@ out: * */ -void gfs2_glock_xmote_th(struct gfs2_glock *gl, struct gfs2_holder *gh) +static void gfs2_glock_xmote_th(struct gfs2_glock *gl, struct gfs2_holder *gh) { struct gfs2_sbd *sdp = gl->gl_sbd; int flags = gh ? gh->gh_flags : 0; @@ -871,7 +907,6 @@ static void drop_bh(struct gfs2_glock *gl, unsigned int ret) gfs2_assert_warn(sdp, !ret); state_change(gl, LM_ST_UNLOCKED); - gfs2_demote_wake(gl); if (glops->go_inval) glops->go_inval(gl, DIO_METADATA); @@ -884,10 +919,10 @@ static void drop_bh(struct gfs2_glock *gl, unsigned int ret) } spin_lock(&gl->gl_spin); + gfs2_demote_wake(gl); gl->gl_req_gh = NULL; gl->gl_req_bh = NULL; clear_bit(GLF_LOCK, &gl->gl_flags); - run_queue(gl); spin_unlock(&gl->gl_spin); gfs2_glock_put(gl); @@ -1067,24 +1102,31 @@ static void add_to_queue(struct gfs2_holder *gh) if (test_and_set_bit(HIF_WAIT, &gh->gh_iflags)) BUG(); - existing = find_holder_by_owner(&gl->gl_holders, gh->gh_owner_pid); - if (existing) { - print_symbol(KERN_WARNING "original: %s\n", existing->gh_ip); - printk(KERN_INFO "pid : %d\n", existing->gh_owner_pid); - printk(KERN_INFO "lock type : %d lock state : %d\n", - existing->gh_gl->gl_name.ln_type, existing->gh_gl->gl_state); - print_symbol(KERN_WARNING "new: %s\n", gh->gh_ip); - printk(KERN_INFO "pid : %d\n", gh->gh_owner_pid); - printk(KERN_INFO "lock type : %d lock state : %d\n", - gl->gl_name.ln_type, gl->gl_state); - BUG(); - } - - existing = find_holder_by_owner(&gl->gl_waiters3, gh->gh_owner_pid); - if (existing) { - print_symbol(KERN_WARNING "original: %s\n", existing->gh_ip); - print_symbol(KERN_WARNING "new: %s\n", gh->gh_ip); - BUG(); + if (!(gh->gh_flags & GL_FLOCK)) { + existing = find_holder_by_owner(&gl->gl_holders, + gh->gh_owner_pid); + if (existing) { + print_symbol(KERN_WARNING "original: %s\n", + existing->gh_ip); + printk(KERN_INFO "pid : %d\n", existing->gh_owner_pid); + printk(KERN_INFO "lock type : %d lock state : %d\n", + existing->gh_gl->gl_name.ln_type, + existing->gh_gl->gl_state); + print_symbol(KERN_WARNING "new: %s\n", gh->gh_ip); + printk(KERN_INFO "pid : %d\n", gh->gh_owner_pid); + printk(KERN_INFO "lock type : %d lock state : %d\n", + gl->gl_name.ln_type, gl->gl_state); + BUG(); + } + + existing = find_holder_by_owner(&gl->gl_waiters3, + gh->gh_owner_pid); + if (existing) { + print_symbol(KERN_WARNING "original: %s\n", + existing->gh_ip); + print_symbol(KERN_WARNING "new: %s\n", gh->gh_ip); + BUG(); + } } if (gh->gh_flags & LM_FLAG_PRIORITY) @@ -1195,9 +1237,10 @@ void gfs2_glock_dq(struct gfs2_holder *gh) { struct gfs2_glock *gl = gh->gh_gl; const struct gfs2_glock_operations *glops = gl->gl_ops; + unsigned delay = 0; if (gh->gh_flags & GL_NOCACHE) - handle_callback(gl, LM_ST_UNLOCKED, 0); + handle_callback(gl, LM_ST_UNLOCKED, 0, 0); gfs2_glmutex_lock(gl); @@ -1215,8 +1258,14 @@ void gfs2_glock_dq(struct gfs2_holder *gh) } clear_bit(GLF_LOCK, &gl->gl_flags); - run_queue(gl); spin_unlock(&gl->gl_spin); + + gfs2_glock_hold(gl); + if (test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) && + !test_bit(GLF_DEMOTE, &gl->gl_flags)) + delay = gl->gl_ops->go_min_hold_time; + if (queue_delayed_work(glock_workqueue, &gl->gl_work, delay) == 0) + gfs2_glock_put(gl); } void gfs2_glock_dq_wait(struct gfs2_holder *gh) @@ -1443,18 +1492,21 @@ static void blocking_cb(struct gfs2_sbd *sdp, struct lm_lockname *name, unsigned int state) { struct gfs2_glock *gl; + unsigned long delay = 0; + unsigned long holdtime; + unsigned long now = jiffies; gl = gfs2_glock_find(sdp, name); if (!gl) return; - handle_callback(gl, state, 1); - - spin_lock(&gl->gl_spin); - run_queue(gl); - spin_unlock(&gl->gl_spin); + holdtime = gl->gl_tchange + gl->gl_ops->go_min_hold_time; + if (time_before(now, holdtime)) + delay = holdtime - now; - gfs2_glock_put(gl); + handle_callback(gl, state, 1, delay); + if (queue_delayed_work(glock_workqueue, &gl->gl_work, delay) == 0) + gfs2_glock_put(gl); } /** @@ -1495,7 +1547,8 @@ void gfs2_glock_cb(void *cb_data, unsigned int type, void *data) return; if (!gfs2_assert_warn(sdp, gl->gl_req_bh)) gl->gl_req_bh(gl, async->lc_ret); - gfs2_glock_put(gl); + if (queue_delayed_work(glock_workqueue, &gl->gl_work, 0) == 0) + gfs2_glock_put(gl); up_read(&gfs2_umount_flush_sem); return; } @@ -1588,7 +1641,7 @@ void gfs2_reclaim_glock(struct gfs2_sbd *sdp) if (gfs2_glmutex_trylock(gl)) { if (list_empty(&gl->gl_holders) && gl->gl_state != LM_ST_UNLOCKED && demote_ok(gl)) - handle_callback(gl, LM_ST_UNLOCKED, 0); + handle_callback(gl, LM_ST_UNLOCKED, 0, 0); gfs2_glmutex_unlock(gl); } @@ -1617,7 +1670,7 @@ static int examine_bucket(glock_examiner examiner, struct gfs2_sbd *sdp, goto out; gl = list_entry(head->first, struct gfs2_glock, gl_list); while(1) { - if (gl->gl_sbd == sdp) { + if (!sdp || gl->gl_sbd == sdp) { gfs2_glock_hold(gl); read_unlock(gl_lock_addr(hash)); if (prev) @@ -1635,6 +1688,7 @@ out: read_unlock(gl_lock_addr(hash)); if (prev) gfs2_glock_put(prev); + cond_resched(); return has_entries; } @@ -1663,20 +1717,6 @@ out_schedule: } /** - * gfs2_scand_internal - Look for glocks and inodes to toss from memory - * @sdp: the filesystem - * - */ - -void gfs2_scand_internal(struct gfs2_sbd *sdp) -{ - unsigned int x; - - for (x = 0; x < GFS2_GL_HASH_SIZE; x++) - examine_bucket(scan_glock, sdp, x); -} - -/** * clear_glock - look at a glock and see if we can free it from glock cache * @gl: the glock to look at * @@ -1701,7 +1741,7 @@ static void clear_glock(struct gfs2_glock *gl) if (gfs2_glmutex_trylock(gl)) { if (list_empty(&gl->gl_holders) && gl->gl_state != LM_ST_UNLOCKED) - handle_callback(gl, LM_ST_UNLOCKED, 0); + handle_callback(gl, LM_ST_UNLOCKED, 0, 0); gfs2_glmutex_unlock(gl); } } @@ -1843,7 +1883,7 @@ static int dump_glock(struct glock_iter *gi, struct gfs2_glock *gl) spin_lock(&gl->gl_spin); - print_dbg(gi, "Glock 0x%p (%u, %llu)\n", gl, gl->gl_name.ln_type, + print_dbg(gi, "Glock 0x%p (%u, 0x%llx)\n", gl, gl->gl_name.ln_type, (unsigned long long)gl->gl_name.ln_number); print_dbg(gi, " gl_flags ="); for (x = 0; x < 32; x++) { @@ -1963,6 +2003,35 @@ static int gfs2_dump_lockstate(struct gfs2_sbd *sdp) return error; } +/** + * gfs2_scand - Look for cached glocks and inodes to toss from memory + * @sdp: Pointer to GFS2 superblock + * + * One of these daemons runs, finding candidates to add to sd_reclaim_list. + * See gfs2_glockd() + */ + +static int gfs2_scand(void *data) +{ + unsigned x; + unsigned delay; + + while (!kthread_should_stop()) { + for (x = 0; x < GFS2_GL_HASH_SIZE; x++) + examine_bucket(scan_glock, NULL, x); + if (freezing(current)) + refrigerator(); + delay = scand_secs; + if (delay < 1) + delay = 1; + schedule_timeout_interruptible(delay * HZ); + } + + return 0; +} + + + int __init gfs2_glock_init(void) { unsigned i; @@ -1974,52 +2043,69 @@ int __init gfs2_glock_init(void) rwlock_init(&gl_hash_locks[i]); } #endif + + scand_process = kthread_run(gfs2_scand, NULL, "gfs2_scand"); + if (IS_ERR(scand_process)) + return PTR_ERR(scand_process); + + glock_workqueue = create_workqueue("glock_workqueue"); + if (IS_ERR(glock_workqueue)) { + kthread_stop(scand_process); + return PTR_ERR(glock_workqueue); + } + return 0; } +void gfs2_glock_exit(void) +{ + destroy_workqueue(glock_workqueue); + kthread_stop(scand_process); +} + +module_param(scand_secs, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(scand_secs, "The number of seconds between scand runs"); + static int gfs2_glock_iter_next(struct glock_iter *gi) { + struct gfs2_glock *gl; + +restart: read_lock(gl_lock_addr(gi->hash)); - while (1) { - if (!gi->hb_list) { /* If we don't have a hash bucket yet */ - gi->hb_list = &gl_hash_table[gi->hash].hb_list; - if (hlist_empty(gi->hb_list)) { - read_unlock(gl_lock_addr(gi->hash)); - gi->hash++; - read_lock(gl_lock_addr(gi->hash)); - gi->hb_list = NULL; - if (gi->hash >= GFS2_GL_HASH_SIZE) { - read_unlock(gl_lock_addr(gi->hash)); - return 1; - } - else - continue; - } - if (!hlist_empty(gi->hb_list)) { - gi->gl = list_entry(gi->hb_list->first, - struct gfs2_glock, - gl_list); - } - } else { - if (gi->gl->gl_list.next == NULL) { - read_unlock(gl_lock_addr(gi->hash)); - gi->hash++; - read_lock(gl_lock_addr(gi->hash)); - gi->hb_list = NULL; - continue; - } - gi->gl = list_entry(gi->gl->gl_list.next, - struct gfs2_glock, gl_list); - } + gl = gi->gl; + if (gl) { + gi->gl = hlist_entry(gl->gl_list.next, + struct gfs2_glock, gl_list); if (gi->gl) - break; + gfs2_glock_hold(gi->gl); } read_unlock(gl_lock_addr(gi->hash)); + if (gl) + gfs2_glock_put(gl); + if (gl && gi->gl == NULL) + gi->hash++; + while(gi->gl == NULL) { + if (gi->hash >= GFS2_GL_HASH_SIZE) + return 1; + read_lock(gl_lock_addr(gi->hash)); + gi->gl = hlist_entry(gl_hash_table[gi->hash].hb_list.first, + struct gfs2_glock, gl_list); + if (gi->gl) + gfs2_glock_hold(gi->gl); + read_unlock(gl_lock_addr(gi->hash)); + gi->hash++; + } + + if (gi->sdp != gi->gl->gl_sbd) + goto restart; + return 0; } static void gfs2_glock_iter_free(struct glock_iter *gi) { + if (gi->gl) + gfs2_glock_put(gi->gl); kfree(gi); } @@ -2033,9 +2119,8 @@ static struct glock_iter *gfs2_glock_iter_init(struct gfs2_sbd *sdp) gi->sdp = sdp; gi->hash = 0; - gi->gl = NULL; - gi->hb_list = NULL; gi->seq = NULL; + gi->gl = NULL; memset(gi->string, 0, sizeof(gi->string)); if (gfs2_glock_iter_next(gi)) { @@ -2055,7 +2140,7 @@ static void *gfs2_glock_seq_start(struct seq_file *file, loff_t *pos) if (!gi) return NULL; - while (n--) { + while(n--) { if (gfs2_glock_iter_next(gi)) { gfs2_glock_iter_free(gi); return NULL; @@ -2082,7 +2167,9 @@ static void *gfs2_glock_seq_next(struct seq_file *file, void *iter_ptr, static void gfs2_glock_seq_stop(struct seq_file *file, void *iter_ptr) { - /* nothing for now */ + struct glock_iter *gi = iter_ptr; + if (gi) + gfs2_glock_iter_free(gi); } static int gfs2_glock_seq_show(struct seq_file *file, void *iter_ptr) @@ -2095,7 +2182,7 @@ static int gfs2_glock_seq_show(struct seq_file *file, void *iter_ptr) return 0; } -static struct seq_operations gfs2_glock_seq_ops = { +static const struct seq_operations gfs2_glock_seq_ops = { .start = gfs2_glock_seq_start, .next = gfs2_glock_seq_next, .stop = gfs2_glock_seq_stop, diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 7721ca3fff9..b16f604eea9 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -26,6 +26,7 @@ #define GL_SKIP 0x00000100 #define GL_ATIME 0x00000200 #define GL_NOCACHE 0x00000400 +#define GL_FLOCK 0x00000800 #define GL_NOCANCEL 0x00001000 #define GLR_TRYFAILED 13 @@ -132,11 +133,11 @@ void gfs2_glock_cb(void *cb_data, unsigned int type, void *data); void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl); void gfs2_reclaim_glock(struct gfs2_sbd *sdp); - -void gfs2_scand_internal(struct gfs2_sbd *sdp); void gfs2_gl_hash_clear(struct gfs2_sbd *sdp, int wait); int __init gfs2_glock_init(void); +void gfs2_glock_exit(void); + int gfs2_create_debugfs_file(struct gfs2_sbd *sdp); void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp); int gfs2_register_debugfs(void); diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 777ca46010e..4670dcb2a87 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -41,7 +41,6 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl) struct list_head *head = &gl->gl_ail_list; struct gfs2_bufdata *bd; struct buffer_head *bh; - u64 blkno; int error; blocks = atomic_read(&gl->gl_ail_count); @@ -57,19 +56,12 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl) bd = list_entry(head->next, struct gfs2_bufdata, bd_ail_gl_list); bh = bd->bd_bh; - blkno = bh->b_blocknr; + gfs2_remove_from_ail(NULL, bd); + bd->bd_bh = NULL; + bh->b_private = NULL; + bd->bd_blkno = bh->b_blocknr; gfs2_assert_withdraw(sdp, !buffer_busy(bh)); - - bd->bd_ail = NULL; - list_del(&bd->bd_ail_st_list); - list_del(&bd->bd_ail_gl_list); - atomic_dec(&gl->gl_ail_count); - brelse(bh); - gfs2_log_unlock(sdp); - - gfs2_trans_add_revoke(sdp, blkno); - - gfs2_log_lock(sdp); + gfs2_trans_add_revoke(sdp, bd); } gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); gfs2_log_unlock(sdp); @@ -156,9 +148,11 @@ static void inode_go_sync(struct gfs2_glock *gl) ip = NULL; if (test_bit(GLF_DIRTY, &gl->gl_flags)) { - if (ip) + if (ip && !gfs2_is_jdata(ip)) filemap_fdatawrite(ip->i_inode.i_mapping); gfs2_log_flush(gl->gl_sbd, gl); + if (ip && gfs2_is_jdata(ip)) + filemap_fdatawrite(ip->i_inode.i_mapping); gfs2_meta_sync(gl); if (ip) { struct address_space *mapping = ip->i_inode.i_mapping; @@ -452,6 +446,7 @@ const struct gfs2_glock_operations gfs2_inode_glops = { .go_lock = inode_go_lock, .go_unlock = inode_go_unlock, .go_type = LM_TYPE_INODE, + .go_min_hold_time = HZ / 10, }; const struct gfs2_glock_operations gfs2_rgrp_glops = { @@ -462,6 +457,7 @@ const struct gfs2_glock_operations gfs2_rgrp_glops = { .go_lock = rgrp_go_lock, .go_unlock = rgrp_go_unlock, .go_type = LM_TYPE_RGRP, + .go_min_hold_time = HZ / 10, }; const struct gfs2_glock_operations gfs2_trans_glops = { diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 170ba93829c..eaddfb5a8e6 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -11,6 +11,7 @@ #define __INCORE_DOT_H__ #include <linux/fs.h> +#include <linux/workqueue.h> #define DIO_WAIT 0x00000010 #define DIO_METADATA 0x00000020 @@ -113,7 +114,13 @@ struct gfs2_bufdata { struct buffer_head *bd_bh; struct gfs2_glock *bd_gl; - struct list_head bd_list_tr; + union { + struct list_head list_tr; + u64 blkno; + } u; +#define bd_list_tr u.list_tr +#define bd_blkno u.blkno + struct gfs2_log_element bd_le; struct gfs2_ail *bd_ail; @@ -130,6 +137,7 @@ struct gfs2_glock_operations { int (*go_lock) (struct gfs2_holder *gh); void (*go_unlock) (struct gfs2_holder *gh); const int go_type; + const unsigned long go_min_hold_time; }; enum { @@ -161,6 +169,7 @@ enum { GLF_LOCK = 1, GLF_STICKY = 2, GLF_DEMOTE = 3, + GLF_PENDING_DEMOTE = 4, GLF_DIRTY = 5, }; @@ -193,6 +202,7 @@ struct gfs2_glock { u64 gl_vn; unsigned long gl_stamp; + unsigned long gl_tchange; void *gl_object; struct list_head gl_reclaim; @@ -203,6 +213,7 @@ struct gfs2_glock { struct gfs2_log_element gl_le; struct list_head gl_ail_list; atomic_t gl_ail_count; + struct delayed_work gl_work; }; struct gfs2_alloc { @@ -293,11 +304,6 @@ struct gfs2_file { struct gfs2_holder f_fl_gh; }; -struct gfs2_revoke { - struct gfs2_log_element rv_le; - u64 rv_blkno; -}; - struct gfs2_revoke_replay { struct list_head rr_list; u64 rr_blkno; @@ -335,12 +341,6 @@ struct gfs2_quota_data { unsigned long qd_last_touched; }; -struct gfs2_log_buf { - struct list_head lb_list; - struct buffer_head *lb_bh; - struct buffer_head *lb_real; -}; - struct gfs2_trans { unsigned long tr_ip; @@ -429,7 +429,6 @@ struct gfs2_tune { unsigned int gt_log_flush_secs; unsigned int gt_jindex_refresh_secs; /* Check for new journal index */ - unsigned int gt_scand_secs; unsigned int gt_recoverd_secs; unsigned int gt_logd_secs; unsigned int gt_quotad_secs; @@ -574,7 +573,6 @@ struct gfs2_sbd { /* Daemon stuff */ - struct task_struct *sd_scand_process; struct task_struct *sd_recoverd_process; struct task_struct *sd_logd_process; struct task_struct *sd_quotad_process; @@ -609,13 +607,13 @@ struct gfs2_sbd { unsigned int sd_log_num_revoke; unsigned int sd_log_num_rg; unsigned int sd_log_num_databuf; - unsigned int sd_log_num_jdata; struct list_head sd_log_le_gl; struct list_head sd_log_le_buf; struct list_head sd_log_le_revoke; struct list_head sd_log_le_rg; struct list_head sd_log_le_databuf; + struct list_head sd_log_le_ordered; unsigned int sd_log_blks_free; struct mutex sd_log_reserve_mutex; @@ -627,7 +625,8 @@ struct gfs2_sbd { unsigned long sd_log_flush_time; struct rw_semaphore sd_log_flush_lock; - struct list_head sd_log_flush_list; + atomic_t sd_log_in_flight; + wait_queue_head_t sd_log_flush_wait; unsigned int sd_log_flush_head; u64 sd_log_flush_wrapped; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 34f7bcdea1e..5f6dc32946c 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -77,6 +77,49 @@ static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr) return iget5_locked(sb, hash, iget_test, iget_set, &no_addr); } +struct gfs2_skip_data { + u64 no_addr; + int skipped; +}; + +static int iget_skip_test(struct inode *inode, void *opaque) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_skip_data *data = opaque; + + if (ip->i_no_addr == data->no_addr && inode->i_private != NULL){ + if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)){ + data->skipped = 1; + return 0; + } + return 1; + } + return 0; +} + +static int iget_skip_set(struct inode *inode, void *opaque) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_skip_data *data = opaque; + + if (data->skipped) + return 1; + inode->i_ino = (unsigned long)(data->no_addr); + ip->i_no_addr = data->no_addr; + return 0; +} + +static struct inode *gfs2_iget_skip(struct super_block *sb, + u64 no_addr) +{ + struct gfs2_skip_data data; + unsigned long hash = (unsigned long)no_addr; + + data.no_addr = no_addr; + data.skipped = 0; + return iget5_locked(sb, hash, iget_skip_test, iget_skip_set, &data); +} + /** * GFS2 lookup code fills in vfs inode contents based on info obtained * from directory entry inside gfs2_inode_lookup(). This has caused issues @@ -112,6 +155,7 @@ void gfs2_set_iop(struct inode *inode) * @sb: The super block * @no_addr: The inode number * @type: The type of the inode + * @skip_freeing: set this not return an inode if it is currently being freed. * * Returns: A VFS inode, or an error */ @@ -119,13 +163,19 @@ void gfs2_set_iop(struct inode *inode) struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, u64 no_addr, - u64 no_formal_ino) + u64 no_formal_ino, int skip_freeing) { - struct inode *inode = gfs2_iget(sb, no_addr); - struct gfs2_inode *ip = GFS2_I(inode); + struct inode *inode; + struct gfs2_inode *ip; struct gfs2_glock *io_gl; int error; + if (skip_freeing) + inode = gfs2_iget_skip(sb, no_addr); + else + inode = gfs2_iget(sb, no_addr); + ip = GFS2_I(inode); + if (!inode) return ERR_PTR(-ENOBUFS); @@ -244,6 +294,11 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) return 0; } +static void gfs2_inode_bh(struct gfs2_inode *ip, struct buffer_head *bh) +{ + ip->i_cache[0] = bh; +} + /** * gfs2_inode_refresh - Refresh the incore copy of the dinode * @ip: The GFS2 inode @@ -688,7 +743,7 @@ out: static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, const struct gfs2_inum_host *inum, unsigned int mode, unsigned int uid, unsigned int gid, - const u64 *generation, dev_t dev) + const u64 *generation, dev_t dev, struct buffer_head **bhp) { struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_dinode *di; @@ -743,13 +798,15 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, di->di_mtime_nsec = cpu_to_be32(tv.tv_nsec); di->di_ctime_nsec = cpu_to_be32(tv.tv_nsec); memset(&di->di_reserved, 0, sizeof(di->di_reserved)); + + set_buffer_uptodate(dibh); - brelse(dibh); + *bhp = dibh; } static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, unsigned int mode, const struct gfs2_inum_host *inum, - const u64 *generation, dev_t dev) + const u64 *generation, dev_t dev, struct buffer_head **bhp) { struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); unsigned int uid, gid; @@ -770,7 +827,7 @@ static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, if (error) goto out_quota; - init_dinode(dip, gl, inum, mode, uid, gid, generation, dev); + init_dinode(dip, gl, inum, mode, uid, gid, generation, dev, bhp); gfs2_quota_change(dip, +1, uid, gid); gfs2_trans_end(sdp); @@ -909,6 +966,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; int error; u64 generation; + struct buffer_head *bh=NULL; if (!name->len || name->len > GFS2_FNAMESIZE) return ERR_PTR(-ENAMETOOLONG); @@ -935,16 +993,18 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, if (error) goto fail_gunlock; - error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev); + error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, &bh); if (error) goto fail_gunlock2; inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, - inum.no_formal_ino); + inum.no_formal_ino, 0); if (IS_ERR(inode)) goto fail_gunlock2; + gfs2_inode_bh(GFS2_I(inode), bh); + error = gfs2_inode_refresh(GFS2_I(inode)); if (error) goto fail_gunlock2; diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index 4517ac82c01..351ac87ab38 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -49,7 +49,8 @@ static inline void gfs2_inum_out(const struct gfs2_inode *ip, void gfs2_inode_attr_in(struct gfs2_inode *ip); void gfs2_set_iop(struct inode *inode); struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, - u64 no_addr, u64 no_formal_ino); + u64 no_addr, u64 no_formal_ino, + int skip_freeing); struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr); int gfs2_inode_refresh(struct gfs2_inode *ip); diff --git a/fs/gfs2/locking/dlm/lock_dlm.h b/fs/gfs2/locking/dlm/lock_dlm.h index 24d70f73b65..9e8265d2837 100644 --- a/fs/gfs2/locking/dlm/lock_dlm.h +++ b/fs/gfs2/locking/dlm/lock_dlm.h @@ -13,7 +13,6 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/module.h> #include <linux/types.h> #include <linux/string.h> #include <linux/list.h> diff --git a/fs/gfs2/locking/dlm/plock.c b/fs/gfs2/locking/dlm/plock.c index fba1f1d87e4..1f7b038530b 100644 --- a/fs/gfs2/locking/dlm/plock.c +++ b/fs/gfs2/locking/dlm/plock.c @@ -346,15 +346,16 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count, static unsigned int dev_poll(struct file *file, poll_table *wait) { + unsigned int mask = 0; + poll_wait(file, &send_wq, wait); spin_lock(&ops_lock); - if (!list_empty(&send_list)) { - spin_unlock(&ops_lock); - return POLLIN | POLLRDNORM; - } + if (!list_empty(&send_list)) + mask = POLLIN | POLLRDNORM; spin_unlock(&ops_lock); - return 0; + + return mask; } static const struct file_operations dev_fops = { diff --git a/fs/gfs2/locking/dlm/thread.c b/fs/gfs2/locking/dlm/thread.c index 1aca51e4509..bd938f06481 100644 --- a/fs/gfs2/locking/dlm/thread.c +++ b/fs/gfs2/locking/dlm/thread.c @@ -268,20 +268,16 @@ static inline int check_drop(struct gdlm_ls *ls) return 0; } -static int gdlm_thread(void *data) +static int gdlm_thread(void *data, int blist) { struct gdlm_ls *ls = (struct gdlm_ls *) data; struct gdlm_lock *lp = NULL; - int blist = 0; uint8_t complete, blocking, submit, drop; DECLARE_WAITQUEUE(wait, current); /* Only thread1 is allowed to do blocking callbacks since gfs may wait for a completion callback within a blocking cb. */ - if (current == ls->thread1) - blist = 1; - while (!kthread_should_stop()) { set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&ls->thread_wait, &wait); @@ -333,12 +329,22 @@ static int gdlm_thread(void *data) return 0; } +static int gdlm_thread1(void *data) +{ + return gdlm_thread(data, 1); +} + +static int gdlm_thread2(void *data) +{ + return gdlm_thread(data, 0); +} + int gdlm_init_threads(struct gdlm_ls *ls) { struct task_struct *p; int error; - p = kthread_run(gdlm_thread, ls, "lock_dlm1"); + p = kthread_run(gdlm_thread1, ls, "lock_dlm1"); error = IS_ERR(p); if (error) { log_error("can't start lock_dlm1 thread %d", error); @@ -346,7 +352,7 @@ int gdlm_init_threads(struct gdlm_ls *ls) } ls->thread1 = p; - p = kthread_run(gdlm_thread, ls, "lock_dlm2"); + p = kthread_run(gdlm_thread2, ls, "lock_dlm2"); error = IS_ERR(p); if (error) { log_error("can't start lock_dlm2 thread %d", error); diff --git a/fs/gfs2/locking/nolock/main.c b/fs/gfs2/locking/nolock/main.c index 0d149c8c493..d3b8ce6fbbe 100644 --- a/fs/gfs2/locking/nolock/main.c +++ b/fs/gfs2/locking/nolock/main.c @@ -9,7 +9,6 @@ #include <linux/module.h> #include <linux/slab.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/types.h> #include <linux/fs.h> diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index f49a12e2408..7df70247325 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -60,6 +60,26 @@ unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, } /** + * gfs2_remove_from_ail - Remove an entry from the ail lists, updating counters + * @mapping: The associated mapping (maybe NULL) + * @bd: The gfs2_bufdata to remove + * + * The log lock _must_ be held when calling this function + * + */ + +void gfs2_remove_from_ail(struct address_space *mapping, struct gfs2_bufdata *bd) +{ + bd->bd_ail = NULL; + list_del_init(&bd->bd_ail_st_list); + list_del_init(&bd->bd_ail_gl_list); + atomic_dec(&bd->bd_gl->gl_ail_count); + if (mapping) + gfs2_meta_cache_flush(GFS2_I(mapping->host)); + brelse(bd->bd_bh); +} + +/** * gfs2_ail1_start_one - Start I/O on a part of the AIL * @sdp: the filesystem * @tr: the part of the AIL @@ -83,17 +103,9 @@ static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) gfs2_assert(sdp, bd->bd_ail == ai); - if (!bh){ - list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); - continue; - } - if (!buffer_busy(bh)) { - if (!buffer_uptodate(bh)) { - gfs2_log_unlock(sdp); + if (!buffer_uptodate(bh)) gfs2_io_error_bh(sdp, bh); - gfs2_log_lock(sdp); - } list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); continue; } @@ -103,9 +115,16 @@ static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list); + get_bh(bh); gfs2_log_unlock(sdp); - wait_on_buffer(bh); - ll_rw_block(WRITE, 1, &bh); + lock_buffer(bh); + if (test_clear_buffer_dirty(bh)) { + bh->b_end_io = end_buffer_write_sync; + submit_bh(WRITE, bh); + } else { + unlock_buffer(bh); + brelse(bh); + } gfs2_log_lock(sdp); retry = 1; @@ -130,11 +149,6 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl bd_ail_st_list) { bh = bd->bd_bh; - if (!bh){ - list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); - continue; - } - gfs2_assert(sdp, bd->bd_ail == ai); if (buffer_busy(bh)) { @@ -155,13 +169,14 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl static void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags) { - struct list_head *head = &sdp->sd_ail1_list; + struct list_head *head; u64 sync_gen; struct list_head *first; struct gfs2_ail *first_ai, *ai, *tmp; int done = 0; gfs2_log_lock(sdp); + head = &sdp->sd_ail1_list; if (list_empty(head)) { gfs2_log_unlock(sdp); return; @@ -233,11 +248,7 @@ static void gfs2_ail2_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) bd = list_entry(head->prev, struct gfs2_bufdata, bd_ail_st_list); gfs2_assert(sdp, bd->bd_ail == ai); - bd->bd_ail = NULL; - list_del(&bd->bd_ail_st_list); - list_del(&bd->bd_ail_gl_list); - atomic_dec(&bd->bd_gl->gl_ail_count); - brelse(bd->bd_bh); + gfs2_remove_from_ail(bd->bd_bh->b_page->mapping, bd); } } @@ -439,10 +450,10 @@ static unsigned int current_tail(struct gfs2_sbd *sdp) return tail; } -static inline void log_incr_head(struct gfs2_sbd *sdp) +void gfs2_log_incr_head(struct gfs2_sbd *sdp) { if (sdp->sd_log_flush_head == sdp->sd_log_tail) - gfs2_assert_withdraw(sdp, sdp->sd_log_flush_head == sdp->sd_log_head); + BUG_ON(sdp->sd_log_flush_head != sdp->sd_log_head); if (++sdp->sd_log_flush_head == sdp->sd_jdesc->jd_blocks) { sdp->sd_log_flush_head = 0; @@ -451,6 +462,23 @@ static inline void log_incr_head(struct gfs2_sbd *sdp) } /** + * gfs2_log_write_endio - End of I/O for a log buffer + * @bh: The buffer head + * @uptodate: I/O Status + * + */ + +static void gfs2_log_write_endio(struct buffer_head *bh, int uptodate) +{ + struct gfs2_sbd *sdp = bh->b_private; + bh->b_private = NULL; + + end_buffer_write_sync(bh, uptodate); + if (atomic_dec_and_test(&sdp->sd_log_in_flight)) + wake_up(&sdp->sd_log_flush_wait); +} + +/** * gfs2_log_get_buf - Get and initialize a buffer to use for log control data * @sdp: The GFS2 superblock * @@ -460,25 +488,43 @@ static inline void log_incr_head(struct gfs2_sbd *sdp) struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp) { u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); - struct gfs2_log_buf *lb; struct buffer_head *bh; - lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_NOFS | __GFP_NOFAIL); - list_add(&lb->lb_list, &sdp->sd_log_flush_list); - - bh = lb->lb_bh = sb_getblk(sdp->sd_vfs, blkno); + bh = sb_getblk(sdp->sd_vfs, blkno); lock_buffer(bh); memset(bh->b_data, 0, bh->b_size); set_buffer_uptodate(bh); clear_buffer_dirty(bh); - unlock_buffer(bh); - - log_incr_head(sdp); + gfs2_log_incr_head(sdp); + atomic_inc(&sdp->sd_log_in_flight); + bh->b_private = sdp; + bh->b_end_io = gfs2_log_write_endio; return bh; } /** + * gfs2_fake_write_endio - + * @bh: The buffer head + * @uptodate: The I/O Status + * + */ + +static void gfs2_fake_write_endio(struct buffer_head *bh, int uptodate) +{ + struct buffer_head *real_bh = bh->b_private; + struct gfs2_bufdata *bd = real_bh->b_private; + struct gfs2_sbd *sdp = bd->bd_gl->gl_sbd; + + end_buffer_write_sync(bh, uptodate); + free_buffer_head(bh); + unlock_buffer(real_bh); + brelse(real_bh); + if (atomic_dec_and_test(&sdp->sd_log_in_flight)) + wake_up(&sdp->sd_log_flush_wait); +} + +/** * gfs2_log_fake_buf - Build a fake buffer head to write metadata buffer to log * @sdp: the filesystem * @data: the data the buffer_head should point to @@ -490,22 +536,20 @@ struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp, struct buffer_head *real) { u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); - struct gfs2_log_buf *lb; struct buffer_head *bh; - lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_NOFS | __GFP_NOFAIL); - list_add(&lb->lb_list, &sdp->sd_log_flush_list); - lb->lb_real = real; - - bh = lb->lb_bh = alloc_buffer_head(GFP_NOFS | __GFP_NOFAIL); + bh = alloc_buffer_head(GFP_NOFS | __GFP_NOFAIL); atomic_set(&bh->b_count, 1); - bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate); + bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate) | (1 << BH_Lock); set_bh_page(bh, real->b_page, bh_offset(real)); bh->b_blocknr = blkno; bh->b_size = sdp->sd_sb.sb_bsize; bh->b_bdev = sdp->sd_vfs->s_bdev; + bh->b_private = real; + bh->b_end_io = gfs2_fake_write_endio; - log_incr_head(sdp); + gfs2_log_incr_head(sdp); + atomic_inc(&sdp->sd_log_in_flight); return bh; } @@ -572,45 +616,75 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) gfs2_assert_withdraw(sdp, !pull); sdp->sd_log_idle = (tail == sdp->sd_log_flush_head); - log_incr_head(sdp); + gfs2_log_incr_head(sdp); } static void log_flush_commit(struct gfs2_sbd *sdp) { - struct list_head *head = &sdp->sd_log_flush_list; - struct gfs2_log_buf *lb; - struct buffer_head *bh; - int flushcount = 0; + DEFINE_WAIT(wait); + + if (atomic_read(&sdp->sd_log_in_flight)) { + do { + prepare_to_wait(&sdp->sd_log_flush_wait, &wait, + TASK_UNINTERRUPTIBLE); + if (atomic_read(&sdp->sd_log_in_flight)) + io_schedule(); + } while(atomic_read(&sdp->sd_log_in_flight)); + finish_wait(&sdp->sd_log_flush_wait, &wait); + } - while (!list_empty(head)) { - lb = list_entry(head->next, struct gfs2_log_buf, lb_list); - list_del(&lb->lb_list); - bh = lb->lb_bh; + log_write_header(sdp, 0, 0); +} - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - gfs2_io_error_bh(sdp, bh); - if (lb->lb_real) { - while (atomic_read(&bh->b_count) != 1) /* Grrrr... */ - schedule(); - free_buffer_head(bh); - } else +static void gfs2_ordered_write(struct gfs2_sbd *sdp) +{ + struct gfs2_bufdata *bd; + struct buffer_head *bh; + LIST_HEAD(written); + + gfs2_log_lock(sdp); + while (!list_empty(&sdp->sd_log_le_ordered)) { + bd = list_entry(sdp->sd_log_le_ordered.next, struct gfs2_bufdata, bd_le.le_list); + list_move(&bd->bd_le.le_list, &written); + bh = bd->bd_bh; + if (!buffer_dirty(bh)) + continue; + get_bh(bh); + gfs2_log_unlock(sdp); + lock_buffer(bh); + if (test_clear_buffer_dirty(bh)) { + bh->b_end_io = end_buffer_write_sync; + submit_bh(WRITE, bh); + } else { + unlock_buffer(bh); brelse(bh); - kfree(lb); - flushcount++; + } + gfs2_log_lock(sdp); } + list_splice(&written, &sdp->sd_log_le_ordered); + gfs2_log_unlock(sdp); +} - /* If nothing was journaled, the header is unplanned and unwanted. */ - if (flushcount) { - log_write_header(sdp, 0, 0); - } else { - unsigned int tail; - tail = current_tail(sdp); +static void gfs2_ordered_wait(struct gfs2_sbd *sdp) +{ + struct gfs2_bufdata *bd; + struct buffer_head *bh; - gfs2_ail1_empty(sdp, 0); - if (sdp->sd_log_tail != tail) - log_pull_tail(sdp, tail); + gfs2_log_lock(sdp); + while (!list_empty(&sdp->sd_log_le_ordered)) { + bd = list_entry(sdp->sd_log_le_ordered.prev, struct gfs2_bufdata, bd_le.le_list); + bh = bd->bd_bh; + if (buffer_locked(bh)) { + get_bh(bh); + gfs2_log_unlock(sdp); + wait_on_buffer(bh); + brelse(bh); + gfs2_log_lock(sdp); + continue; + } + list_del_init(&bd->bd_le.le_list); } + gfs2_log_unlock(sdp); } /** @@ -640,10 +714,16 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) INIT_LIST_HEAD(&ai->ai_ail1_list); INIT_LIST_HEAD(&ai->ai_ail2_list); - gfs2_assert_withdraw(sdp, - sdp->sd_log_num_buf + sdp->sd_log_num_jdata == - sdp->sd_log_commited_buf + - sdp->sd_log_commited_databuf); + if (sdp->sd_log_num_buf != sdp->sd_log_commited_buf) { + printk(KERN_INFO "GFS2: log buf %u %u\n", sdp->sd_log_num_buf, + sdp->sd_log_commited_buf); + gfs2_assert_withdraw(sdp, 0); + } + if (sdp->sd_log_num_databuf != sdp->sd_log_commited_databuf) { + printk(KERN_INFO "GFS2: log databuf %u %u\n", + sdp->sd_log_num_databuf, sdp->sd_log_commited_databuf); + gfs2_assert_withdraw(sdp, 0); + } gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke); @@ -651,8 +731,11 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) sdp->sd_log_flush_wrapped = 0; ai->ai_first = sdp->sd_log_flush_head; + gfs2_ordered_write(sdp); lops_before_commit(sdp); - if (!list_empty(&sdp->sd_log_flush_list)) + gfs2_ordered_wait(sdp); + + if (sdp->sd_log_head != sdp->sd_log_flush_head) log_flush_commit(sdp); else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){ gfs2_log_lock(sdp); @@ -744,7 +827,6 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp) gfs2_assert_withdraw(sdp, !sdp->sd_log_blks_reserved); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_gl); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_buf); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_jdata); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf); diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h index 8e7aa0f2910..dae28240062 100644 --- a/fs/gfs2/log.h +++ b/fs/gfs2/log.h @@ -52,12 +52,14 @@ int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags); int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks); void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks); +void gfs2_log_incr_head(struct gfs2_sbd *sdp); struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp); struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp, struct buffer_head *real); void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl); void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans); +void gfs2_remove_from_ail(struct address_space *mapping, struct gfs2_bufdata *bd); void gfs2_log_shutdown(struct gfs2_sbd *sdp); void gfs2_meta_syncfs(struct gfs2_sbd *sdp); diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index 3b395c41b2f..6c27cea761c 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -27,7 +27,104 @@ #include "trans.h" #include "util.h" -static void glock_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) +/** + * gfs2_pin - Pin a buffer in memory + * @sdp: The superblock + * @bh: The buffer to be pinned + * + * The log lock must be held when calling this function + */ +static void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh) +{ + struct gfs2_bufdata *bd; + + gfs2_assert_withdraw(sdp, test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)); + + clear_buffer_dirty(bh); + if (test_set_buffer_pinned(bh)) + gfs2_assert_withdraw(sdp, 0); + if (!buffer_uptodate(bh)) + gfs2_io_error_bh(sdp, bh); + bd = bh->b_private; + /* If this buffer is in the AIL and it has already been written + * to in-place disk block, remove it from the AIL. + */ + if (bd->bd_ail) + list_move(&bd->bd_ail_st_list, &bd->bd_ail->ai_ail2_list); + get_bh(bh); +} + +/** + * gfs2_unpin - Unpin a buffer + * @sdp: the filesystem the buffer belongs to + * @bh: The buffer to unpin + * @ai: + * + */ + +static void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, + struct gfs2_ail *ai) +{ + struct gfs2_bufdata *bd = bh->b_private; + + gfs2_assert_withdraw(sdp, buffer_uptodate(bh)); + + if (!buffer_pinned(bh)) + gfs2_assert_withdraw(sdp, 0); + + lock_buffer(bh); + mark_buffer_dirty(bh); + clear_buffer_pinned(bh); + + gfs2_log_lock(sdp); + if (bd->bd_ail) { + list_del(&bd->bd_ail_st_list); + brelse(bh); + } else { + struct gfs2_glock *gl = bd->bd_gl; + list_add(&bd->bd_ail_gl_list, &gl->gl_ail_list); + atomic_inc(&gl->gl_ail_count); + } + bd->bd_ail = ai; + list_add(&bd->bd_ail_st_list, &ai->ai_ail1_list); + gfs2_log_unlock(sdp); + unlock_buffer(bh); +} + + +static inline struct gfs2_log_descriptor *bh_log_desc(struct buffer_head *bh) +{ + return (struct gfs2_log_descriptor *)bh->b_data; +} + +static inline __be64 *bh_log_ptr(struct buffer_head *bh) +{ + struct gfs2_log_descriptor *ld = bh_log_desc(bh); + return (__force __be64 *)(ld + 1); +} + +static inline __be64 *bh_ptr_end(struct buffer_head *bh) +{ + return (__force __be64 *)(bh->b_data + bh->b_size); +} + + +static struct buffer_head *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type) +{ + struct buffer_head *bh = gfs2_log_get_buf(sdp); + struct gfs2_log_descriptor *ld = bh_log_desc(bh); + ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC); + ld->ld_header.mh_type = cpu_to_be32(GFS2_METATYPE_LD); + ld->ld_header.mh_format = cpu_to_be32(GFS2_FORMAT_LD); + ld->ld_type = cpu_to_be32(ld_type); + ld->ld_length = 0; + ld->ld_data1 = 0; + ld->ld_data2 = 0; + memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved)); + return bh; +} + +static void __glock_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) { struct gfs2_glock *gl; struct gfs2_trans *tr = current->journal_info; @@ -38,15 +135,19 @@ static void glock_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(gl))) return; - gfs2_log_lock(sdp); - if (!list_empty(&le->le_list)){ - gfs2_log_unlock(sdp); + if (!list_empty(&le->le_list)) return; - } + gfs2_glock_hold(gl); set_bit(GLF_DIRTY, &gl->gl_flags); sdp->sd_log_num_gl++; list_add(&le->le_list, &sdp->sd_log_le_gl); +} + +static void glock_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) +{ + gfs2_log_lock(sdp); + __glock_lo_add(sdp, le); gfs2_log_unlock(sdp); } @@ -71,30 +172,25 @@ static void buf_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) struct gfs2_bufdata *bd = container_of(le, struct gfs2_bufdata, bd_le); struct gfs2_trans *tr; + lock_buffer(bd->bd_bh); gfs2_log_lock(sdp); - if (!list_empty(&bd->bd_list_tr)) { - gfs2_log_unlock(sdp); - return; - } + if (!list_empty(&bd->bd_list_tr)) + goto out; tr = current->journal_info; tr->tr_touched = 1; tr->tr_num_buf++; list_add(&bd->bd_list_tr, &tr->tr_list_buf); - gfs2_log_unlock(sdp); - if (!list_empty(&le->le_list)) - return; - - gfs2_trans_add_gl(bd->bd_gl); - + goto out; + __glock_lo_add(sdp, &bd->bd_gl->gl_le); gfs2_meta_check(sdp, bd->bd_bh); gfs2_pin(sdp, bd->bd_bh); - gfs2_log_lock(sdp); sdp->sd_log_num_buf++; list_add(&le->le_list, &sdp->sd_log_le_buf); - gfs2_log_unlock(sdp); - tr->tr_num_buf_new++; +out: + gfs2_log_unlock(sdp); + unlock_buffer(bd->bd_bh); } static void buf_lo_incore_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) @@ -117,8 +213,7 @@ static void buf_lo_before_commit(struct gfs2_sbd *sdp) struct buffer_head *bh; struct gfs2_log_descriptor *ld; struct gfs2_bufdata *bd1 = NULL, *bd2; - unsigned int total = sdp->sd_log_num_buf; - unsigned int offset = BUF_OFFSET; + unsigned int total; unsigned int limit; unsigned int num; unsigned n; @@ -127,22 +222,20 @@ static void buf_lo_before_commit(struct gfs2_sbd *sdp) limit = buf_limit(sdp); /* for 4k blocks, limit = 503 */ + gfs2_log_lock(sdp); + total = sdp->sd_log_num_buf; bd1 = bd2 = list_prepare_entry(bd1, &sdp->sd_log_le_buf, bd_le.le_list); while(total) { num = total; if (total > limit) num = limit; - bh = gfs2_log_get_buf(sdp); - ld = (struct gfs2_log_descriptor *)bh->b_data; - ptr = (__be64 *)(bh->b_data + offset); - ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC); - ld->ld_header.mh_type = cpu_to_be32(GFS2_METATYPE_LD); - ld->ld_header.mh_format = cpu_to_be32(GFS2_FORMAT_LD); - ld->ld_type = cpu_to_be32(GFS2_LOG_DESC_METADATA); + gfs2_log_unlock(sdp); + bh = gfs2_get_log_desc(sdp, GFS2_LOG_DESC_METADATA); + gfs2_log_lock(sdp); + ld = bh_log_desc(bh); + ptr = bh_log_ptr(bh); ld->ld_length = cpu_to_be32(num + 1); ld->ld_data1 = cpu_to_be32(num); - ld->ld_data2 = cpu_to_be32(0); - memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved)); n = 0; list_for_each_entry_continue(bd1, &sdp->sd_log_le_buf, @@ -152,21 +245,27 @@ static void buf_lo_before_commit(struct gfs2_sbd *sdp) break; } - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); + gfs2_log_unlock(sdp); + submit_bh(WRITE, bh); + gfs2_log_lock(sdp); n = 0; list_for_each_entry_continue(bd2, &sdp->sd_log_le_buf, bd_le.le_list) { + get_bh(bd2->bd_bh); + gfs2_log_unlock(sdp); + lock_buffer(bd2->bd_bh); bh = gfs2_log_fake_buf(sdp, bd2->bd_bh); - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); + submit_bh(WRITE, bh); + gfs2_log_lock(sdp); if (++n >= num) break; } + BUG_ON(total < num); total -= num; } + gfs2_log_unlock(sdp); } static void buf_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai) @@ -270,11 +369,8 @@ static void revoke_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) tr = current->journal_info; tr->tr_touched = 1; tr->tr_num_revoke++; - - gfs2_log_lock(sdp); sdp->sd_log_num_revoke++; list_add(&le->le_list, &sdp->sd_log_le_revoke); - gfs2_log_unlock(sdp); } static void revoke_lo_before_commit(struct gfs2_sbd *sdp) @@ -284,32 +380,25 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp) struct buffer_head *bh; unsigned int offset; struct list_head *head = &sdp->sd_log_le_revoke; - struct gfs2_revoke *rv; + struct gfs2_bufdata *bd; if (!sdp->sd_log_num_revoke) return; - bh = gfs2_log_get_buf(sdp); - ld = (struct gfs2_log_descriptor *)bh->b_data; - ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC); - ld->ld_header.mh_type = cpu_to_be32(GFS2_METATYPE_LD); - ld->ld_header.mh_format = cpu_to_be32(GFS2_FORMAT_LD); - ld->ld_type = cpu_to_be32(GFS2_LOG_DESC_REVOKE); + bh = gfs2_get_log_desc(sdp, GFS2_LOG_DESC_REVOKE); + ld = bh_log_desc(bh); ld->ld_length = cpu_to_be32(gfs2_struct2blk(sdp, sdp->sd_log_num_revoke, sizeof(u64))); ld->ld_data1 = cpu_to_be32(sdp->sd_log_num_revoke); - ld->ld_data2 = cpu_to_be32(0); - memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved)); offset = sizeof(struct gfs2_log_descriptor); while (!list_empty(head)) { - rv = list_entry(head->next, struct gfs2_revoke, rv_le.le_list); - list_del_init(&rv->rv_le.le_list); + bd = list_entry(head->next, struct gfs2_bufdata, bd_le.le_list); + list_del_init(&bd->bd_le.le_list); sdp->sd_log_num_revoke--; if (offset + sizeof(u64) > sdp->sd_sb.sb_bsize) { - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); + submit_bh(WRITE, bh); bh = gfs2_log_get_buf(sdp); mh = (struct gfs2_meta_header *)bh->b_data; @@ -319,15 +408,14 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp) offset = sizeof(struct gfs2_meta_header); } - *(__be64 *)(bh->b_data + offset) = cpu_to_be64(rv->rv_blkno); - kfree(rv); + *(__be64 *)(bh->b_data + offset) = cpu_to_be64(bd->bd_blkno); + kmem_cache_free(gfs2_bufdata_cachep, bd); offset += sizeof(u64); } gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); + submit_bh(WRITE, bh); } static void revoke_lo_before_scan(struct gfs2_jdesc *jd, @@ -466,222 +554,136 @@ static void databuf_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) struct address_space *mapping = bd->bd_bh->b_page->mapping; struct gfs2_inode *ip = GFS2_I(mapping->host); + lock_buffer(bd->bd_bh); gfs2_log_lock(sdp); - if (!list_empty(&bd->bd_list_tr)) { - gfs2_log_unlock(sdp); - return; - } + if (!list_empty(&bd->bd_list_tr)) + goto out; tr->tr_touched = 1; if (gfs2_is_jdata(ip)) { tr->tr_num_buf++; list_add(&bd->bd_list_tr, &tr->tr_list_buf); } - gfs2_log_unlock(sdp); if (!list_empty(&le->le_list)) - return; + goto out; - gfs2_trans_add_gl(bd->bd_gl); + __glock_lo_add(sdp, &bd->bd_gl->gl_le); if (gfs2_is_jdata(ip)) { - sdp->sd_log_num_jdata++; gfs2_pin(sdp, bd->bd_bh); tr->tr_num_databuf_new++; + sdp->sd_log_num_databuf++; + list_add(&le->le_list, &sdp->sd_log_le_databuf); + } else { + list_add(&le->le_list, &sdp->sd_log_le_ordered); } - gfs2_log_lock(sdp); - sdp->sd_log_num_databuf++; - list_add(&le->le_list, &sdp->sd_log_le_databuf); +out: gfs2_log_unlock(sdp); + unlock_buffer(bd->bd_bh); } -static int gfs2_check_magic(struct buffer_head *bh) +static void gfs2_check_magic(struct buffer_head *bh) { - struct page *page = bh->b_page; void *kaddr; __be32 *ptr; - int rv = 0; - kaddr = kmap_atomic(page, KM_USER0); + clear_buffer_escaped(bh); + kaddr = kmap_atomic(bh->b_page, KM_USER0); ptr = kaddr + bh_offset(bh); if (*ptr == cpu_to_be32(GFS2_MAGIC)) - rv = 1; + set_buffer_escaped(bh); kunmap_atomic(kaddr, KM_USER0); - - return rv; } -/** - * databuf_lo_before_commit - Scan the data buffers, writing as we go - * - * Here we scan through the lists of buffers and make the assumption - * that any buffer thats been pinned is being journaled, and that - * any unpinned buffer is an ordered write data buffer and therefore - * will be written back rather than journaled. - */ -static void databuf_lo_before_commit(struct gfs2_sbd *sdp) +static void gfs2_write_blocks(struct gfs2_sbd *sdp, struct buffer_head *bh, + struct list_head *list, struct list_head *done, + unsigned int n) { - LIST_HEAD(started); - struct gfs2_bufdata *bd1 = NULL, *bd2, *bdt; - struct buffer_head *bh = NULL,*bh1 = NULL; + struct buffer_head *bh1; struct gfs2_log_descriptor *ld; - unsigned int limit; - unsigned int total_dbuf; - unsigned int total_jdata = sdp->sd_log_num_jdata; - unsigned int num, n; - __be64 *ptr = NULL; + struct gfs2_bufdata *bd; + __be64 *ptr; + + if (!bh) + return; - limit = databuf_limit(sdp); + ld = bh_log_desc(bh); + ld->ld_length = cpu_to_be32(n + 1); + ld->ld_data1 = cpu_to_be32(n); - /* - * Start writing ordered buffers, write journaled buffers - * into the log along with a header - */ + ptr = bh_log_ptr(bh); + + get_bh(bh); + submit_bh(WRITE, bh); gfs2_log_lock(sdp); - total_dbuf = sdp->sd_log_num_databuf; - bd2 = bd1 = list_prepare_entry(bd1, &sdp->sd_log_le_databuf, - bd_le.le_list); - while(total_dbuf) { - num = total_jdata; - if (num > limit) - num = limit; - n = 0; - list_for_each_entry_safe_continue(bd1, bdt, - &sdp->sd_log_le_databuf, - bd_le.le_list) { - /* store off the buffer head in a local ptr since - * gfs2_bufdata might change when we drop the log lock - */ - bh1 = bd1->bd_bh; - - /* An ordered write buffer */ - if (bh1 && !buffer_pinned(bh1)) { - list_move(&bd1->bd_le.le_list, &started); - if (bd1 == bd2) { - bd2 = NULL; - bd2 = list_prepare_entry(bd2, - &sdp->sd_log_le_databuf, - bd_le.le_list); - } - total_dbuf--; - if (bh1) { - if (buffer_dirty(bh1)) { - get_bh(bh1); - - gfs2_log_unlock(sdp); - - ll_rw_block(SWRITE, 1, &bh1); - brelse(bh1); - - gfs2_log_lock(sdp); - } - continue; - } - continue; - } else if (bh1) { /* A journaled buffer */ - int magic; - gfs2_log_unlock(sdp); - if (!bh) { - bh = gfs2_log_get_buf(sdp); - ld = (struct gfs2_log_descriptor *) - bh->b_data; - ptr = (__be64 *)(bh->b_data + - DATABUF_OFFSET); - ld->ld_header.mh_magic = - cpu_to_be32(GFS2_MAGIC); - ld->ld_header.mh_type = - cpu_to_be32(GFS2_METATYPE_LD); - ld->ld_header.mh_format = - cpu_to_be32(GFS2_FORMAT_LD); - ld->ld_type = - cpu_to_be32(GFS2_LOG_DESC_JDATA); - ld->ld_length = cpu_to_be32(num + 1); - ld->ld_data1 = cpu_to_be32(num); - ld->ld_data2 = cpu_to_be32(0); - memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved)); - } - magic = gfs2_check_magic(bh1); - *ptr++ = cpu_to_be64(bh1->b_blocknr); - *ptr++ = cpu_to_be64((__u64)magic); - clear_buffer_escaped(bh1); - if (unlikely(magic != 0)) - set_buffer_escaped(bh1); - gfs2_log_lock(sdp); - if (++n >= num) - break; - } else if (!bh1) { - total_dbuf--; - sdp->sd_log_num_databuf--; - list_del_init(&bd1->bd_le.le_list); - if (bd1 == bd2) { - bd2 = NULL; - bd2 = list_prepare_entry(bd2, - &sdp->sd_log_le_databuf, - bd_le.le_list); - } - kmem_cache_free(gfs2_bufdata_cachep, bd1); - } + while(!list_empty(list)) { + bd = list_entry(list->next, struct gfs2_bufdata, bd_le.le_list); + list_move_tail(&bd->bd_le.le_list, done); + get_bh(bd->bd_bh); + while (be64_to_cpu(*ptr) != bd->bd_bh->b_blocknr) { + gfs2_log_incr_head(sdp); + ptr += 2; } gfs2_log_unlock(sdp); - if (bh) { - set_buffer_mapped(bh); - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); - bh = NULL; + lock_buffer(bd->bd_bh); + if (buffer_escaped(bd->bd_bh)) { + void *kaddr; + bh1 = gfs2_log_get_buf(sdp); + kaddr = kmap_atomic(bd->bd_bh->b_page, KM_USER0); + memcpy(bh1->b_data, kaddr + bh_offset(bd->bd_bh), + bh1->b_size); + kunmap_atomic(kaddr, KM_USER0); + *(__be32 *)bh1->b_data = 0; + clear_buffer_escaped(bd->bd_bh); + unlock_buffer(bd->bd_bh); + brelse(bd->bd_bh); + } else { + bh1 = gfs2_log_fake_buf(sdp, bd->bd_bh); } - n = 0; + submit_bh(WRITE, bh1); gfs2_log_lock(sdp); - list_for_each_entry_continue(bd2, &sdp->sd_log_le_databuf, - bd_le.le_list) { - if (!bd2->bd_bh) - continue; - /* copy buffer if it needs escaping */ - gfs2_log_unlock(sdp); - if (unlikely(buffer_escaped(bd2->bd_bh))) { - void *kaddr; - struct page *page = bd2->bd_bh->b_page; - bh = gfs2_log_get_buf(sdp); - kaddr = kmap_atomic(page, KM_USER0); - memcpy(bh->b_data, - kaddr + bh_offset(bd2->bd_bh), - sdp->sd_sb.sb_bsize); - kunmap_atomic(kaddr, KM_USER0); - *(__be32 *)bh->b_data = 0; - } else { - bh = gfs2_log_fake_buf(sdp, bd2->bd_bh); - } - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); - gfs2_log_lock(sdp); - if (++n >= num) - break; - } - bh = NULL; - BUG_ON(total_dbuf < num); - total_dbuf -= num; - total_jdata -= num; + ptr += 2; } gfs2_log_unlock(sdp); + brelse(bh); +} - /* Wait on all ordered buffers */ - while (!list_empty(&started)) { - gfs2_log_lock(sdp); - bd1 = list_entry(started.next, struct gfs2_bufdata, - bd_le.le_list); - list_del_init(&bd1->bd_le.le_list); - sdp->sd_log_num_databuf--; - bh = bd1->bd_bh; - if (bh) { - bh->b_private = NULL; - get_bh(bh); - gfs2_log_unlock(sdp); - wait_on_buffer(bh); - brelse(bh); - } else - gfs2_log_unlock(sdp); +/** + * databuf_lo_before_commit - Scan the data buffers, writing as we go + * + */ - kmem_cache_free(gfs2_bufdata_cachep, bd1); - } +static void databuf_lo_before_commit(struct gfs2_sbd *sdp) +{ + struct gfs2_bufdata *bd = NULL; + struct buffer_head *bh = NULL; + unsigned int n = 0; + __be64 *ptr = NULL, *end = NULL; + LIST_HEAD(processed); + LIST_HEAD(in_progress); - /* We've removed all the ordered write bufs here, so only jdata left */ - gfs2_assert_warn(sdp, sdp->sd_log_num_databuf == sdp->sd_log_num_jdata); + gfs2_log_lock(sdp); + while (!list_empty(&sdp->sd_log_le_databuf)) { + if (ptr == end) { + gfs2_log_unlock(sdp); + gfs2_write_blocks(sdp, bh, &in_progress, &processed, n); + n = 0; + bh = gfs2_get_log_desc(sdp, GFS2_LOG_DESC_JDATA); + ptr = bh_log_ptr(bh); + end = bh_ptr_end(bh) - 1; + gfs2_log_lock(sdp); + continue; + } + bd = list_entry(sdp->sd_log_le_databuf.next, struct gfs2_bufdata, bd_le.le_list); + list_move_tail(&bd->bd_le.le_list, &in_progress); + gfs2_check_magic(bd->bd_bh); + *ptr++ = cpu_to_be64(bd->bd_bh->b_blocknr); + *ptr++ = cpu_to_be64(buffer_escaped(bh) ? 1 : 0); + n++; + } + gfs2_log_unlock(sdp); + gfs2_write_blocks(sdp, bh, &in_progress, &processed, n); + gfs2_log_lock(sdp); + list_splice(&processed, &sdp->sd_log_le_databuf); + gfs2_log_unlock(sdp); } static int databuf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start, @@ -765,11 +767,9 @@ static void databuf_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai) bd = list_entry(head->next, struct gfs2_bufdata, bd_le.le_list); list_del_init(&bd->bd_le.le_list); sdp->sd_log_num_databuf--; - sdp->sd_log_num_jdata--; gfs2_unpin(sdp, bd->bd_bh, ai); } gfs2_assert_warn(sdp, !sdp->sd_log_num_databuf); - gfs2_assert_warn(sdp, !sdp->sd_log_num_jdata); } @@ -817,10 +817,10 @@ const struct gfs2_log_operations gfs2_databuf_lops = { const struct gfs2_log_operations *gfs2_log_ops[] = { &gfs2_glock_lops, + &gfs2_databuf_lops, &gfs2_buf_lops, - &gfs2_revoke_lops, &gfs2_rg_lops, - &gfs2_databuf_lops, + &gfs2_revoke_lops, NULL, }; diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index d5d4e68b880..79c91fd8381 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -107,6 +107,8 @@ static int __init init_gfs2_fs(void) fail_unregister: unregister_filesystem(&gfs2_fs_type); fail: + gfs2_glock_exit(); + if (gfs2_bufdata_cachep) kmem_cache_destroy(gfs2_bufdata_cachep); @@ -127,6 +129,7 @@ fail: static void __exit exit_gfs2_fs(void) { + gfs2_glock_exit(); gfs2_unregister_debugfs(); unregister_filesystem(&gfs2_fs_type); unregister_filesystem(&gfs2meta_fs_type); diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index 8da343b34ae..4da423985e4 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -297,74 +297,35 @@ void gfs2_attach_bufdata(struct gfs2_glock *gl, struct buffer_head *bh, unlock_page(bh->b_page); } -/** - * gfs2_pin - Pin a buffer in memory - * @sdp: the filesystem the buffer belongs to - * @bh: The buffer to be pinned - * - */ - -void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh) +void gfs2_remove_from_journal(struct buffer_head *bh, struct gfs2_trans *tr, int meta) { + struct gfs2_sbd *sdp = GFS2_SB(bh->b_page->mapping->host); struct gfs2_bufdata *bd = bh->b_private; - - gfs2_assert_withdraw(sdp, test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)); - - if (test_set_buffer_pinned(bh)) - gfs2_assert_withdraw(sdp, 0); - - wait_on_buffer(bh); - - /* If this buffer is in the AIL and it has already been written - to in-place disk block, remove it from the AIL. */ - - gfs2_log_lock(sdp); - if (bd->bd_ail && !buffer_in_io(bh)) - list_move(&bd->bd_ail_st_list, &bd->bd_ail->ai_ail2_list); - gfs2_log_unlock(sdp); - - clear_buffer_dirty(bh); - wait_on_buffer(bh); - - if (!buffer_uptodate(bh)) - gfs2_io_error_bh(sdp, bh); - - get_bh(bh); -} - -/** - * gfs2_unpin - Unpin a buffer - * @sdp: the filesystem the buffer belongs to - * @bh: The buffer to unpin - * @ai: - * - */ - -void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, - struct gfs2_ail *ai) -{ - struct gfs2_bufdata *bd = bh->b_private; - - gfs2_assert_withdraw(sdp, buffer_uptodate(bh)); - - if (!buffer_pinned(bh)) - gfs2_assert_withdraw(sdp, 0); - - mark_buffer_dirty(bh); - clear_buffer_pinned(bh); - - gfs2_log_lock(sdp); - if (bd->bd_ail) { - list_del(&bd->bd_ail_st_list); + if (test_clear_buffer_pinned(bh)) { + list_del_init(&bd->bd_le.le_list); + if (meta) { + gfs2_assert_warn(sdp, sdp->sd_log_num_buf); + sdp->sd_log_num_buf--; + tr->tr_num_buf_rm++; + } else { + gfs2_assert_warn(sdp, sdp->sd_log_num_databuf); + sdp->sd_log_num_databuf--; + tr->tr_num_databuf_rm++; + } + tr->tr_touched = 1; brelse(bh); - } else { - struct gfs2_glock *gl = bd->bd_gl; - list_add(&bd->bd_ail_gl_list, &gl->gl_ail_list); - atomic_inc(&gl->gl_ail_count); } - bd->bd_ail = ai; - list_add(&bd->bd_ail_st_list, &ai->ai_ail1_list); - gfs2_log_unlock(sdp); + if (bd) { + if (bd->bd_ail) { + gfs2_remove_from_ail(NULL, bd); + bh->b_private = NULL; + bd->bd_bh = NULL; + bd->bd_blkno = bh->b_blocknr; + gfs2_trans_add_revoke(sdp, bd); + } + } + clear_buffer_dirty(bh); + clear_buffer_uptodate(bh); } /** @@ -383,44 +344,11 @@ void gfs2_meta_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen) while (blen) { bh = getbuf(ip->i_gl, bstart, NO_CREATE); if (bh) { - struct gfs2_bufdata *bd = bh->b_private; - - if (test_clear_buffer_pinned(bh)) { - struct gfs2_trans *tr = current->journal_info; - struct gfs2_inode *bh_ip = - GFS2_I(bh->b_page->mapping->host); - - gfs2_log_lock(sdp); - list_del_init(&bd->bd_le.le_list); - gfs2_assert_warn(sdp, sdp->sd_log_num_buf); - sdp->sd_log_num_buf--; - gfs2_log_unlock(sdp); - if (bh_ip->i_inode.i_private != NULL) - tr->tr_num_databuf_rm++; - else - tr->tr_num_buf_rm++; - brelse(bh); - } - if (bd) { - gfs2_log_lock(sdp); - if (bd->bd_ail) { - u64 blkno = bh->b_blocknr; - bd->bd_ail = NULL; - list_del(&bd->bd_ail_st_list); - list_del(&bd->bd_ail_gl_list); - atomic_dec(&bd->bd_gl->gl_ail_count); - brelse(bh); - gfs2_log_unlock(sdp); - gfs2_trans_add_revoke(sdp, blkno); - } else - gfs2_log_unlock(sdp); - } - lock_buffer(bh); - clear_buffer_dirty(bh); - clear_buffer_uptodate(bh); + gfs2_log_lock(sdp); + gfs2_remove_from_journal(bh, current->journal_info, 1); + gfs2_log_unlock(sdp); unlock_buffer(bh); - brelse(bh); } @@ -446,10 +374,10 @@ void gfs2_meta_cache_flush(struct gfs2_inode *ip) for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) { bh_slot = &ip->i_cache[x]; - if (!*bh_slot) - break; - brelse(*bh_slot); - *bh_slot = NULL; + if (*bh_slot) { + brelse(*bh_slot); + *bh_slot = NULL; + } } spin_unlock(&ip->i_spin); diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h index 527bf19d969..b7048222ebb 100644 --- a/fs/gfs2/meta_io.h +++ b/fs/gfs2/meta_io.h @@ -50,9 +50,9 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh); void gfs2_attach_bufdata(struct gfs2_glock *gl, struct buffer_head *bh, int meta); -void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh); -void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, - struct gfs2_ail *ai); + +void gfs2_remove_from_journal(struct buffer_head *bh, struct gfs2_trans *tr, + int meta); void gfs2_meta_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen); diff --git a/fs/gfs2/mount.c b/fs/gfs2/mount.c index 4864659555d..b941f9f9f95 100644 --- a/fs/gfs2/mount.c +++ b/fs/gfs2/mount.c @@ -42,6 +42,7 @@ enum { Opt_nosuiddir, Opt_data_writeback, Opt_data_ordered, + Opt_err, }; static match_table_t tokens = { @@ -64,7 +65,8 @@ static match_table_t tokens = { {Opt_suiddir, "suiddir"}, {Opt_nosuiddir, "nosuiddir"}, {Opt_data_writeback, "data=writeback"}, - {Opt_data_ordered, "data=ordered"} + {Opt_data_ordered, "data=ordered"}, + {Opt_err, NULL} }; /** @@ -237,6 +239,7 @@ int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount) case Opt_data_ordered: args->ar_data = GFS2_DATA_ORDERED; break; + case Opt_err: default: fs_info(sdp, "unknown option: %s\n", o); error = -EINVAL; diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c index 42a5f58f6fc..873a511ef2b 100644 --- a/fs/gfs2/ops_address.c +++ b/fs/gfs2/ops_address.c @@ -90,7 +90,7 @@ static int gfs2_get_block_noalloc(struct inode *inode, sector_t lblock, error = gfs2_block_map(inode, lblock, 0, bh_result); if (error) return error; - if (bh_result->b_blocknr == 0) + if (!buffer_mapped(bh_result)) return -EIO; return 0; } @@ -414,7 +414,8 @@ static int gfs2_prepare_write(struct file *file, struct page *page, if (ind_blocks || data_blocks) rblocks += RES_STATFS + RES_QUOTA; - error = gfs2_trans_begin(sdp, rblocks, 0); + error = gfs2_trans_begin(sdp, rblocks, + PAGE_CACHE_SIZE/sdp->sd_sb.sb_bsize); if (error) goto out_trans_fail; @@ -616,58 +617,50 @@ static sector_t gfs2_bmap(struct address_space *mapping, sector_t lblock) return dblock; } -static void discard_buffer(struct gfs2_sbd *sdp, struct buffer_head *bh) +static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh) { struct gfs2_bufdata *bd; + lock_buffer(bh); gfs2_log_lock(sdp); + clear_buffer_dirty(bh); bd = bh->b_private; if (bd) { - bd->bd_bh = NULL; - bh->b_private = NULL; - if (!bd->bd_ail && list_empty(&bd->bd_le.le_list)) - kmem_cache_free(gfs2_bufdata_cachep, bd); + if (!list_empty(&bd->bd_le.le_list) && !buffer_pinned(bh)) + list_del_init(&bd->bd_le.le_list); + else + gfs2_remove_from_journal(bh, current->journal_info, 0); } - gfs2_log_unlock(sdp); - - lock_buffer(bh); - clear_buffer_dirty(bh); bh->b_bdev = NULL; clear_buffer_mapped(bh); clear_buffer_req(bh); clear_buffer_new(bh); - clear_buffer_delay(bh); + gfs2_log_unlock(sdp); unlock_buffer(bh); } static void gfs2_invalidatepage(struct page *page, unsigned long offset) { struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host); - struct buffer_head *head, *bh, *next; - unsigned int curr_off = 0; + struct buffer_head *bh, *head; + unsigned long pos = 0; BUG_ON(!PageLocked(page)); if (offset == 0) ClearPageChecked(page); if (!page_has_buffers(page)) - return; + goto out; bh = head = page_buffers(page); do { - unsigned int next_off = curr_off + bh->b_size; - next = bh->b_this_page; - - if (offset <= curr_off) - discard_buffer(sdp, bh); - - curr_off = next_off; - bh = next; + if (offset <= pos) + gfs2_discard(sdp, bh); + pos += bh->b_size; + bh = bh->b_this_page; } while (bh != head); - - if (!offset) +out: + if (offset == 0) try_to_release_page(page, 0); - - return; } /** @@ -736,59 +729,6 @@ out: } /** - * stuck_releasepage - We're stuck in gfs2_releasepage(). Print stuff out. - * @bh: the buffer we're stuck on - * - */ - -static void stuck_releasepage(struct buffer_head *bh) -{ - struct inode *inode = bh->b_page->mapping->host; - struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; - struct gfs2_bufdata *bd = bh->b_private; - struct gfs2_glock *gl; -static unsigned limit = 0; - - if (limit > 3) - return; - limit++; - - fs_warn(sdp, "stuck in gfs2_releasepage() %p\n", inode); - fs_warn(sdp, "blkno = %llu, bh->b_count = %d\n", - (unsigned long long)bh->b_blocknr, atomic_read(&bh->b_count)); - fs_warn(sdp, "pinned = %u\n", buffer_pinned(bh)); - fs_warn(sdp, "bh->b_private = %s\n", (bd) ? "!NULL" : "NULL"); - - if (!bd) - return; - - gl = bd->bd_gl; - - fs_warn(sdp, "gl = (%u, %llu)\n", - gl->gl_name.ln_type, (unsigned long long)gl->gl_name.ln_number); - - fs_warn(sdp, "bd_list_tr = %s, bd_le.le_list = %s\n", - (list_empty(&bd->bd_list_tr)) ? "no" : "yes", - (list_empty(&bd->bd_le.le_list)) ? "no" : "yes"); - - if (gl->gl_ops == &gfs2_inode_glops) { - struct gfs2_inode *ip = gl->gl_object; - unsigned int x; - - if (!ip) - return; - - fs_warn(sdp, "ip = %llu %llu\n", - (unsigned long long)ip->i_no_formal_ino, - (unsigned long long)ip->i_no_addr); - - for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) - fs_warn(sdp, "ip->i_cache[%u] = %s\n", - x, (ip->i_cache[x]) ? "!NULL" : "NULL"); - } -} - -/** * gfs2_releasepage - free the metadata associated with a page * @page: the page that's being released * @gfp_mask: passed from Linux VFS, ignored by us @@ -805,41 +745,39 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask) struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; struct buffer_head *bh, *head; struct gfs2_bufdata *bd; - unsigned long t = jiffies + gfs2_tune_get(sdp, gt_stall_secs) * HZ; if (!page_has_buffers(page)) - goto out; + return 0; + gfs2_log_lock(sdp); head = bh = page_buffers(page); do { - while (atomic_read(&bh->b_count)) { - if (!atomic_read(&aspace->i_writecount)) - return 0; - - if (!(gfp_mask & __GFP_WAIT)) - return 0; - - if (time_after_eq(jiffies, t)) { - stuck_releasepage(bh); - /* should we withdraw here? */ - return 0; - } - - yield(); - } - + if (atomic_read(&bh->b_count)) + goto cannot_release; + bd = bh->b_private; + if (bd && bd->bd_ail) + goto cannot_release; gfs2_assert_warn(sdp, !buffer_pinned(bh)); gfs2_assert_warn(sdp, !buffer_dirty(bh)); + bh = bh->b_this_page; + } while(bh != head); + gfs2_log_unlock(sdp); + head = bh = page_buffers(page); + do { gfs2_log_lock(sdp); bd = bh->b_private; if (bd) { gfs2_assert_warn(sdp, bd->bd_bh == bh); gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); - gfs2_assert_warn(sdp, !bd->bd_ail); - bd->bd_bh = NULL; - if (!list_empty(&bd->bd_le.le_list)) - bd = NULL; + if (!list_empty(&bd->bd_le.le_list)) { + if (!buffer_pinned(bh)) + list_del_init(&bd->bd_le.le_list); + else + bd = NULL; + } + if (bd) + bd->bd_bh = NULL; bh->b_private = NULL; } gfs2_log_unlock(sdp); @@ -849,8 +787,10 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask) bh = bh->b_this_page; } while (bh != head); -out: return try_to_free_buffers(page); +cannot_release: + gfs2_log_unlock(sdp); + return 0; } const struct address_space_operations gfs2_file_aops = { diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c index b8312edee0e..e2d1347796a 100644 --- a/fs/gfs2/ops_export.c +++ b/fs/gfs2/ops_export.c @@ -237,7 +237,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj) inode = gfs2_inode_lookup(sb, DT_UNKNOWN, inum->no_addr, - 0); + 0, 0); if (!inode) goto fail; if (IS_ERR(inode)) { diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index 94d76ace0b9..46a9e10ff17 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -571,7 +571,8 @@ static int do_flock(struct file *file, int cmd, struct file_lock *fl) int error = 0; state = (fl->fl_type == F_WRLCK) ? LM_ST_EXCLUSIVE : LM_ST_SHARED; - flags = (IS_SETLKW(cmd) ? 0 : LM_FLAG_TRY) | GL_EXACT | GL_NOCACHE; + flags = (IS_SETLKW(cmd) ? 0 : LM_FLAG_TRY) | GL_EXACT | GL_NOCACHE + | GL_FLOCK; mutex_lock(&fp->f_fl_mutex); @@ -579,21 +580,19 @@ static int do_flock(struct file *file, int cmd, struct file_lock *fl) if (gl) { if (fl_gh->gh_state == state) goto out; - gfs2_glock_hold(gl); flock_lock_file_wait(file, &(struct file_lock){.fl_type = F_UNLCK}); - gfs2_glock_dq_uninit(fl_gh); + gfs2_glock_dq_wait(fl_gh); + gfs2_holder_reinit(state, flags, fl_gh); } else { error = gfs2_glock_get(GFS2_SB(&ip->i_inode), ip->i_no_addr, &gfs2_flock_glops, CREATE, &gl); if (error) goto out; + gfs2_holder_init(gl, state, flags, fl_gh); + gfs2_glock_put(gl); } - - gfs2_holder_init(gl, state, flags, fl_gh); - gfs2_glock_put(gl); - error = gfs2_glock_nq(fl_gh); if (error) { gfs2_holder_uninit(fl_gh); diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index cf5aa505054..17de58e83d9 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -28,18 +28,18 @@ #include "lm.h" #include "mount.h" #include "ops_fstype.h" +#include "ops_dentry.h" #include "ops_super.h" #include "recovery.h" #include "rgrp.h" #include "super.h" #include "sys.h" #include "util.h" +#include "log.h" #define DO 0 #define UNDO 1 -extern struct dentry_operations gfs2_dops; - static struct gfs2_sbd *init_sbd(struct super_block *sb) { struct gfs2_sbd *sdp; @@ -82,13 +82,15 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) INIT_LIST_HEAD(&sdp->sd_log_le_revoke); INIT_LIST_HEAD(&sdp->sd_log_le_rg); INIT_LIST_HEAD(&sdp->sd_log_le_databuf); + INIT_LIST_HEAD(&sdp->sd_log_le_ordered); mutex_init(&sdp->sd_log_reserve_mutex); INIT_LIST_HEAD(&sdp->sd_ail1_list); INIT_LIST_HEAD(&sdp->sd_ail2_list); init_rwsem(&sdp->sd_log_flush_lock); - INIT_LIST_HEAD(&sdp->sd_log_flush_list); + atomic_set(&sdp->sd_log_in_flight, 0); + init_waitqueue_head(&sdp->sd_log_flush_wait); INIT_LIST_HEAD(&sdp->sd_revoke_list); @@ -145,7 +147,8 @@ static int init_names(struct gfs2_sbd *sdp, int silent) snprintf(sdp->sd_proto_name, GFS2_FSNAME_LEN, "%s", proto); snprintf(sdp->sd_table_name, GFS2_FSNAME_LEN, "%s", table); - while ((table = strchr(sdp->sd_table_name, '/'))) + table = sdp->sd_table_name; + while ((table = strchr(table, '/'))) *table = '_'; out: @@ -161,14 +164,6 @@ static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh, if (undo) goto fail_trans; - p = kthread_run(gfs2_scand, sdp, "gfs2_scand"); - error = IS_ERR(p); - if (error) { - fs_err(sdp, "can't start scand thread: %d\n", error); - return error; - } - sdp->sd_scand_process = p; - for (sdp->sd_glockd_num = 0; sdp->sd_glockd_num < sdp->sd_args.ar_num_glockd; sdp->sd_glockd_num++) { @@ -229,14 +224,13 @@ fail: while (sdp->sd_glockd_num--) kthread_stop(sdp->sd_glockd_process[sdp->sd_glockd_num]); - kthread_stop(sdp->sd_scand_process); return error; } static inline struct inode *gfs2_lookup_root(struct super_block *sb, u64 no_addr) { - return gfs2_inode_lookup(sb, DT_DIR, no_addr, 0); + return gfs2_inode_lookup(sb, DT_DIR, no_addr, 0, 0); } static int init_sb(struct gfs2_sbd *sdp, int silent, int undo) @@ -301,8 +295,9 @@ static int init_sb(struct gfs2_sbd *sdp, int silent, int undo) fs_err(sdp, "can't get root dentry\n"); error = -ENOMEM; iput(inode); - } - sb->s_root->d_op = &gfs2_dops; + } else + sb->s_root->d_op = &gfs2_dops; + out: gfs2_glock_dq_uninit(&sb_gh); return error; @@ -368,7 +363,7 @@ static int init_journal(struct gfs2_sbd *sdp, int undo) ip = GFS2_I(sdp->sd_jdesc->jd_inode); error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, - LM_FLAG_NOEXP | GL_EXACT, + LM_FLAG_NOEXP | GL_EXACT | GL_NOCACHE, &sdp->sd_jinode_gh); if (error) { fs_err(sdp, "can't acquire journal inode glock: %d\n", @@ -818,7 +813,6 @@ static struct super_block* get_gfs2_sb(const char *dev_name) struct nameidata nd; struct file_system_type *fstype; struct super_block *sb = NULL, *s; - struct list_head *l; int error; error = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); @@ -830,8 +824,7 @@ static struct super_block* get_gfs2_sb(const char *dev_name) error = vfs_getattr(nd.mnt, nd.dentry, &stat); fstype = get_fs_type("gfs2"); - list_for_each(l, &fstype->fs_supers) { - s = list_entry(l, struct super_block, s_instances); + list_for_each_entry(s, &fstype->fs_supers, s_instances) { if ((S_ISBLK(stat.mode) && s->s_dev == stat.rdev) || (S_ISDIR(stat.mode) && s == nd.dentry->d_inode->i_sb)) { sb = s; @@ -861,7 +854,7 @@ static int gfs2_get_sb_meta(struct file_system_type *fs_type, int flags, error = -ENOENT; goto error; } - sdp = (struct gfs2_sbd*) sb->s_fs_info; + sdp = sb->s_fs_info; if (sdp->sd_vfs_meta) { printk(KERN_WARNING "GFS2: gfs2meta mount already exists\n"); error = -EBUSY; @@ -896,7 +889,10 @@ error: static void gfs2_kill_sb(struct super_block *sb) { - gfs2_delete_debugfs_file(sb->s_fs_info); + if (sb->s_fs_info) { + gfs2_delete_debugfs_file(sb->s_fs_info); + gfs2_meta_syncfs(sb->s_fs_info); + } kill_block_super(sb); } diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 911c115b5c6..291f0c7eaa3 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -69,7 +69,7 @@ static int gfs2_create(struct inode *dir, struct dentry *dentry, mark_inode_dirty(inode); break; } else if (PTR_ERR(inode) != -EEXIST || - (nd->intent.open.flags & O_EXCL)) { + (nd && (nd->intent.open.flags & O_EXCL))) { gfs2_holder_uninit(ghs); return PTR_ERR(inode); } @@ -278,17 +278,25 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2); - error = gfs2_glock_nq_m(3, ghs); + error = gfs2_glock_nq(ghs); /* parent */ if (error) - goto out; + goto out_parent; + + error = gfs2_glock_nq(ghs + 1); /* child */ + if (error) + goto out_child; + + error = gfs2_glock_nq(ghs + 2); /* rgrp */ + if (error) + goto out_rgrp; error = gfs2_unlink_ok(dip, &dentry->d_name, ip); if (error) - goto out_gunlock; + goto out_rgrp; error = gfs2_trans_begin(sdp, 2*RES_DINODE + RES_LEAF + RES_RG_BIT, 0); if (error) - goto out_gunlock; + goto out_rgrp; error = gfs2_dir_del(dip, &dentry->d_name); if (error) @@ -298,12 +306,15 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) out_end_trans: gfs2_trans_end(sdp); -out_gunlock: - gfs2_glock_dq_m(3, ghs); -out: - gfs2_holder_uninit(ghs); - gfs2_holder_uninit(ghs + 1); + gfs2_glock_dq(ghs + 2); +out_rgrp: gfs2_holder_uninit(ghs + 2); + gfs2_glock_dq(ghs + 1); +out_child: + gfs2_holder_uninit(ghs + 1); + gfs2_glock_dq(ghs); +out_parent: + gfs2_holder_uninit(ghs); gfs2_glock_dq_uninit(&ri_gh); return error; } @@ -894,12 +905,17 @@ static int gfs2_permission(struct inode *inode, int mask, struct nameidata *nd) static int setattr_size(struct inode *inode, struct iattr *attr) { struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); int error; if (attr->ia_size != ip->i_di.di_size) { - error = vmtruncate(inode, attr->ia_size); + error = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); if (error) return error; + error = vmtruncate(inode, attr->ia_size); + gfs2_trans_end(sdp); + if (error) + return error; } error = gfs2_truncatei(ip, attr->ia_size); diff --git a/fs/gfs2/ops_super.c b/fs/gfs2/ops_super.c index 603d940f115..950f31460e8 100644 --- a/fs/gfs2/ops_super.c +++ b/fs/gfs2/ops_super.c @@ -92,7 +92,6 @@ static void gfs2_put_super(struct super_block *sb) kthread_stop(sdp->sd_recoverd_process); while (sdp->sd_glockd_num--) kthread_stop(sdp->sd_glockd_process[sdp->sd_glockd_num]); - kthread_stop(sdp->sd_scand_process); if (!(sb->s_flags & MS_RDONLY)) { error = gfs2_make_fs_ro(sdp); @@ -456,12 +455,15 @@ static void gfs2_delete_inode(struct inode *inode) } error = gfs2_dinode_dealloc(ip); - /* - * Must do this before unlock to avoid trying to write back - * potentially dirty data now that inode no longer exists - * on disk. - */ + if (error) + goto out_unlock; + + error = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); + if (error) + goto out_unlock; + /* Needs to be done before glock release & also in a transaction */ truncate_inode_pages(&inode->i_data, 0); + gfs2_trans_end(sdp); out_unlock: gfs2_glock_dq(&ip->i_iopen_gh); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 6e546ee8f3d..addb51e0f13 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -70,6 +70,7 @@ struct gfs2_quota_host { u64 qu_limit; u64 qu_warn; s64 qu_value; + u32 qu_ll_next; }; struct gfs2_quota_change_host { @@ -580,6 +581,7 @@ static void gfs2_quota_in(struct gfs2_quota_host *qu, const void *buf) qu->qu_limit = be64_to_cpu(str->qu_limit); qu->qu_warn = be64_to_cpu(str->qu_warn); qu->qu_value = be64_to_cpu(str->qu_value); + qu->qu_ll_next = be32_to_cpu(str->qu_ll_next); } static void gfs2_quota_out(const struct gfs2_quota_host *qu, void *buf) @@ -589,6 +591,7 @@ static void gfs2_quota_out(const struct gfs2_quota_host *qu, void *buf) str->qu_limit = cpu_to_be64(qu->qu_limit); str->qu_warn = cpu_to_be64(qu->qu_warn); str->qu_value = cpu_to_be64(qu->qu_value); + str->qu_ll_next = cpu_to_be32(qu->qu_ll_next); memset(&str->qu_reserved, 0, sizeof(str->qu_reserved)); } @@ -614,6 +617,16 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, s64 value; int err = -EIO; + if (gfs2_is_stuffed(ip)) { + struct gfs2_alloc *al = NULL; + al = gfs2_alloc_get(ip); + /* just request 1 blk */ + al->al_requested = 1; + gfs2_inplace_reserve(ip); + gfs2_unstuff_dinode(ip, NULL); + gfs2_inplace_release(ip); + gfs2_alloc_put(ip); + } page = grab_cache_page(mapping, index); if (!page) return -ENOMEM; diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 5ada38c99a2..beb6c7ac008 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -469,7 +469,7 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd) }; error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, - LM_FLAG_NOEXP, &ji_gh); + LM_FLAG_NOEXP | GL_NOCACHE, &ji_gh); if (error) goto fail_gunlock_j; } else { diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index ce48c4594ec..708c287e1d0 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -31,6 +31,7 @@ #include "inode.h" #define BFITNOENT ((u32)~0) +#define NO_BLOCK ((u64)~0) /* * These routines are used by the resource group routines (rgrp.c) @@ -116,8 +117,7 @@ static unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd, unsigned char *buffer, * @buffer: the buffer that holds the bitmaps * @buflen: the length (in bytes) of the buffer * @goal: start search at this block's bit-pair (within @buffer) - * @old_state: GFS2_BLKST_XXX the state of the block we're looking for; - * bit 0 = alloc(1)/free(0), bit 1 = meta(1)/data(0) + * @old_state: GFS2_BLKST_XXX the state of the block we're looking for. * * Scope of @goal and returned block number is only within this bitmap buffer, * not entire rgrp or filesystem. @buffer will be offset from the actual @@ -137,9 +137,13 @@ static u32 gfs2_bitfit(struct gfs2_rgrpd *rgd, unsigned char *buffer, byte = buffer + (goal / GFS2_NBBY); bit = (goal % GFS2_NBBY) * GFS2_BIT_SIZE; end = buffer + buflen; - alloc = (old_state & 1) ? 0 : 0x55; + alloc = (old_state == GFS2_BLKST_FREE) ? 0x55 : 0; while (byte < end) { + /* If we're looking for a free block we can eliminate all + bitmap settings with 0x55, which represents four data + blocks in a row. If we're looking for a data block, we can + eliminate 0x00 which corresponds to four free blocks. */ if ((*byte & 0x55) == alloc) { blk += (8 - bit) >> 1; @@ -859,23 +863,28 @@ static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_alloc *al) static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked) { struct inode *inode; - u32 goal = 0; + u32 goal = 0, block; u64 no_addr; + struct gfs2_sbd *sdp = rgd->rd_sbd; for(;;) { if (goal >= rgd->rd_data) break; - goal = rgblk_search(rgd, goal, GFS2_BLKST_UNLINKED, - GFS2_BLKST_UNLINKED); - if (goal == BFITNOENT) + down_write(&sdp->sd_log_flush_lock); + block = rgblk_search(rgd, goal, GFS2_BLKST_UNLINKED, + GFS2_BLKST_UNLINKED); + up_write(&sdp->sd_log_flush_lock); + if (block == BFITNOENT) break; - no_addr = goal + rgd->rd_data0; + /* rgblk_search can return a block < goal, so we need to + keep it marching forward. */ + no_addr = block + rgd->rd_data0; goal++; - if (no_addr < *last_unlinked) + if (*last_unlinked != NO_BLOCK && no_addr <= *last_unlinked) continue; *last_unlinked = no_addr; inode = gfs2_inode_lookup(rgd->rd_sbd->sd_vfs, DT_UNKNOWN, - no_addr, -1); + no_addr, -1, 1); if (!IS_ERR(inode)) return inode; } @@ -1152,7 +1161,7 @@ int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, unsigned int line) struct gfs2_alloc *al = &ip->i_alloc; struct inode *inode; int error = 0; - u64 last_unlinked = 0; + u64 last_unlinked = NO_BLOCK; if (gfs2_assert_warn(sdp, al->al_requested)) return -EINVAL; @@ -1289,7 +1298,9 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, allocatable block anywhere else, we want to be able wrap around and search in the first part of our first-searched bit block. */ for (x = 0; x <= length; x++) { - if (bi->bi_clone) + /* The GFS2_BLKST_UNLINKED state doesn't apply to the clone + bitmaps, so we must search the originals for that. */ + if (old_state != GFS2_BLKST_UNLINKED && bi->bi_clone) blk = gfs2_bitfit(rgd, bi->bi_clone + bi->bi_offset, bi->bi_len, goal, old_state); else @@ -1305,9 +1316,7 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, goal = 0; } - if (old_state != new_state) { - gfs2_assert_withdraw(rgd->rd_sbd, blk != BFITNOENT); - + if (blk != BFITNOENT && old_state != new_state) { gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1); gfs2_setbit(rgd, bi->bi_bh->b_data + bi->bi_offset, bi->bi_len, blk, new_state); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index a2da76b5ae4..dd3e737f528 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -58,7 +58,6 @@ void gfs2_tune_init(struct gfs2_tune *gt) gt->gt_incore_log_blocks = 1024; gt->gt_log_flush_secs = 60; gt->gt_jindex_refresh_secs = 60; - gt->gt_scand_secs = 15; gt->gt_recoverd_secs = 60; gt->gt_logd_secs = 1; gt->gt_quotad_secs = 5; diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index c26c21b53c1..ba3a1729cc1 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -442,7 +442,6 @@ TUNE_ATTR(quota_simul_sync, 1); TUNE_ATTR(quota_cache_secs, 1); TUNE_ATTR(stall_secs, 1); TUNE_ATTR(statfs_quantum, 1); -TUNE_ATTR_DAEMON(scand_secs, scand_process); TUNE_ATTR_DAEMON(recoverd_secs, recoverd_process); TUNE_ATTR_DAEMON(logd_secs, logd_process); TUNE_ATTR_DAEMON(quotad_secs, quotad_process); @@ -464,7 +463,6 @@ static struct attribute *tune_attrs[] = { &tune_attr_quota_cache_secs.attr, &tune_attr_stall_secs.attr, &tune_attr_statfs_quantum.attr, - &tune_attr_scand_secs.attr, &tune_attr_recoverd_secs.attr, &tune_attr_logd_secs.attr, &tune_attr_quotad_secs.attr, diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c index f8dabf8446b..717983e2c2a 100644 --- a/fs/gfs2/trans.c +++ b/fs/gfs2/trans.c @@ -142,25 +142,25 @@ void gfs2_trans_add_bh(struct gfs2_glock *gl, struct buffer_head *bh, int meta) lops_add(sdp, &bd->bd_le); } -void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, u64 blkno) +void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd) { - struct gfs2_revoke *rv = kmalloc(sizeof(struct gfs2_revoke), - GFP_NOFS | __GFP_NOFAIL); - lops_init_le(&rv->rv_le, &gfs2_revoke_lops); - rv->rv_blkno = blkno; - lops_add(sdp, &rv->rv_le); + BUG_ON(!list_empty(&bd->bd_le.le_list)); + BUG_ON(!list_empty(&bd->bd_ail_st_list)); + BUG_ON(!list_empty(&bd->bd_ail_gl_list)); + lops_init_le(&bd->bd_le, &gfs2_revoke_lops); + lops_add(sdp, &bd->bd_le); } void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno) { - struct gfs2_revoke *rv; + struct gfs2_bufdata *bd; int found = 0; gfs2_log_lock(sdp); - list_for_each_entry(rv, &sdp->sd_log_le_revoke, rv_le.le_list) { - if (rv->rv_blkno == blkno) { - list_del(&rv->rv_le.le_list); + list_for_each_entry(bd, &sdp->sd_log_le_revoke, bd_le.le_list) { + if (bd->bd_blkno == blkno) { + list_del_init(&bd->bd_le.le_list); gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke); sdp->sd_log_num_revoke--; found = 1; @@ -172,7 +172,7 @@ void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno) if (found) { struct gfs2_trans *tr = current->journal_info; - kfree(rv); + kmem_cache_free(gfs2_bufdata_cachep, bd); tr->tr_num_revoke_rm++; } } diff --git a/fs/gfs2/trans.h b/fs/gfs2/trans.h index 23d4cbe1de5..043d5f4b9c4 100644 --- a/fs/gfs2/trans.h +++ b/fs/gfs2/trans.h @@ -32,7 +32,7 @@ void gfs2_trans_end(struct gfs2_sbd *sdp); void gfs2_trans_add_gl(struct gfs2_glock *gl); void gfs2_trans_add_bh(struct gfs2_glock *gl, struct buffer_head *bh, int meta); -void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, u64 blkno); +void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd); void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno); void gfs2_trans_add_rg(struct gfs2_rgrpd *rgd); diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog index af4ef808fa9..345798ebd36 100644 --- a/fs/ntfs/ChangeLog +++ b/fs/ntfs/ChangeLog @@ -17,6 +17,18 @@ ToDo/Notes: happen is unclear however so it is worth waiting until someone hits the problem. +2.1.29 - Fix a deadlock at mount time. + + - During mount the VFS holds s_umount lock on the superblock. So when + we try to empty the journal $LogFile contents by calling + ntfs_attr_set() when the machine does not have much memory and the + journal is large ntfs_attr_set() results in the VM trying to balance + dirty pages which in turn tries to that the s_umount lock and thus we + get a deadlock. The solution is to not use ntfs_attr_set() and + instead do the zeroing by hand at the block level rather than page + cache level. + - Fix sparse warnings. + 2.1.28 - Fix a deadlock. - Fix deadlock in fs/ntfs/inode.c::ntfs_put_inode(). Thanks to Sergey diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index 82550838556..58b6be99254 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile @@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \ index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \ unistr.o upcase.o -EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.28\" +EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.29\" ifeq ($(CONFIG_NTFS_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 6e5c2534f4b..cfdc7900d27 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -2,7 +2,7 @@ * aops.c - NTFS kernel address space operations and page cache handling. * Part of the Linux-NTFS project. * - * Copyright (c) 2001-2006 Anton Altaparmakov + * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (c) 2002 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -396,7 +396,7 @@ static int ntfs_readpage(struct file *file, struct page *page) loff_t i_size; struct inode *vi; ntfs_inode *ni, *base_ni; - u8 *kaddr; + u8 *addr; ntfs_attr_search_ctx *ctx; MFT_RECORD *mrec; unsigned long flags; @@ -491,15 +491,15 @@ retry_readpage: /* Race with shrinking truncate. */ attr_len = i_size; } - kaddr = kmap_atomic(page, KM_USER0); + addr = kmap_atomic(page, KM_USER0); /* Copy the data to the page. */ - memcpy(kaddr, (u8*)ctx->attr + + memcpy(addr, (u8*)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset), attr_len); /* Zero the remainder of the page. */ - memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len); + memset(addr + attr_len, 0, PAGE_CACHE_SIZE - attr_len); flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + kunmap_atomic(addr, KM_USER0); put_unm_err_out: ntfs_attr_put_search_ctx(ctx); unm_err_out: @@ -1344,7 +1344,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) loff_t i_size; struct inode *vi = page->mapping->host; ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi); - char *kaddr; + char *addr; ntfs_attr_search_ctx *ctx = NULL; MFT_RECORD *m = NULL; u32 attr_len; @@ -1484,14 +1484,14 @@ retry_writepage: /* Shrinking cannot fail. */ BUG_ON(err); } - kaddr = kmap_atomic(page, KM_USER0); + addr = kmap_atomic(page, KM_USER0); /* Copy the data from the page to the mft record. */ memcpy((u8*)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset), - kaddr, attr_len); + addr, attr_len); /* Zero out of bounds area in the page cache page. */ - memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len); - kunmap_atomic(kaddr, KM_USER0); + memset(addr + attr_len, 0, PAGE_CACHE_SIZE - attr_len); + kunmap_atomic(addr, KM_USER0); flush_dcache_page(page); flush_dcache_mft_record_page(ctx->ntfs_ino); /* We are done with the page. */ diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 1c08fefe487..92dabdcf2b8 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -1,7 +1,7 @@ /** * attrib.c - NTFS attribute operations. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2006 Anton Altaparmakov + * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (c) 2002 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -2500,7 +2500,7 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val) struct page *page; u8 *kaddr; pgoff_t idx, end; - unsigned int start_ofs, end_ofs, size; + unsigned start_ofs, end_ofs, size; ntfs_debug("Entering for ofs 0x%llx, cnt 0x%llx, val 0x%hx.", (long long)ofs, (long long)cnt, val); @@ -2548,6 +2548,8 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val) kunmap_atomic(kaddr, KM_USER0); set_page_dirty(page); page_cache_release(page); + balance_dirty_pages_ratelimited(mapping); + cond_resched(); if (idx == end) goto done; idx++; @@ -2604,6 +2606,8 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val) kunmap_atomic(kaddr, KM_USER0); set_page_dirty(page); page_cache_release(page); + balance_dirty_pages_ratelimited(mapping); + cond_resched(); } done: ntfs_debug("Done."); diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index ffcc504a166..c814204d4ea 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -1,7 +1,7 @@ /* * file.c - NTFS kernel file operations. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2006 Anton Altaparmakov + * Copyright (c) 2001-2007 Anton Altaparmakov * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -26,7 +26,6 @@ #include <linux/swap.h> #include <linux/uio.h> #include <linux/writeback.h> -#include <linux/sched.h> #include <asm/page.h> #include <asm/uaccess.h> @@ -362,7 +361,7 @@ static inline void ntfs_fault_in_pages_readable(const char __user *uaddr, volatile char c; /* Set @end to the first byte outside the last page we care about. */ - end = (const char __user*)PAGE_ALIGN((ptrdiff_t __user)uaddr + bytes); + end = (const char __user*)PAGE_ALIGN((unsigned long)uaddr + bytes); while (!__get_user(c, uaddr) && (uaddr += PAGE_SIZE, uaddr < end)) ; @@ -532,7 +531,8 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages, blocksize_bits = vol->sb->s_blocksize_bits; u = 0; do { - struct page *page = pages[u]; + page = pages[u]; + BUG_ON(!page); /* * create_empty_buffers() will create uptodate/dirty buffers if * the page is uptodate/dirty. @@ -1291,7 +1291,7 @@ static inline size_t ntfs_copy_from_user(struct page **pages, size_t bytes) { struct page **last_page = pages + nr_pages; - char *kaddr; + char *addr; size_t total = 0; unsigned len; int left; @@ -1300,13 +1300,13 @@ static inline size_t ntfs_copy_from_user(struct page **pages, len = PAGE_CACHE_SIZE - ofs; if (len > bytes) len = bytes; - kaddr = kmap_atomic(*pages, KM_USER0); - left = __copy_from_user_inatomic(kaddr + ofs, buf, len); - kunmap_atomic(kaddr, KM_USER0); + addr = kmap_atomic(*pages, KM_USER0); + left = __copy_from_user_inatomic(addr + ofs, buf, len); + kunmap_atomic(addr, KM_USER0); if (unlikely(left)) { /* Do it the slow way. */ - kaddr = kmap(*pages); - left = __copy_from_user(kaddr + ofs, buf, len); + addr = kmap(*pages); + left = __copy_from_user(addr + ofs, buf, len); kunmap(*pages); if (unlikely(left)) goto err_out; @@ -1408,26 +1408,26 @@ static inline size_t ntfs_copy_from_user_iovec(struct page **pages, size_t *iov_ofs, size_t bytes) { struct page **last_page = pages + nr_pages; - char *kaddr; + char *addr; size_t copied, len, total = 0; do { len = PAGE_CACHE_SIZE - ofs; if (len > bytes) len = bytes; - kaddr = kmap_atomic(*pages, KM_USER0); - copied = __ntfs_copy_from_user_iovec_inatomic(kaddr + ofs, + addr = kmap_atomic(*pages, KM_USER0); + copied = __ntfs_copy_from_user_iovec_inatomic(addr + ofs, *iov, *iov_ofs, len); - kunmap_atomic(kaddr, KM_USER0); + kunmap_atomic(addr, KM_USER0); if (unlikely(copied != len)) { /* Do it the slow way. */ - kaddr = kmap(*pages); - copied = __ntfs_copy_from_user_iovec_inatomic(kaddr + ofs, + addr = kmap(*pages); + copied = __ntfs_copy_from_user_iovec_inatomic(addr + ofs, *iov, *iov_ofs, len); /* * Zero the rest of the target like __copy_from_user(). */ - memset(kaddr + ofs + copied, 0, len - copied); + memset(addr + ofs + copied, 0, len - copied); kunmap(*pages); if (unlikely(copied != len)) goto err_out; @@ -1735,8 +1735,6 @@ static int ntfs_commit_pages_after_write(struct page **pages, read_unlock_irqrestore(&ni->size_lock, flags); BUG_ON(initialized_size != i_size); if (end > initialized_size) { - unsigned long flags; - write_lock_irqsave(&ni->size_lock, flags); ni->initialized_size = end; i_size_write(vi, end); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index b532a730cec..e9da092e277 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -34,7 +34,6 @@ #include "dir.h" #include "debug.h" #include "inode.h" -#include "attrib.h" #include "lcnalloc.h" #include "malloc.h" #include "mft.h" @@ -2500,8 +2499,6 @@ retry_truncate: /* Resize the attribute record to best fit the new attribute size. */ if (new_size < vol->mft_record_size && !ntfs_resident_attr_value_resize(m, a, new_size)) { - unsigned long flags; - /* The resize succeeded! */ flush_dcache_mft_record_page(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino); diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c index acfed325f4e..d7932e95b1f 100644 --- a/fs/ntfs/logfile.c +++ b/fs/ntfs/logfile.c @@ -1,7 +1,7 @@ /* * logfile.c - NTFS kernel journal handling. Part of the Linux-NTFS project. * - * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2007 Anton Altaparmakov * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -724,24 +724,139 @@ bool ntfs_is_logfile_clean(struct inode *log_vi, const RESTART_PAGE_HEADER *rp) */ bool ntfs_empty_logfile(struct inode *log_vi) { - ntfs_volume *vol = NTFS_SB(log_vi->i_sb); + VCN vcn, end_vcn; + ntfs_inode *log_ni = NTFS_I(log_vi); + ntfs_volume *vol = log_ni->vol; + struct super_block *sb = vol->sb; + runlist_element *rl; + unsigned long flags; + unsigned block_size, block_size_bits; + int err; + bool should_wait = true; ntfs_debug("Entering."); - if (!NVolLogFileEmpty(vol)) { - int err; - - err = ntfs_attr_set(NTFS_I(log_vi), 0, i_size_read(log_vi), - 0xff); - if (unlikely(err)) { - ntfs_error(vol->sb, "Failed to fill $LogFile with " - "0xff bytes (error code %i).", err); - return false; - } - /* Set the flag so we do not have to do it again on remount. */ - NVolSetLogFileEmpty(vol); + if (NVolLogFileEmpty(vol)) { + ntfs_debug("Done."); + return true; } + /* + * We cannot use ntfs_attr_set() because we may be still in the middle + * of a mount operation. Thus we do the emptying by hand by first + * zapping the page cache pages for the $LogFile/$DATA attribute and + * then emptying each of the buffers in each of the clusters specified + * by the runlist by hand. + */ + block_size = sb->s_blocksize; + block_size_bits = sb->s_blocksize_bits; + vcn = 0; + read_lock_irqsave(&log_ni->size_lock, flags); + end_vcn = (log_ni->initialized_size + vol->cluster_size_mask) >> + vol->cluster_size_bits; + read_unlock_irqrestore(&log_ni->size_lock, flags); + truncate_inode_pages(log_vi->i_mapping, 0); + down_write(&log_ni->runlist.lock); + rl = log_ni->runlist.rl; + if (unlikely(!rl || vcn < rl->vcn || !rl->length)) { +map_vcn: + err = ntfs_map_runlist_nolock(log_ni, vcn, NULL); + if (err) { + ntfs_error(sb, "Failed to map runlist fragment (error " + "%d).", -err); + goto err; + } + rl = log_ni->runlist.rl; + BUG_ON(!rl || vcn < rl->vcn || !rl->length); + } + /* Seek to the runlist element containing @vcn. */ + while (rl->length && vcn >= rl[1].vcn) + rl++; + do { + LCN lcn; + sector_t block, end_block; + s64 len; + + /* + * If this run is not mapped map it now and start again as the + * runlist will have been updated. + */ + lcn = rl->lcn; + if (unlikely(lcn == LCN_RL_NOT_MAPPED)) { + vcn = rl->vcn; + goto map_vcn; + } + /* If this run is not valid abort with an error. */ + if (unlikely(!rl->length || lcn < LCN_HOLE)) + goto rl_err; + /* Skip holes. */ + if (lcn == LCN_HOLE) + continue; + block = lcn << vol->cluster_size_bits >> block_size_bits; + len = rl->length; + if (rl[1].vcn > end_vcn) + len = end_vcn - rl->vcn; + end_block = (lcn + len) << vol->cluster_size_bits >> + block_size_bits; + /* Iterate over the blocks in the run and empty them. */ + do { + struct buffer_head *bh; + + /* Obtain the buffer, possibly not uptodate. */ + bh = sb_getblk(sb, block); + BUG_ON(!bh); + /* Setup buffer i/o submission. */ + lock_buffer(bh); + bh->b_end_io = end_buffer_write_sync; + get_bh(bh); + /* Set the entire contents of the buffer to 0xff. */ + memset(bh->b_data, -1, block_size); + if (!buffer_uptodate(bh)) + set_buffer_uptodate(bh); + if (buffer_dirty(bh)) + clear_buffer_dirty(bh); + /* + * Submit the buffer and wait for i/o to complete but + * only for the first buffer so we do not miss really + * serious i/o errors. Once the first buffer has + * completed ignore errors afterwards as we can assume + * that if one buffer worked all of them will work. + */ + submit_bh(WRITE, bh); + if (should_wait) { + should_wait = false; + wait_on_buffer(bh); + if (unlikely(!buffer_uptodate(bh))) + goto io_err; + } + brelse(bh); + } while (++block < end_block); + } while ((++rl)->vcn < end_vcn); + up_write(&log_ni->runlist.lock); + /* + * Zap the pages again just in case any got instantiated whilst we were + * emptying the blocks by hand. FIXME: We may not have completed + * writing to all the buffer heads yet so this may happen too early. + * We really should use a kernel thread to do the emptying + * asynchronously and then we can also set the volume dirty and output + * an error message if emptying should fail. + */ + truncate_inode_pages(log_vi->i_mapping, 0); + /* Set the flag so we do not have to do it again on remount. */ + NVolSetLogFileEmpty(vol); ntfs_debug("Done."); return true; +io_err: + ntfs_error(sb, "Failed to write buffer. Unmount and run chkdsk."); + goto dirty_err; +rl_err: + ntfs_error(sb, "Runlist is corrupt. Unmount and run chkdsk."); +dirty_err: + NVolSetErrors(vol); + err = -EIO; +err: + up_write(&log_ni->runlist.lock); + ntfs_error(sb, "Failed to fill $LogFile with 0xff bytes (error %d).", + -err); + return false; } #endif /* NTFS_RW */ diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index 9afd72c7ad0..56a9a6d25a2 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -1,7 +1,7 @@ /** * runlist.c - NTFS runlist handling code. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2005 Anton Altaparmakov + * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (c) 2002-2005 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -1714,7 +1714,7 @@ extend_hole: sizeof(*rl)); /* Adjust the beginning of the tail if necessary. */ if (end > rl->vcn) { - s64 delta = end - rl->vcn; + delta = end - rl->vcn; rl->vcn = end; rl->length -= delta; /* Only adjust the lcn if it is real. */ diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 778a850b463..4ba7f0bdc24 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -354,7 +354,6 @@ struct ocfs2_insert_type { enum ocfs2_append_type ins_appending; enum ocfs2_contig_type ins_contig; int ins_contig_index; - int ins_free_records; int ins_tree_depth; }; @@ -362,7 +361,6 @@ struct ocfs2_merge_ctxt { enum ocfs2_contig_type c_contig_type; int c_has_empty_extent; int c_split_covers_rec; - int c_used_tail_recs; }; /* @@ -2808,36 +2806,28 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, struct ocfs2_merge_ctxt *ctxt) { - int ret = 0, delete_tail_recs = 0; + int ret = 0; struct ocfs2_extent_list *el = path_leaf_el(left_path); struct ocfs2_extent_rec *rec = &el->l_recs[split_index]; BUG_ON(ctxt->c_contig_type == CONTIG_NONE); - if (ctxt->c_split_covers_rec) { - delete_tail_recs++; - - if (ctxt->c_contig_type == CONTIG_LEFTRIGHT || - ctxt->c_has_empty_extent) - delete_tail_recs++; - - if (ctxt->c_has_empty_extent) { - /* - * The merge code will need to create an empty - * extent to take the place of the newly - * emptied slot. Remove any pre-existing empty - * extents - having more than one in a leaf is - * illegal. - */ - ret = ocfs2_rotate_tree_left(inode, handle, left_path, - dealloc); - if (ret) { - mlog_errno(ret); - goto out; - } - split_index--; - rec = &el->l_recs[split_index]; + if (ctxt->c_split_covers_rec && ctxt->c_has_empty_extent) { + /* + * The merge code will need to create an empty + * extent to take the place of the newly + * emptied slot. Remove any pre-existing empty + * extents - having more than one in a leaf is + * illegal. + */ + ret = ocfs2_rotate_tree_left(inode, handle, left_path, + dealloc); + if (ret) { + mlog_errno(ret); + goto out; } + split_index--; + rec = &el->l_recs[split_index]; } if (ctxt->c_contig_type == CONTIG_LEFTRIGHT) { @@ -3593,6 +3583,7 @@ static int ocfs2_figure_insert_type(struct inode *inode, struct buffer_head *di_bh, struct buffer_head **last_eb_bh, struct ocfs2_extent_rec *insert_rec, + int *free_records, struct ocfs2_insert_type *insert) { int ret; @@ -3633,7 +3624,7 @@ static int ocfs2_figure_insert_type(struct inode *inode, * XXX: This test is simplistic, we can search for empty * extent records too. */ - insert->ins_free_records = le16_to_cpu(el->l_count) - + *free_records = le16_to_cpu(el->l_count) - le16_to_cpu(el->l_next_free_rec); if (!insert->ins_tree_depth) { @@ -3730,10 +3721,13 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, struct ocfs2_alloc_context *meta_ac) { int status; + int uninitialized_var(free_records); struct buffer_head *last_eb_bh = NULL; struct ocfs2_insert_type insert = {0, }; struct ocfs2_extent_rec rec; + BUG_ON(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL); + mlog(0, "add %u clusters at position %u to inode %llu\n", new_clusters, cpos, (unsigned long long)OCFS2_I(inode)->ip_blkno); @@ -3752,7 +3746,7 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, rec.e_flags = flags; status = ocfs2_figure_insert_type(inode, fe_bh, &last_eb_bh, &rec, - &insert); + &free_records, &insert); if (status < 0) { mlog_errno(status); goto bail; @@ -3762,9 +3756,9 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, "Insert.contig_index: %d, Insert.free_records: %d, " "Insert.tree_depth: %d\n", insert.ins_appending, insert.ins_contig, insert.ins_contig_index, - insert.ins_free_records, insert.ins_tree_depth); + free_records, insert.ins_tree_depth); - if (insert.ins_contig == CONTIG_NONE && insert.ins_free_records == 0) { + if (insert.ins_contig == CONTIG_NONE && free_records == 0) { status = ocfs2_grow_tree(inode, handle, fe_bh, &insert.ins_tree_depth, &last_eb_bh, meta_ac); @@ -3847,26 +3841,17 @@ leftright: if (le16_to_cpu(rightmost_el->l_next_free_rec) == le16_to_cpu(rightmost_el->l_count)) { - int old_depth = depth; - ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, last_eb_bh, meta_ac); if (ret) { mlog_errno(ret); goto out; } - - if (old_depth != depth) { - eb = (struct ocfs2_extent_block *)(*last_eb_bh)->b_data; - rightmost_el = &eb->h_list; - } } memset(&insert, 0, sizeof(struct ocfs2_insert_type)); insert.ins_appending = APPEND_NONE; insert.ins_contig = CONTIG_NONE; - insert.ins_free_records = le16_to_cpu(rightmost_el->l_count) - - le16_to_cpu(rightmost_el->l_next_free_rec); insert.ins_tree_depth = depth; insert_range = le32_to_cpu(split_rec.e_cpos) + @@ -4015,11 +4000,6 @@ static int __ocfs2_mark_extent_written(struct inode *inode, } else rightmost_el = path_root_el(path); - ctxt.c_used_tail_recs = le16_to_cpu(rightmost_el->l_next_free_rec); - if (ctxt.c_used_tail_recs > 0 && - ocfs2_is_empty_extent(&rightmost_el->l_recs[0])) - ctxt.c_used_tail_recs--; - if (rec->e_cpos == split_rec->e_cpos && rec->e_leaf_clusters == split_rec->e_leaf_clusters) ctxt.c_split_covers_rec = 1; @@ -4028,10 +4008,9 @@ static int __ocfs2_mark_extent_written(struct inode *inode, ctxt.c_has_empty_extent = ocfs2_is_empty_extent(&el->l_recs[0]); - mlog(0, "index: %d, contig: %u, used_tail_recs: %u, " - "has_empty: %u, split_covers: %u\n", split_index, - ctxt.c_contig_type, ctxt.c_used_tail_recs, - ctxt.c_has_empty_extent, ctxt.c_split_covers_rec); + mlog(0, "index: %d, contig: %u, has_empty: %u, split_covers: %u\n", + split_index, ctxt.c_contig_type, ctxt.c_has_empty_extent, + ctxt.c_split_covers_rec); if (ctxt.c_contig_type == CONTIG_NONE) { if (ctxt.c_split_covers_rec) @@ -4180,27 +4159,18 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, if (le16_to_cpu(rightmost_el->l_next_free_rec) == le16_to_cpu(rightmost_el->l_count)) { - int old_depth = depth; - ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, &last_eb_bh, meta_ac); if (ret) { mlog_errno(ret); goto out; } - - if (old_depth != depth) { - eb = (struct ocfs2_extent_block *)last_eb_bh->b_data; - rightmost_el = &eb->h_list; - } } memset(&insert, 0, sizeof(struct ocfs2_insert_type)); insert.ins_appending = APPEND_NONE; insert.ins_contig = CONTIG_NONE; insert.ins_split = SPLIT_RIGHT; - insert.ins_free_records = le16_to_cpu(rightmost_el->l_count) - - le16_to_cpu(rightmost_el->l_next_free_rec); insert.ins_tree_depth = depth; ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec, &insert); @@ -5665,12 +5635,50 @@ static int ocfs2_ordered_zero_func(handle_t *handle, struct buffer_head *bh) return ocfs2_journal_dirty_data(handle, bh); } +static void ocfs2_map_and_dirty_page(struct inode *inode, handle_t *handle, + unsigned int from, unsigned int to, + struct page *page, int zero, u64 *phys) +{ + int ret, partial = 0; + + ret = ocfs2_map_page_blocks(page, phys, inode, from, to, 0); + if (ret) + mlog_errno(ret); + + if (zero) + zero_user_page(page, from, to - from, KM_USER0); + + /* + * Need to set the buffers we zero'd into uptodate + * here if they aren't - ocfs2_map_page_blocks() + * might've skipped some + */ + if (ocfs2_should_order_data(inode)) { + ret = walk_page_buffers(handle, + page_buffers(page), + from, to, &partial, + ocfs2_ordered_zero_func); + if (ret < 0) + mlog_errno(ret); + } else { + ret = walk_page_buffers(handle, page_buffers(page), + from, to, &partial, + ocfs2_writeback_zero_func); + if (ret < 0) + mlog_errno(ret); + } + + if (!partial) + SetPageUptodate(page); + + flush_dcache_page(page); +} + static void ocfs2_zero_cluster_pages(struct inode *inode, loff_t start, loff_t end, struct page **pages, int numpages, u64 phys, handle_t *handle) { - int i, ret, partial = 0; - void *kaddr; + int i; struct page *page; unsigned int from, to = PAGE_CACHE_SIZE; struct super_block *sb = inode->i_sb; @@ -5691,87 +5699,31 @@ static void ocfs2_zero_cluster_pages(struct inode *inode, loff_t start, BUG_ON(from > PAGE_CACHE_SIZE); BUG_ON(to > PAGE_CACHE_SIZE); - ret = ocfs2_map_page_blocks(page, &phys, inode, from, to, 0); - if (ret) - mlog_errno(ret); - - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + from, 0, to - from); - kunmap_atomic(kaddr, KM_USER0); - - /* - * Need to set the buffers we zero'd into uptodate - * here if they aren't - ocfs2_map_page_blocks() - * might've skipped some - */ - if (ocfs2_should_order_data(inode)) { - ret = walk_page_buffers(handle, - page_buffers(page), - from, to, &partial, - ocfs2_ordered_zero_func); - if (ret < 0) - mlog_errno(ret); - } else { - ret = walk_page_buffers(handle, page_buffers(page), - from, to, &partial, - ocfs2_writeback_zero_func); - if (ret < 0) - mlog_errno(ret); - } - - if (!partial) - SetPageUptodate(page); - - flush_dcache_page(page); + ocfs2_map_and_dirty_page(inode, handle, from, to, page, 1, + &phys); start = (page->index + 1) << PAGE_CACHE_SHIFT; } out: - if (pages) { - for (i = 0; i < numpages; i++) { - page = pages[i]; - unlock_page(page); - mark_page_accessed(page); - page_cache_release(page); - } - } + if (pages) + ocfs2_unlock_and_free_pages(pages, numpages); } static int ocfs2_grab_eof_pages(struct inode *inode, loff_t start, loff_t end, - struct page **pages, int *num, u64 *phys) + struct page **pages, int *num) { - int i, numpages = 0, ret = 0; - unsigned int ext_flags; + int numpages, ret = 0; struct super_block *sb = inode->i_sb; struct address_space *mapping = inode->i_mapping; unsigned long index; loff_t last_page_bytes; - BUG_ON(!ocfs2_sparse_alloc(OCFS2_SB(sb))); BUG_ON(start > end); - if (start == end) - goto out; - BUG_ON(start >> OCFS2_SB(sb)->s_clustersize_bits != (end - 1) >> OCFS2_SB(sb)->s_clustersize_bits); - ret = ocfs2_extent_map_get_blocks(inode, start >> sb->s_blocksize_bits, - phys, NULL, &ext_flags); - if (ret) { - mlog_errno(ret); - goto out; - } - - /* Tail is a hole. */ - if (*phys == 0) - goto out; - - /* Tail is marked as unwritten, we can count on write to zero - * in that case. */ - if (ext_flags & OCFS2_EXT_UNWRITTEN) - goto out; - + numpages = 0; last_page_bytes = PAGE_ALIGN(end); index = start >> PAGE_CACHE_SHIFT; do { @@ -5788,14 +5740,8 @@ static int ocfs2_grab_eof_pages(struct inode *inode, loff_t start, loff_t end, out: if (ret != 0) { - if (pages) { - for (i = 0; i < numpages; i++) { - if (pages[i]) { - unlock_page(pages[i]); - page_cache_release(pages[i]); - } - } - } + if (pages) + ocfs2_unlock_and_free_pages(pages, numpages); numpages = 0; } @@ -5816,18 +5762,20 @@ out: int ocfs2_zero_range_for_truncate(struct inode *inode, handle_t *handle, u64 range_start, u64 range_end) { - int ret, numpages; + int ret = 0, numpages; struct page **pages = NULL; u64 phys; + unsigned int ext_flags; + struct super_block *sb = inode->i_sb; /* * File systems which don't support sparse files zero on every * extend. */ - if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) + if (!ocfs2_sparse_alloc(OCFS2_SB(sb))) return 0; - pages = kcalloc(ocfs2_pages_per_cluster(inode->i_sb), + pages = kcalloc(ocfs2_pages_per_cluster(sb), sizeof(struct page *), GFP_NOFS); if (pages == NULL) { ret = -ENOMEM; @@ -5835,16 +5783,31 @@ int ocfs2_zero_range_for_truncate(struct inode *inode, handle_t *handle, goto out; } - ret = ocfs2_grab_eof_pages(inode, range_start, range_end, pages, - &numpages, &phys); + if (range_start == range_end) + goto out; + + ret = ocfs2_extent_map_get_blocks(inode, + range_start >> sb->s_blocksize_bits, + &phys, NULL, &ext_flags); if (ret) { mlog_errno(ret); goto out; } - if (numpages == 0) + /* + * Tail is a hole, or is marked unwritten. In either case, we + * can count on read and write to return/push zero's. + */ + if (phys == 0 || ext_flags & OCFS2_EXT_UNWRITTEN) goto out; + ret = ocfs2_grab_eof_pages(inode, range_start, range_end, pages, + &numpages); + if (ret) { + mlog_errno(ret); + goto out; + } + ocfs2_zero_cluster_pages(inode, range_start, range_end, pages, numpages, phys, handle); @@ -5865,6 +5828,178 @@ out: return ret; } +static void ocfs2_zero_dinode_id2(struct inode *inode, struct ocfs2_dinode *di) +{ + unsigned int blocksize = 1 << inode->i_sb->s_blocksize_bits; + + memset(&di->id2, 0, blocksize - offsetof(struct ocfs2_dinode, id2)); +} + +void ocfs2_dinode_new_extent_list(struct inode *inode, + struct ocfs2_dinode *di) +{ + ocfs2_zero_dinode_id2(inode, di); + di->id2.i_list.l_tree_depth = 0; + di->id2.i_list.l_next_free_rec = 0; + di->id2.i_list.l_count = cpu_to_le16(ocfs2_extent_recs_per_inode(inode->i_sb)); +} + +void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di) +{ + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_inline_data *idata = &di->id2.i_data; + + spin_lock(&oi->ip_lock); + oi->ip_dyn_features |= OCFS2_INLINE_DATA_FL; + di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); + spin_unlock(&oi->ip_lock); + + /* + * We clear the entire i_data structure here so that all + * fields can be properly initialized. + */ + ocfs2_zero_dinode_id2(inode, di); + + idata->id_count = cpu_to_le16(ocfs2_max_inline_data(inode->i_sb)); +} + +int ocfs2_convert_inline_data_to_extents(struct inode *inode, + struct buffer_head *di_bh) +{ + int ret, i, has_data, num_pages = 0; + handle_t *handle; + u64 uninitialized_var(block); + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_alloc_context *data_ac = NULL; + struct page **pages = NULL; + loff_t end = osb->s_clustersize; + + has_data = i_size_read(inode) ? 1 : 0; + + if (has_data) { + pages = kcalloc(ocfs2_pages_per_cluster(osb->sb), + sizeof(struct page *), GFP_NOFS); + if (pages == NULL) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_reserve_clusters(osb, 1, &data_ac); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + handle = ocfs2_start_trans(osb, OCFS2_INLINE_TO_EXTENTS_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out_unlock; + } + + ret = ocfs2_journal_access(handle, inode, di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + if (has_data) { + u32 bit_off, num; + unsigned int page_end; + u64 phys; + + ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, + &num); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + /* + * Save two copies, one for insert, and one that can + * be changed by ocfs2_map_and_dirty_page() below. + */ + block = phys = ocfs2_clusters_to_blocks(inode->i_sb, bit_off); + + /* + * Non sparse file systems zero on extend, so no need + * to do that now. + */ + if (!ocfs2_sparse_alloc(osb) && + PAGE_CACHE_SIZE < osb->s_clustersize) + end = PAGE_CACHE_SIZE; + + ret = ocfs2_grab_eof_pages(inode, 0, end, pages, &num_pages); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + /* + * This should populate the 1st page for us and mark + * it up to date. + */ + ret = ocfs2_read_inline_data(inode, pages[0], di_bh); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + page_end = PAGE_CACHE_SIZE; + if (PAGE_CACHE_SIZE > osb->s_clustersize) + page_end = osb->s_clustersize; + + for (i = 0; i < num_pages; i++) + ocfs2_map_and_dirty_page(inode, handle, 0, page_end, + pages[i], i > 0, &phys); + } + + spin_lock(&oi->ip_lock); + oi->ip_dyn_features &= ~OCFS2_INLINE_DATA_FL; + di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); + spin_unlock(&oi->ip_lock); + + ocfs2_dinode_new_extent_list(inode, di); + + ocfs2_journal_dirty(handle, di_bh); + + if (has_data) { + /* + * An error at this point should be extremely rare. If + * this proves to be false, we could always re-build + * the in-inode data from our pages. + */ + ret = ocfs2_insert_extent(osb, handle, inode, di_bh, + 0, block, 1, 0, NULL); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + inode->i_blocks = ocfs2_inode_sector_count(inode); + } + +out_commit: + ocfs2_commit_trans(osb, handle); + +out_unlock: + if (data_ac) + ocfs2_free_alloc_context(data_ac); + +out: + if (pages) { + ocfs2_unlock_and_free_pages(pages, num_pages); + kfree(pages); + } + + return ret; +} + /* * It is expected, that by the time you call this function, * inode->i_size and fe->i_size have been adjusted. @@ -6090,6 +6225,81 @@ bail: return status; } +/* + * 'start' is inclusive, 'end' is not. + */ +int ocfs2_truncate_inline(struct inode *inode, struct buffer_head *di_bh, + unsigned int start, unsigned int end, int trunc) +{ + int ret; + unsigned int numbytes; + handle_t *handle; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_inline_data *idata = &di->id2.i_data; + + if (end > i_size_read(inode)) + end = i_size_read(inode); + + BUG_ON(start >= end); + + if (!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) || + !(le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) || + !ocfs2_supports_inline_data(osb)) { + ocfs2_error(inode->i_sb, + "Inline data flags for inode %llu don't agree! " + "Disk: 0x%x, Memory: 0x%x, Superblock: 0x%x\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + le16_to_cpu(di->i_dyn_features), + OCFS2_I(inode)->ip_dyn_features, + osb->s_feature_incompat); + ret = -EROFS; + goto out; + } + + handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + numbytes = end - start; + memset(idata->id_data + start, 0, numbytes); + + /* + * No need to worry about the data page here - it's been + * truncated already and inline data doesn't need it for + * pushing zero's to disk, so we'll let readpage pick it up + * later. + */ + if (trunc) { + i_size_write(inode, start); + di->i_size = cpu_to_le64(start); + } + + inode->i_blocks = ocfs2_inode_sector_count(inode); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + + di->i_ctime = di->i_mtime = cpu_to_le64(inode->i_ctime.tv_sec); + di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); + + ocfs2_journal_dirty(handle, di_bh); + +out_commit: + ocfs2_commit_trans(osb, handle); + +out: + return ret; +} + static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc) { /* diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h index 990df48ae8d..42ff94bd801 100644 --- a/fs/ocfs2/alloc.h +++ b/fs/ocfs2/alloc.h @@ -62,6 +62,11 @@ static inline int ocfs2_extend_meta_needed(struct ocfs2_dinode *fe) return le16_to_cpu(fe->id2.i_list.l_tree_depth) + 2; } +void ocfs2_dinode_new_extent_list(struct inode *inode, struct ocfs2_dinode *di); +void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di); +int ocfs2_convert_inline_data_to_extents(struct inode *inode, + struct buffer_head *di_bh); + int ocfs2_truncate_log_init(struct ocfs2_super *osb); void ocfs2_truncate_log_shutdown(struct ocfs2_super *osb); void ocfs2_schedule_truncate_log_flush(struct ocfs2_super *osb, @@ -115,6 +120,8 @@ int ocfs2_commit_truncate(struct ocfs2_super *osb, struct inode *inode, struct buffer_head *fe_bh, struct ocfs2_truncate_context *tc); +int ocfs2_truncate_inline(struct inode *inode, struct buffer_head *di_bh, + unsigned int start, unsigned int end, int trunc); int ocfs2_find_leaf(struct inode *inode, struct ocfs2_extent_list *root_el, u32 cpos, struct buffer_head **leaf_bh); diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index f37f25c931f..34d10452c56 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -206,9 +206,70 @@ bail: return err; } +int ocfs2_read_inline_data(struct inode *inode, struct page *page, + struct buffer_head *di_bh) +{ + void *kaddr; + unsigned int size; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + + if (!(le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL)) { + ocfs2_error(inode->i_sb, "Inode %llu lost inline data flag", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + return -EROFS; + } + + size = i_size_read(inode); + + if (size > PAGE_CACHE_SIZE || + size > ocfs2_max_inline_data(inode->i_sb)) { + ocfs2_error(inode->i_sb, + "Inode %llu has with inline data has bad size: %u", + (unsigned long long)OCFS2_I(inode)->ip_blkno, size); + return -EROFS; + } + + kaddr = kmap_atomic(page, KM_USER0); + if (size) + memcpy(kaddr, di->id2.i_data.id_data, size); + /* Clear the remaining part of the page */ + memset(kaddr + size, 0, PAGE_CACHE_SIZE - size); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + + SetPageUptodate(page); + + return 0; +} + +static int ocfs2_readpage_inline(struct inode *inode, struct page *page) +{ + int ret; + struct buffer_head *di_bh = NULL; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + BUG_ON(!PageLocked(page)); + BUG_ON(!OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL); + + ret = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &di_bh, + OCFS2_BH_CACHED, inode); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_read_inline_data(inode, page, di_bh); +out: + unlock_page(page); + + brelse(di_bh); + return ret; +} + static int ocfs2_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; + struct ocfs2_inode_info *oi = OCFS2_I(inode); loff_t start = (loff_t)page->index << PAGE_CACHE_SHIFT; int ret, unlock = 1; @@ -222,7 +283,7 @@ static int ocfs2_readpage(struct file *file, struct page *page) goto out; } - if (down_read_trylock(&OCFS2_I(inode)->ip_alloc_sem) == 0) { + if (down_read_trylock(&oi->ip_alloc_sem) == 0) { ret = AOP_TRUNCATED_PAGE; goto out_meta_unlock; } @@ -252,7 +313,10 @@ static int ocfs2_readpage(struct file *file, struct page *page) goto out_alloc; } - ret = block_read_full_page(page, ocfs2_get_block); + if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) + ret = ocfs2_readpage_inline(inode, page); + else + ret = block_read_full_page(page, ocfs2_get_block); unlock = 0; ocfs2_data_unlock(inode, 0); @@ -301,12 +365,8 @@ int ocfs2_prepare_write_nolock(struct inode *inode, struct page *page, { int ret; - down_read(&OCFS2_I(inode)->ip_alloc_sem); - ret = block_prepare_write(page, from, to, ocfs2_get_block); - up_read(&OCFS2_I(inode)->ip_alloc_sem); - return ret; } @@ -401,7 +461,9 @@ static sector_t ocfs2_bmap(struct address_space *mapping, sector_t block) down_read(&OCFS2_I(inode)->ip_alloc_sem); } - err = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL, NULL); + if (!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)) + err = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL, + NULL); if (!INODE_JOURNAL(inode)) { up_read(&OCFS2_I(inode)->ip_alloc_sem); @@ -415,7 +477,6 @@ static sector_t ocfs2_bmap(struct address_space *mapping, sector_t block) goto bail; } - bail: status = err ? 0 : p_blkno; @@ -570,6 +631,13 @@ static ssize_t ocfs2_direct_IO(int rw, mlog_entry_void(); + /* + * Fallback to buffered I/O if we see an inode without + * extents. + */ + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) + return 0; + if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) { /* * We get PR data locks even for O_DIRECT. This @@ -834,18 +902,22 @@ struct ocfs2_write_ctxt { struct ocfs2_cached_dealloc_ctxt w_dealloc; }; -static void ocfs2_free_write_ctxt(struct ocfs2_write_ctxt *wc) +void ocfs2_unlock_and_free_pages(struct page **pages, int num_pages) { int i; - for(i = 0; i < wc->w_num_pages; i++) { - if (wc->w_pages[i] == NULL) - continue; - - unlock_page(wc->w_pages[i]); - mark_page_accessed(wc->w_pages[i]); - page_cache_release(wc->w_pages[i]); + for(i = 0; i < num_pages; i++) { + if (pages[i]) { + unlock_page(pages[i]); + mark_page_accessed(pages[i]); + page_cache_release(pages[i]); + } } +} + +static void ocfs2_free_write_ctxt(struct ocfs2_write_ctxt *wc) +{ + ocfs2_unlock_and_free_pages(wc->w_pages, wc->w_num_pages); brelse(wc->w_di_bh); kfree(wc); @@ -1360,6 +1432,160 @@ out: return ret; } +static int ocfs2_write_begin_inline(struct address_space *mapping, + struct inode *inode, + struct ocfs2_write_ctxt *wc) +{ + int ret; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct page *page; + handle_t *handle; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)wc->w_di_bh->b_data; + + page = find_or_create_page(mapping, 0, GFP_NOFS); + if (!page) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + /* + * If we don't set w_num_pages then this page won't get unlocked + * and freed on cleanup of the write context. + */ + wc->w_pages[0] = wc->w_target_page = page; + wc->w_num_pages = 1; + + handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, wc->w_di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + ocfs2_commit_trans(osb, handle); + + mlog_errno(ret); + goto out; + } + + if (!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)) + ocfs2_set_inode_data_inline(inode, di); + + if (!PageUptodate(page)) { + ret = ocfs2_read_inline_data(inode, page, wc->w_di_bh); + if (ret) { + ocfs2_commit_trans(osb, handle); + + goto out; + } + } + + wc->w_handle = handle; +out: + return ret; +} + +int ocfs2_size_fits_inline_data(struct buffer_head *di_bh, u64 new_size) +{ + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + + if (new_size < le16_to_cpu(di->id2.i_data.id_count)) + return 1; + return 0; +} + +static int ocfs2_try_to_write_inline_data(struct address_space *mapping, + struct inode *inode, loff_t pos, + unsigned len, struct page *mmap_page, + struct ocfs2_write_ctxt *wc) +{ + int ret, written = 0; + loff_t end = pos + len; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + + mlog(0, "Inode %llu, write of %u bytes at off %llu. features: 0x%x\n", + (unsigned long long)oi->ip_blkno, len, (unsigned long long)pos, + oi->ip_dyn_features); + + /* + * Handle inodes which already have inline data 1st. + */ + if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + if (mmap_page == NULL && + ocfs2_size_fits_inline_data(wc->w_di_bh, end)) + goto do_inline_write; + + /* + * The write won't fit - we have to give this inode an + * inline extent list now. + */ + ret = ocfs2_convert_inline_data_to_extents(inode, wc->w_di_bh); + if (ret) + mlog_errno(ret); + goto out; + } + + /* + * Check whether the inode can accept inline data. + */ + if (oi->ip_clusters != 0 || i_size_read(inode) != 0) + return 0; + + /* + * Check whether the write can fit. + */ + if (mmap_page || end > ocfs2_max_inline_data(inode->i_sb)) + return 0; + +do_inline_write: + ret = ocfs2_write_begin_inline(mapping, inode, wc); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * This signals to the caller that the data can be written + * inline. + */ + written = 1; +out: + return written ? written : ret; +} + +/* + * This function only does anything for file systems which can't + * handle sparse files. + * + * What we want to do here is fill in any hole between the current end + * of allocation and the end of our write. That way the rest of the + * write path can treat it as an non-allocating write, which has no + * special case code for sparse/nonsparse files. + */ +static int ocfs2_expand_nonsparse_inode(struct inode *inode, loff_t pos, + unsigned len, + struct ocfs2_write_ctxt *wc) +{ + int ret; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + loff_t newsize = pos + len; + + if (ocfs2_sparse_alloc(osb)) + return 0; + + if (newsize <= i_size_read(inode)) + return 0; + + ret = ocfs2_extend_no_holes(inode, newsize, newsize - len); + if (ret) + mlog_errno(ret); + + return ret; +} + int ocfs2_write_begin_nolock(struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata, @@ -1381,6 +1607,25 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, return ret; } + if (ocfs2_supports_inline_data(osb)) { + ret = ocfs2_try_to_write_inline_data(mapping, inode, pos, len, + mmap_page, wc); + if (ret == 1) { + ret = 0; + goto success; + } + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + ret = ocfs2_expand_nonsparse_inode(inode, pos, len, wc); + if (ret) { + mlog_errno(ret); + goto out; + } + ret = ocfs2_populate_write_desc(inode, wc, &clusters_to_alloc, &extents_to_split); if (ret) { @@ -1462,6 +1707,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, if (meta_ac) ocfs2_free_alloc_context(meta_ac); +success: *pagep = wc->w_target_page; *fsdata = wc; return 0; @@ -1529,6 +1775,31 @@ out_fail: return ret; } +static void ocfs2_write_end_inline(struct inode *inode, loff_t pos, + unsigned len, unsigned *copied, + struct ocfs2_dinode *di, + struct ocfs2_write_ctxt *wc) +{ + void *kaddr; + + if (unlikely(*copied < len)) { + if (!PageUptodate(wc->w_target_page)) { + *copied = 0; + return; + } + } + + kaddr = kmap_atomic(wc->w_target_page, KM_USER0); + memcpy(di->id2.i_data.id_data + pos, kaddr + pos, *copied); + kunmap_atomic(kaddr, KM_USER0); + + mlog(0, "Data written to inode at offset %llu. " + "id_count = %u, copied = %u, i_dyn_features = 0x%x\n", + (unsigned long long)pos, *copied, + le16_to_cpu(di->id2.i_data.id_count), + le16_to_cpu(di->i_dyn_features)); +} + int ocfs2_write_end_nolock(struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata) @@ -1542,6 +1813,11 @@ int ocfs2_write_end_nolock(struct address_space *mapping, handle_t *handle = wc->w_handle; struct page *tmppage; + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + ocfs2_write_end_inline(inode, pos, len, &copied, di, wc); + goto out_write_size; + } + if (unlikely(copied < len)) { if (!PageUptodate(wc->w_target_page)) copied = 0; @@ -1579,6 +1855,7 @@ int ocfs2_write_end_nolock(struct address_space *mapping, block_commit_write(tmppage, from, to); } +out_write_size: pos += copied; if (pos > inode->i_size) { i_size_write(inode, pos); diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index 389579bd64e..113560877db 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -34,6 +34,8 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, struct inode *inode, unsigned int from, unsigned int to, int new); +void ocfs2_unlock_and_free_pages(struct page **pages, int num_pages); + int walk_page_buffers( handle_t *handle, struct buffer_head *head, unsigned from, @@ -59,6 +61,10 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, struct page **pagep, void **fsdata, struct buffer_head *di_bh, struct page *mmap_page); +int ocfs2_read_inline_data(struct inode *inode, struct page *page, + struct buffer_head *di_bh); +int ocfs2_size_fits_inline_data(struct buffer_head *di_bh, u64 new_size); + /* all ocfs2_dio_end_io()'s fault */ #define ocfs2_iocb_is_rw_locked(iocb) \ test_bit(0, (unsigned long *)&iocb->private) diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 0d5fdde959c..7453b70c1a1 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -55,10 +55,16 @@ #include "journal.h" #include "namei.h" #include "suballoc.h" +#include "super.h" #include "uptodate.h" #include "buffer_head_io.h" +#define NAMEI_RA_CHUNKS 2 +#define NAMEI_RA_BLOCKS 4 +#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) +#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) + static unsigned char ocfs2_filetype_table[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK }; @@ -66,12 +72,614 @@ static unsigned char ocfs2_filetype_table[] = { static int ocfs2_extend_dir(struct ocfs2_super *osb, struct inode *dir, struct buffer_head *parent_fe_bh, + unsigned int blocks_wanted, struct buffer_head **new_de_bh); +static int ocfs2_do_extend_dir(struct super_block *sb, + handle_t *handle, + struct inode *dir, + struct buffer_head *parent_fe_bh, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + struct buffer_head **new_bh); + /* - * ocfs2_readdir() + * bh passed here can be an inode block or a dir data block, depending + * on the inode inline data flag. + */ +static int ocfs2_check_dir_entry(struct inode * dir, + struct ocfs2_dir_entry * de, + struct buffer_head * bh, + unsigned long offset) +{ + const char *error_msg = NULL; + const int rlen = le16_to_cpu(de->rec_len); + + if (rlen < OCFS2_DIR_REC_LEN(1)) + error_msg = "rec_len is smaller than minimal"; + else if (rlen % 4 != 0) + error_msg = "rec_len % 4 != 0"; + else if (rlen < OCFS2_DIR_REC_LEN(de->name_len)) + error_msg = "rec_len is too small for name_len"; + else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize) + error_msg = "directory entry across blocks"; + + if (error_msg != NULL) + mlog(ML_ERROR, "bad entry in directory #%llu: %s - " + "offset=%lu, inode=%llu, rec_len=%d, name_len=%d\n", + (unsigned long long)OCFS2_I(dir)->ip_blkno, error_msg, + offset, (unsigned long long)le64_to_cpu(de->inode), rlen, + de->name_len); + return error_msg == NULL ? 1 : 0; +} + +static inline int ocfs2_match(int len, + const char * const name, + struct ocfs2_dir_entry *de) +{ + if (len != de->name_len) + return 0; + if (!de->inode) + return 0; + return !memcmp(name, de->name, len); +} + +/* + * Returns 0 if not found, -1 on failure, and 1 on success + */ +static int inline ocfs2_search_dirblock(struct buffer_head *bh, + struct inode *dir, + const char *name, int namelen, + unsigned long offset, + char *first_de, + unsigned int bytes, + struct ocfs2_dir_entry **res_dir) +{ + struct ocfs2_dir_entry *de; + char *dlimit, *de_buf; + int de_len; + int ret = 0; + + mlog_entry_void(); + + de_buf = first_de; + dlimit = de_buf + bytes; + + while (de_buf < dlimit) { + /* this code is executed quadratically often */ + /* do minimal checking `by hand' */ + + de = (struct ocfs2_dir_entry *) de_buf; + + if (de_buf + namelen <= dlimit && + ocfs2_match(namelen, name, de)) { + /* found a match - just to be sure, do a full check */ + if (!ocfs2_check_dir_entry(dir, de, bh, offset)) { + ret = -1; + goto bail; + } + *res_dir = de; + ret = 1; + goto bail; + } + + /* prevent looping on a bad block */ + de_len = le16_to_cpu(de->rec_len); + if (de_len <= 0) { + ret = -1; + goto bail; + } + + de_buf += de_len; + offset += de_len; + } + +bail: + mlog_exit(ret); + return ret; +} + +static struct buffer_head *ocfs2_find_entry_id(const char *name, + int namelen, + struct inode *dir, + struct ocfs2_dir_entry **res_dir) +{ + int ret, found; + struct buffer_head *di_bh = NULL; + struct ocfs2_dinode *di; + struct ocfs2_inline_data *data; + + ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno, + &di_bh, OCFS2_BH_CACHED, dir); + if (ret) { + mlog_errno(ret); + goto out; + } + + di = (struct ocfs2_dinode *)di_bh->b_data; + data = &di->id2.i_data; + + found = ocfs2_search_dirblock(di_bh, dir, name, namelen, 0, + data->id_data, i_size_read(dir), res_dir); + if (found == 1) + return di_bh; + + brelse(di_bh); +out: + return NULL; +} + +struct buffer_head *ocfs2_find_entry_el(const char *name, int namelen, + struct inode *dir, + struct ocfs2_dir_entry **res_dir) +{ + struct super_block *sb; + struct buffer_head *bh_use[NAMEI_RA_SIZE]; + struct buffer_head *bh, *ret = NULL; + unsigned long start, block, b; + int ra_max = 0; /* Number of bh's in the readahead + buffer, bh_use[] */ + int ra_ptr = 0; /* Current index into readahead + buffer */ + int num = 0; + int nblocks, i, err; + + mlog_entry_void(); + + sb = dir->i_sb; + + nblocks = i_size_read(dir) >> sb->s_blocksize_bits; + start = OCFS2_I(dir)->ip_dir_start_lookup; + if (start >= nblocks) + start = 0; + block = start; + +restart: + do { + /* + * We deal with the read-ahead logic here. + */ + if (ra_ptr >= ra_max) { + /* Refill the readahead buffer */ + ra_ptr = 0; + b = block; + for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) { + /* + * Terminate if we reach the end of the + * directory and must wrap, or if our + * search has finished at this block. + */ + if (b >= nblocks || (num && block == start)) { + bh_use[ra_max] = NULL; + break; + } + num++; + + bh = ocfs2_bread(dir, b++, &err, 1); + bh_use[ra_max] = bh; + } + } + if ((bh = bh_use[ra_ptr++]) == NULL) + goto next; + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) { + /* read error, skip block & hope for the best */ + ocfs2_error(dir->i_sb, "reading directory %llu, " + "offset %lu\n", + (unsigned long long)OCFS2_I(dir)->ip_blkno, + block); + brelse(bh); + goto next; + } + i = ocfs2_search_dirblock(bh, dir, name, namelen, + block << sb->s_blocksize_bits, + bh->b_data, sb->s_blocksize, + res_dir); + if (i == 1) { + OCFS2_I(dir)->ip_dir_start_lookup = block; + ret = bh; + goto cleanup_and_exit; + } else { + brelse(bh); + if (i < 0) + goto cleanup_and_exit; + } + next: + if (++block >= nblocks) + block = 0; + } while (block != start); + + /* + * If the directory has grown while we were searching, then + * search the last part of the directory before giving up. + */ + block = nblocks; + nblocks = i_size_read(dir) >> sb->s_blocksize_bits; + if (block < nblocks) { + start = 0; + goto restart; + } + +cleanup_and_exit: + /* Clean up the read-ahead blocks */ + for (; ra_ptr < ra_max; ra_ptr++) + brelse(bh_use[ra_ptr]); + + mlog_exit_ptr(ret); + return ret; +} + +/* + * Try to find an entry of the provided name within 'dir'. * + * If nothing was found, NULL is returned. Otherwise, a buffer_head + * and pointer to the dir entry are passed back. + * + * Caller can NOT assume anything about the contents of the + * buffer_head - it is passed back only so that it can be passed into + * any one of the manipulation functions (add entry, delete entry, + * etc). As an example, bh in the extent directory case is a data + * block, in the inline-data case it actually points to an inode. */ -int ocfs2_readdir(struct file * filp, void * dirent, filldir_t filldir) +struct buffer_head *ocfs2_find_entry(const char *name, int namelen, + struct inode *dir, + struct ocfs2_dir_entry **res_dir) +{ + *res_dir = NULL; + + if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) + return ocfs2_find_entry_id(name, namelen, dir, res_dir); + + return ocfs2_find_entry_el(name, namelen, dir, res_dir); +} + +/* + * Update inode number and type of a previously found directory entry. + */ +int ocfs2_update_entry(struct inode *dir, handle_t *handle, + struct buffer_head *de_bh, struct ocfs2_dir_entry *de, + struct inode *new_entry_inode) +{ + int ret; + + /* + * The same code works fine for both inline-data and extent + * based directories, so no need to split this up. + */ + + ret = ocfs2_journal_access(handle, dir, de_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + de->inode = cpu_to_le64(OCFS2_I(new_entry_inode)->ip_blkno); + ocfs2_set_de_type(de, new_entry_inode->i_mode); + + ocfs2_journal_dirty(handle, de_bh); + +out: + return ret; +} + +static int __ocfs2_delete_entry(handle_t *handle, struct inode *dir, + struct ocfs2_dir_entry *de_del, + struct buffer_head *bh, char *first_de, + unsigned int bytes) +{ + struct ocfs2_dir_entry *de, *pde; + int i, status = -ENOENT; + + mlog_entry("(0x%p, 0x%p, 0x%p, 0x%p)\n", handle, dir, de_del, bh); + + i = 0; + pde = NULL; + de = (struct ocfs2_dir_entry *) first_de; + while (i < bytes) { + if (!ocfs2_check_dir_entry(dir, de, bh, i)) { + status = -EIO; + mlog_errno(status); + goto bail; + } + if (de == de_del) { + status = ocfs2_journal_access(handle, dir, bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + status = -EIO; + mlog_errno(status); + goto bail; + } + if (pde) + pde->rec_len = + cpu_to_le16(le16_to_cpu(pde->rec_len) + + le16_to_cpu(de->rec_len)); + else + de->inode = 0; + dir->i_version++; + status = ocfs2_journal_dirty(handle, bh); + goto bail; + } + i += le16_to_cpu(de->rec_len); + pde = de; + de = (struct ocfs2_dir_entry *)((char *)de + le16_to_cpu(de->rec_len)); + } +bail: + mlog_exit(status); + return status; +} + +static inline int ocfs2_delete_entry_id(handle_t *handle, + struct inode *dir, + struct ocfs2_dir_entry *de_del, + struct buffer_head *bh) +{ + int ret; + struct buffer_head *di_bh = NULL; + struct ocfs2_dinode *di; + struct ocfs2_inline_data *data; + + ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno, + &di_bh, OCFS2_BH_CACHED, dir); + if (ret) { + mlog_errno(ret); + goto out; + } + + di = (struct ocfs2_dinode *)di_bh->b_data; + data = &di->id2.i_data; + + ret = __ocfs2_delete_entry(handle, dir, de_del, bh, data->id_data, + i_size_read(dir)); + + brelse(di_bh); +out: + return ret; +} + +static inline int ocfs2_delete_entry_el(handle_t *handle, + struct inode *dir, + struct ocfs2_dir_entry *de_del, + struct buffer_head *bh) +{ + return __ocfs2_delete_entry(handle, dir, de_del, bh, bh->b_data, + bh->b_size); +} + +/* + * ocfs2_delete_entry deletes a directory entry by merging it with the + * previous entry + */ +int ocfs2_delete_entry(handle_t *handle, + struct inode *dir, + struct ocfs2_dir_entry *de_del, + struct buffer_head *bh) +{ + if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) + return ocfs2_delete_entry_id(handle, dir, de_del, bh); + + return ocfs2_delete_entry_el(handle, dir, de_del, bh); +} + +/* + * Check whether 'de' has enough room to hold an entry of + * 'new_rec_len' bytes. + */ +static inline int ocfs2_dirent_would_fit(struct ocfs2_dir_entry *de, + unsigned int new_rec_len) +{ + unsigned int de_really_used; + + /* Check whether this is an empty record with enough space */ + if (le64_to_cpu(de->inode) == 0 && + le16_to_cpu(de->rec_len) >= new_rec_len) + return 1; + + /* + * Record might have free space at the end which we can + * use. + */ + de_really_used = OCFS2_DIR_REC_LEN(de->name_len); + if (le16_to_cpu(de->rec_len) >= (de_really_used + new_rec_len)) + return 1; + + return 0; +} + +/* we don't always have a dentry for what we want to add, so people + * like orphan dir can call this instead. + * + * If you pass me insert_bh, I'll skip the search of the other dir + * blocks and put the record in there. + */ +int __ocfs2_add_entry(handle_t *handle, + struct inode *dir, + const char *name, int namelen, + struct inode *inode, u64 blkno, + struct buffer_head *parent_fe_bh, + struct buffer_head *insert_bh) +{ + unsigned long offset; + unsigned short rec_len; + struct ocfs2_dir_entry *de, *de1; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)parent_fe_bh->b_data; + struct super_block *sb = dir->i_sb; + int retval, status; + unsigned int size = sb->s_blocksize; + char *data_start = insert_bh->b_data; + + mlog_entry_void(); + + if (!namelen) + return -EINVAL; + + if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + data_start = di->id2.i_data.id_data; + size = i_size_read(dir); + + BUG_ON(insert_bh != parent_fe_bh); + } + + rec_len = OCFS2_DIR_REC_LEN(namelen); + offset = 0; + de = (struct ocfs2_dir_entry *) data_start; + while (1) { + BUG_ON((char *)de >= (size + data_start)); + + /* These checks should've already been passed by the + * prepare function, but I guess we can leave them + * here anyway. */ + if (!ocfs2_check_dir_entry(dir, de, insert_bh, offset)) { + retval = -ENOENT; + goto bail; + } + if (ocfs2_match(namelen, name, de)) { + retval = -EEXIST; + goto bail; + } + + if (ocfs2_dirent_would_fit(de, rec_len)) { + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + retval = ocfs2_mark_inode_dirty(handle, dir, parent_fe_bh); + if (retval < 0) { + mlog_errno(retval); + goto bail; + } + + status = ocfs2_journal_access(handle, dir, insert_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + /* By now the buffer is marked for journaling */ + offset += le16_to_cpu(de->rec_len); + if (le64_to_cpu(de->inode)) { + de1 = (struct ocfs2_dir_entry *)((char *) de + + OCFS2_DIR_REC_LEN(de->name_len)); + de1->rec_len = + cpu_to_le16(le16_to_cpu(de->rec_len) - + OCFS2_DIR_REC_LEN(de->name_len)); + de->rec_len = cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len)); + de = de1; + } + de->file_type = OCFS2_FT_UNKNOWN; + if (blkno) { + de->inode = cpu_to_le64(blkno); + ocfs2_set_de_type(de, inode->i_mode); + } else + de->inode = 0; + de->name_len = namelen; + memcpy(de->name, name, namelen); + + dir->i_version++; + status = ocfs2_journal_dirty(handle, insert_bh); + retval = 0; + goto bail; + } + offset += le16_to_cpu(de->rec_len); + de = (struct ocfs2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len)); + } + + /* when you think about it, the assert above should prevent us + * from ever getting here. */ + retval = -ENOSPC; +bail: + + mlog_exit(retval); + return retval; +} + +static int ocfs2_dir_foreach_blk_id(struct inode *inode, + unsigned long *f_version, + loff_t *f_pos, void *priv, + filldir_t filldir, int *filldir_err) +{ + int ret, i, filldir_ret; + unsigned long offset = *f_pos; + struct buffer_head *di_bh = NULL; + struct ocfs2_dinode *di; + struct ocfs2_inline_data *data; + struct ocfs2_dir_entry *de; + + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno, + &di_bh, OCFS2_BH_CACHED, inode); + if (ret) { + mlog(ML_ERROR, "Unable to read inode block for dir %llu\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + goto out; + } + + di = (struct ocfs2_dinode *)di_bh->b_data; + data = &di->id2.i_data; + + while (*f_pos < i_size_read(inode)) { +revalidate: + /* If the dir block has changed since the last call to + * readdir(2), then we might be pointing to an invalid + * dirent right now. Scan from the start of the block + * to make sure. */ + if (*f_version != inode->i_version) { + for (i = 0; i < i_size_read(inode) && i < offset; ) { + de = (struct ocfs2_dir_entry *) + (data->id_data + i); + /* It's too expensive to do a full + * dirent test each time round this + * loop, but we do have to test at + * least that it is non-zero. A + * failure will be detected in the + * dirent test below. */ + if (le16_to_cpu(de->rec_len) < + OCFS2_DIR_REC_LEN(1)) + break; + i += le16_to_cpu(de->rec_len); + } + *f_pos = offset = i; + *f_version = inode->i_version; + } + + de = (struct ocfs2_dir_entry *) (data->id_data + *f_pos); + if (!ocfs2_check_dir_entry(inode, de, di_bh, *f_pos)) { + /* On error, skip the f_pos to the end. */ + *f_pos = i_size_read(inode); + goto out; + } + offset += le16_to_cpu(de->rec_len); + if (le64_to_cpu(de->inode)) { + /* We might block in the next section + * if the data destination is + * currently swapped out. So, use a + * version stamp to detect whether or + * not the directory has been modified + * during the copy operation. + */ + unsigned long version = *f_version; + unsigned char d_type = DT_UNKNOWN; + + if (de->file_type < OCFS2_FT_MAX) + d_type = ocfs2_filetype_table[de->file_type]; + + filldir_ret = filldir(priv, de->name, + de->name_len, + *f_pos, + le64_to_cpu(de->inode), + d_type); + if (filldir_ret) { + if (filldir_err) + *filldir_err = filldir_ret; + break; + } + if (version != *f_version) + goto revalidate; + } + *f_pos += le16_to_cpu(de->rec_len); + } + +out: + brelse(di_bh); + + return 0; +} + +static int ocfs2_dir_foreach_blk_el(struct inode *inode, + unsigned long *f_version, + loff_t *f_pos, void *priv, + filldir_t filldir, int *filldir_err) { int error = 0; unsigned long offset, blk, last_ra_blk = 0; @@ -79,45 +687,23 @@ int ocfs2_readdir(struct file * filp, void * dirent, filldir_t filldir) struct buffer_head * bh, * tmp; struct ocfs2_dir_entry * de; int err; - struct inode *inode = filp->f_path.dentry->d_inode; struct super_block * sb = inode->i_sb; unsigned int ra_sectors = 16; - int lock_level = 0; - - mlog_entry("dirino=%llu\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno); stored = 0; bh = NULL; - error = ocfs2_meta_lock_atime(inode, filp->f_vfsmnt, &lock_level); - if (lock_level && error >= 0) { - /* We release EX lock which used to update atime - * and get PR lock again to reduce contention - * on commonly accessed directories. */ - ocfs2_meta_unlock(inode, 1); - lock_level = 0; - error = ocfs2_meta_lock(inode, NULL, 0); - } - if (error < 0) { - if (error != -ENOENT) - mlog_errno(error); - /* we haven't got any yet, so propagate the error. */ - stored = error; - goto bail_nolock; - } + offset = (*f_pos) & (sb->s_blocksize - 1); - offset = filp->f_pos & (sb->s_blocksize - 1); - - while (!error && !stored && filp->f_pos < i_size_read(inode)) { - blk = (filp->f_pos) >> sb->s_blocksize_bits; + while (!error && !stored && *f_pos < i_size_read(inode)) { + blk = (*f_pos) >> sb->s_blocksize_bits; bh = ocfs2_bread(inode, blk, &err, 0); if (!bh) { mlog(ML_ERROR, "directory #%llu contains a hole at offset %lld\n", (unsigned long long)OCFS2_I(inode)->ip_blkno, - filp->f_pos); - filp->f_pos += sb->s_blocksize - offset; + *f_pos); + *f_pos += sb->s_blocksize - offset; continue; } @@ -143,7 +729,7 @@ revalidate: * readdir(2), then we might be pointing to an invalid * dirent right now. Scan from the start of the block * to make sure. */ - if (filp->f_version != inode->i_version) { + if (*f_version != inode->i_version) { for (i = 0; i < sb->s_blocksize && i < offset; ) { de = (struct ocfs2_dir_entry *) (bh->b_data + i); /* It's too expensive to do a full @@ -158,21 +744,20 @@ revalidate: i += le16_to_cpu(de->rec_len); } offset = i; - filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) + *f_pos = ((*f_pos) & ~(sb->s_blocksize - 1)) | offset; - filp->f_version = inode->i_version; + *f_version = inode->i_version; } - while (!error && filp->f_pos < i_size_read(inode) + while (!error && *f_pos < i_size_read(inode) && offset < sb->s_blocksize) { de = (struct ocfs2_dir_entry *) (bh->b_data + offset); if (!ocfs2_check_dir_entry(inode, de, bh, offset)) { /* On error, skip the f_pos to the next block. */ - filp->f_pos = (filp->f_pos | - (sb->s_blocksize - 1)) + 1; + *f_pos = ((*f_pos) | (sb->s_blocksize - 1)) + 1; brelse(bh); - goto bail; + goto out; } offset += le16_to_cpu(de->rec_len); if (le64_to_cpu(de->inode)) { @@ -183,36 +768,109 @@ revalidate: * not the directory has been modified * during the copy operation. */ - unsigned long version = filp->f_version; + unsigned long version = *f_version; unsigned char d_type = DT_UNKNOWN; if (de->file_type < OCFS2_FT_MAX) d_type = ocfs2_filetype_table[de->file_type]; - error = filldir(dirent, de->name, + error = filldir(priv, de->name, de->name_len, - filp->f_pos, - ino_from_blkno(sb, le64_to_cpu(de->inode)), + *f_pos, + le64_to_cpu(de->inode), d_type); - if (error) + if (error) { + if (filldir_err) + *filldir_err = error; break; - if (version != filp->f_version) + } + if (version != *f_version) goto revalidate; stored ++; } - filp->f_pos += le16_to_cpu(de->rec_len); + *f_pos += le16_to_cpu(de->rec_len); } offset = 0; brelse(bh); } stored = 0; -bail: +out: + return stored; +} + +static int ocfs2_dir_foreach_blk(struct inode *inode, unsigned long *f_version, + loff_t *f_pos, void *priv, filldir_t filldir, + int *filldir_err) +{ + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) + return ocfs2_dir_foreach_blk_id(inode, f_version, f_pos, priv, + filldir, filldir_err); + + return ocfs2_dir_foreach_blk_el(inode, f_version, f_pos, priv, filldir, + filldir_err); +} + +/* + * This is intended to be called from inside other kernel functions, + * so we fake some arguments. + */ +int ocfs2_dir_foreach(struct inode *inode, loff_t *f_pos, void *priv, + filldir_t filldir) +{ + int ret = 0, filldir_err = 0; + unsigned long version = inode->i_version; + + while (*f_pos < i_size_read(inode)) { + ret = ocfs2_dir_foreach_blk(inode, &version, f_pos, priv, + filldir, &filldir_err); + if (ret || filldir_err) + break; + } + + if (ret > 0) + ret = -EIO; + + return 0; +} + +/* + * ocfs2_readdir() + * + */ +int ocfs2_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + int error = 0; + struct inode *inode = filp->f_path.dentry->d_inode; + int lock_level = 0; + + mlog_entry("dirino=%llu\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + + error = ocfs2_meta_lock_atime(inode, filp->f_vfsmnt, &lock_level); + if (lock_level && error >= 0) { + /* We release EX lock which used to update atime + * and get PR lock again to reduce contention + * on commonly accessed directories. */ + ocfs2_meta_unlock(inode, 1); + lock_level = 0; + error = ocfs2_meta_lock(inode, NULL, 0); + } + if (error < 0) { + if (error != -ENOENT) + mlog_errno(error); + /* we haven't got any yet, so propagate the error. */ + goto bail_nolock; + } + + error = ocfs2_dir_foreach_blk(inode, &filp->f_version, &filp->f_pos, + dirent, filldir, NULL); + ocfs2_meta_unlock(inode, lock_level); bail_nolock: - mlog_exit(stored); + mlog_exit(error); - return stored; + return error; } /* @@ -252,6 +910,23 @@ leave: return status; } +/* + * Convenience function for callers which just want the block number + * mapped to a name and don't require the full dirent info, etc. + */ +int ocfs2_lookup_ino_from_name(struct inode *dir, const char *name, + int namelen, u64 *blkno) +{ + int ret; + struct buffer_head *bh = NULL; + struct ocfs2_dir_entry *dirent = NULL; + + ret = ocfs2_find_files_on_disk(name, namelen, blkno, dir, &bh, &dirent); + brelse(bh); + + return ret; +} + /* Check for a name within a directory. * * Return 0 if the name does not exist @@ -284,77 +959,414 @@ bail: return ret; } +struct ocfs2_empty_dir_priv { + unsigned seen_dot; + unsigned seen_dot_dot; + unsigned seen_other; +}; +static int ocfs2_empty_dir_filldir(void *priv, const char *name, int name_len, + loff_t pos, u64 ino, unsigned type) +{ + struct ocfs2_empty_dir_priv *p = priv; + + /* + * Check the positions of "." and ".." records to be sure + * they're in the correct place. + */ + if (name_len == 1 && !strncmp(".", name, 1) && pos == 0) { + p->seen_dot = 1; + return 0; + } + + if (name_len == 2 && !strncmp("..", name, 2) && + pos == OCFS2_DIR_REC_LEN(1)) { + p->seen_dot_dot = 1; + return 0; + } + + p->seen_other = 1; + return 1; +} /* * routine to check that the specified directory is empty (for rmdir) + * + * Returns 1 if dir is empty, zero otherwise. */ int ocfs2_empty_dir(struct inode *inode) { - unsigned long offset; - struct buffer_head * bh; - struct ocfs2_dir_entry * de, * de1; - struct super_block * sb; - int err; + int ret; + loff_t start = 0; + struct ocfs2_empty_dir_priv priv; + + memset(&priv, 0, sizeof(priv)); + + ret = ocfs2_dir_foreach(inode, &start, &priv, ocfs2_empty_dir_filldir); + if (ret) + mlog_errno(ret); - sb = inode->i_sb; - if ((i_size_read(inode) < - (OCFS2_DIR_REC_LEN(1) + OCFS2_DIR_REC_LEN(2))) || - !(bh = ocfs2_bread(inode, 0, &err, 0))) { - mlog(ML_ERROR, "bad directory (dir #%llu) - no data block\n", + if (!priv.seen_dot || !priv.seen_dot_dot) { + mlog(ML_ERROR, "bad directory (dir #%llu) - no `.' or `..'\n", (unsigned long long)OCFS2_I(inode)->ip_blkno); + /* + * XXX: Is it really safe to allow an unlink to continue? + */ return 1; } - de = (struct ocfs2_dir_entry *) bh->b_data; - de1 = (struct ocfs2_dir_entry *) - ((char *)de + le16_to_cpu(de->rec_len)); - if ((le64_to_cpu(de->inode) != OCFS2_I(inode)->ip_blkno) || - !le64_to_cpu(de1->inode) || - strcmp(".", de->name) || - strcmp("..", de1->name)) { - mlog(ML_ERROR, "bad directory (dir #%llu) - no `.' or `..'\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno); - brelse(bh); - return 1; + return !priv.seen_other; +} + +static void ocfs2_fill_initial_dirents(struct inode *inode, + struct inode *parent, + char *start, unsigned int size) +{ + struct ocfs2_dir_entry *de = (struct ocfs2_dir_entry *)start; + + de->inode = cpu_to_le64(OCFS2_I(inode)->ip_blkno); + de->name_len = 1; + de->rec_len = + cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len)); + strcpy(de->name, "."); + ocfs2_set_de_type(de, S_IFDIR); + + de = (struct ocfs2_dir_entry *) ((char *)de + le16_to_cpu(de->rec_len)); + de->inode = cpu_to_le64(OCFS2_I(parent)->ip_blkno); + de->rec_len = cpu_to_le16(size - OCFS2_DIR_REC_LEN(1)); + de->name_len = 2; + strcpy(de->name, ".."); + ocfs2_set_de_type(de, S_IFDIR); +} + +/* + * This works together with code in ocfs2_mknod_locked() which sets + * the inline-data flag and initializes the inline-data section. + */ +static int ocfs2_fill_new_dir_id(struct ocfs2_super *osb, + handle_t *handle, + struct inode *parent, + struct inode *inode, + struct buffer_head *di_bh) +{ + int ret; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_inline_data *data = &di->id2.i_data; + unsigned int size = le16_to_cpu(data->id_count); + + ret = ocfs2_journal_access(handle, inode, di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; } - offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len); - de = (struct ocfs2_dir_entry *)((char *)de1 + le16_to_cpu(de1->rec_len)); - while (offset < i_size_read(inode) ) { - if (!bh || (void *)de >= (void *)(bh->b_data + sb->s_blocksize)) { - brelse(bh); - bh = ocfs2_bread(inode, - offset >> sb->s_blocksize_bits, &err, 0); - if (!bh) { - mlog(ML_ERROR, "dir %llu has a hole at %lu\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, offset); - offset += sb->s_blocksize; - continue; - } - de = (struct ocfs2_dir_entry *) bh->b_data; - } - if (!ocfs2_check_dir_entry(inode, de, bh, offset)) { - brelse(bh); - return 1; + + ocfs2_fill_initial_dirents(inode, parent, data->id_data, size); + + ocfs2_journal_dirty(handle, di_bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + i_size_write(inode, size); + inode->i_nlink = 2; + inode->i_blocks = ocfs2_inode_sector_count(inode); + + ret = ocfs2_mark_inode_dirty(handle, inode, di_bh); + if (ret < 0) + mlog_errno(ret); + +out: + return ret; +} + +static int ocfs2_fill_new_dir_el(struct ocfs2_super *osb, + handle_t *handle, + struct inode *parent, + struct inode *inode, + struct buffer_head *fe_bh, + struct ocfs2_alloc_context *data_ac) +{ + int status; + struct buffer_head *new_bh = NULL; + + mlog_entry_void(); + + status = ocfs2_do_extend_dir(osb->sb, handle, inode, fe_bh, + data_ac, NULL, &new_bh); + if (status < 0) { + mlog_errno(status); + goto bail; + } + + ocfs2_set_new_buffer_uptodate(inode, new_bh); + + status = ocfs2_journal_access(handle, inode, new_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (status < 0) { + mlog_errno(status); + goto bail; + } + memset(new_bh->b_data, 0, osb->sb->s_blocksize); + + ocfs2_fill_initial_dirents(inode, parent, new_bh->b_data, + osb->sb->s_blocksize); + + status = ocfs2_journal_dirty(handle, new_bh); + if (status < 0) { + mlog_errno(status); + goto bail; + } + + i_size_write(inode, inode->i_sb->s_blocksize); + inode->i_nlink = 2; + inode->i_blocks = ocfs2_inode_sector_count(inode); + status = ocfs2_mark_inode_dirty(handle, inode, fe_bh); + if (status < 0) { + mlog_errno(status); + goto bail; + } + + status = 0; +bail: + if (new_bh) + brelse(new_bh); + + mlog_exit(status); + return status; +} + +int ocfs2_fill_new_dir(struct ocfs2_super *osb, + handle_t *handle, + struct inode *parent, + struct inode *inode, + struct buffer_head *fe_bh, + struct ocfs2_alloc_context *data_ac) +{ + BUG_ON(!ocfs2_supports_inline_data(osb) && data_ac == NULL); + + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) + return ocfs2_fill_new_dir_id(osb, handle, parent, inode, fe_bh); + + return ocfs2_fill_new_dir_el(osb, handle, parent, inode, fe_bh, + data_ac); +} + +static void ocfs2_expand_last_dirent(char *start, unsigned int old_size, + unsigned int new_size) +{ + struct ocfs2_dir_entry *de; + struct ocfs2_dir_entry *prev_de; + char *de_buf, *limit; + unsigned int bytes = new_size - old_size; + + limit = start + old_size; + de_buf = start; + de = (struct ocfs2_dir_entry *)de_buf; + do { + prev_de = de; + de_buf += le16_to_cpu(de->rec_len); + de = (struct ocfs2_dir_entry *)de_buf; + } while (de_buf < limit); + + le16_add_cpu(&prev_de->rec_len, bytes); +} + +/* + * We allocate enough clusters to fulfill "blocks_wanted", but set + * i_size to exactly one block. Ocfs2_extend_dir() will handle the + * rest automatically for us. + * + * *first_block_bh is a pointer to the 1st data block allocated to the + * directory. + */ +static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, + unsigned int blocks_wanted, + struct buffer_head **first_block_bh) +{ + int ret, credits = OCFS2_INLINE_TO_EXTENTS_CREDITS; + u32 alloc, bit_off, len; + struct super_block *sb = dir->i_sb; + u64 blkno, bytes = blocks_wanted << sb->s_blocksize_bits; + struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); + struct ocfs2_inode_info *oi = OCFS2_I(dir); + struct ocfs2_alloc_context *data_ac; + struct buffer_head *dirdata_bh = NULL; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + handle_t *handle; + + alloc = ocfs2_clusters_for_bytes(sb, bytes); + + /* + * We should never need more than 2 clusters for this - + * maximum dirent size is far less than one block. In fact, + * the only time we'd need more than one cluster is if + * blocksize == clustersize and the dirent won't fit in the + * extra space that the expansion to a single block gives. As + * of today, that only happens on 4k/4k file systems. + */ + BUG_ON(alloc > 2); + + ret = ocfs2_reserve_clusters(osb, alloc, &data_ac); + if (ret) { + mlog_errno(ret); + goto out; + } + + down_write(&oi->ip_alloc_sem); + + /* + * Prepare for worst case allocation scenario of two seperate + * extents. + */ + if (alloc == 2) + credits += OCFS2_SUBALLOC_ALLOC; + + handle = ocfs2_start_trans(osb, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out_sem; + } + + /* + * Try to claim as many clusters as the bitmap can give though + * if we only get one now, that's enough to continue. The rest + * will be claimed after the conversion to extents. + */ + ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, &len); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + /* + * Operations are carefully ordered so that we set up the new + * data block first. The conversion from inline data to + * extents follows. + */ + blkno = ocfs2_clusters_to_blocks(dir->i_sb, bit_off); + dirdata_bh = sb_getblk(sb, blkno); + if (!dirdata_bh) { + ret = -EIO; + mlog_errno(ret); + goto out_commit; + } + + ocfs2_set_new_buffer_uptodate(dir, dirdata_bh); + + ret = ocfs2_journal_access(handle, dir, dirdata_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + memcpy(dirdata_bh->b_data, di->id2.i_data.id_data, i_size_read(dir)); + memset(dirdata_bh->b_data + i_size_read(dir), 0, + sb->s_blocksize - i_size_read(dir)); + ocfs2_expand_last_dirent(dirdata_bh->b_data, i_size_read(dir), + sb->s_blocksize); + + ret = ocfs2_journal_dirty(handle, dirdata_bh); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + /* + * Set extent, i_size, etc on the directory. After this, the + * inode should contain the same exact dirents as before and + * be fully accessible from system calls. + * + * We let the later dirent insert modify c/mtime - to the user + * the data hasn't changed. + */ + ret = ocfs2_journal_access(handle, dir, di_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + spin_lock(&oi->ip_lock); + oi->ip_dyn_features &= ~OCFS2_INLINE_DATA_FL; + di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); + spin_unlock(&oi->ip_lock); + + ocfs2_dinode_new_extent_list(dir, di); + + i_size_write(dir, sb->s_blocksize); + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + + di->i_size = cpu_to_le64(sb->s_blocksize); + di->i_ctime = di->i_mtime = cpu_to_le64(dir->i_ctime.tv_sec); + di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(dir->i_ctime.tv_nsec); + dir->i_blocks = ocfs2_inode_sector_count(dir); + + /* + * This should never fail as our extent list is empty and all + * related blocks have been journaled already. + */ + ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 0, blkno, len, 0, + NULL); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_dirty(handle, di_bh); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + /* + * We asked for two clusters, but only got one in the 1st + * pass. Claim the 2nd cluster as a separate extent. + */ + if (alloc > len) { + ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, + &len); + if (ret) { + mlog_errno(ret); + goto out_commit; } - if (le64_to_cpu(de->inode)) { - brelse(bh); - return 0; + blkno = ocfs2_clusters_to_blocks(dir->i_sb, bit_off); + + ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 1, blkno, + len, 0, NULL); + if (ret) { + mlog_errno(ret); + goto out; } - offset += le16_to_cpu(de->rec_len); - de = (struct ocfs2_dir_entry *) - ((char *)de + le16_to_cpu(de->rec_len)); } - brelse(bh); - return 1; + + *first_block_bh = dirdata_bh; + dirdata_bh = NULL; + +out_commit: + ocfs2_commit_trans(osb, handle); + +out_sem: + up_write(&oi->ip_alloc_sem); + +out: + if (data_ac) + ocfs2_free_alloc_context(data_ac); + + brelse(dirdata_bh); + + return ret; } /* returns a bh of the 1st new block in the allocation. */ -int ocfs2_do_extend_dir(struct super_block *sb, - handle_t *handle, - struct inode *dir, - struct buffer_head *parent_fe_bh, - struct ocfs2_alloc_context *data_ac, - struct ocfs2_alloc_context *meta_ac, - struct buffer_head **new_bh) +static int ocfs2_do_extend_dir(struct super_block *sb, + handle_t *handle, + struct inode *dir, + struct buffer_head *parent_fe_bh, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + struct buffer_head **new_bh) { int status; int extend; @@ -396,10 +1408,18 @@ bail: return status; } -/* assumes you already have a cluster lock on the directory. */ +/* + * Assumes you already have a cluster lock on the directory. + * + * 'blocks_wanted' is only used if we have an inline directory which + * is to be turned into an extent based one. The size of the dirent to + * insert might be larger than the space gained by growing to just one + * block, so we may have to grow the inode by two blocks in that case. + */ static int ocfs2_extend_dir(struct ocfs2_super *osb, struct inode *dir, struct buffer_head *parent_fe_bh, + unsigned int blocks_wanted, struct buffer_head **new_de_bh) { int status = 0; @@ -415,6 +1435,38 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, mlog_entry_void(); + if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + status = ocfs2_expand_inline_dir(dir, parent_fe_bh, + blocks_wanted, &new_bh); + if (status) { + mlog_errno(status); + goto bail; + } + + if (blocks_wanted == 1) { + /* + * If the new dirent will fit inside the space + * created by pushing out to one block, then + * we can complete the operation + * here. Otherwise we have to expand i_size + * and format the 2nd block below. + */ + BUG_ON(new_bh == NULL); + goto bail_bh; + } + + /* + * Get rid of 'new_bh' - we want to format the 2nd + * data block and return that instead. + */ + brelse(new_bh); + new_bh = NULL; + + dir_i_size = i_size_read(dir); + credits = OCFS2_SIMPLE_DIR_EXTEND_CREDITS; + goto do_extend; + } + dir_i_size = i_size_read(dir); mlog(0, "extending dir %llu (i_size = %lld)\n", (unsigned long long)OCFS2_I(dir)->ip_blkno, dir_i_size); @@ -452,6 +1504,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, credits = OCFS2_SIMPLE_DIR_EXTEND_CREDITS; } +do_extend: down_write(&OCFS2_I(dir)->ip_alloc_sem); drop_alloc_sem = 1; @@ -497,6 +1550,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, goto bail; } +bail_bh: *new_de_bh = new_bh; get_bh(*new_de_bh); bail: @@ -517,41 +1571,71 @@ bail: return status; } -/* - * Search the dir for a good spot, extending it if necessary. The - * block containing an appropriate record is returned in ret_de_bh. - */ -int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb, - struct inode *dir, - struct buffer_head *parent_fe_bh, - const char *name, - int namelen, - struct buffer_head **ret_de_bh) +static int ocfs2_find_dir_space_id(struct inode *dir, struct buffer_head *di_bh, + const char *name, int namelen, + struct buffer_head **ret_de_bh, + unsigned int *blocks_wanted) { - unsigned long offset; - struct buffer_head * bh = NULL; - unsigned short rec_len; - struct ocfs2_dinode *fe; - struct ocfs2_dir_entry *de; - struct super_block *sb; - int status; + int ret; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_dir_entry *de, *last_de = NULL; + char *de_buf, *limit; + unsigned long offset = 0; + unsigned int rec_len, new_rec_len; + + de_buf = di->id2.i_data.id_data; + limit = de_buf + i_size_read(dir); + rec_len = OCFS2_DIR_REC_LEN(namelen); - mlog_entry_void(); + while (de_buf < limit) { + de = (struct ocfs2_dir_entry *)de_buf; - mlog(0, "getting ready to insert namelen %d into dir %llu\n", - namelen, (unsigned long long)OCFS2_I(dir)->ip_blkno); + if (!ocfs2_check_dir_entry(dir, de, di_bh, offset)) { + ret = -ENOENT; + goto out; + } + if (ocfs2_match(namelen, name, de)) { + ret = -EEXIST; + goto out; + } + if (ocfs2_dirent_would_fit(de, rec_len)) { + /* Ok, we found a spot. Return this bh and let + * the caller actually fill it in. */ + *ret_de_bh = di_bh; + get_bh(*ret_de_bh); + ret = 0; + goto out; + } - BUG_ON(!S_ISDIR(dir->i_mode)); - fe = (struct ocfs2_dinode *) parent_fe_bh->b_data; - BUG_ON(le64_to_cpu(fe->i_size) != i_size_read(dir)); + last_de = de; + de_buf += le16_to_cpu(de->rec_len); + offset += le16_to_cpu(de->rec_len); + } - sb = dir->i_sb; + /* + * We're going to require expansion of the directory - figure + * out how many blocks we'll need so that a place for the + * dirent can be found. + */ + *blocks_wanted = 1; + new_rec_len = le16_to_cpu(last_de->rec_len) + (dir->i_sb->s_blocksize - i_size_read(dir)); + if (new_rec_len < (rec_len + OCFS2_DIR_REC_LEN(last_de->name_len))) + *blocks_wanted = 2; + + ret = -ENOSPC; +out: + return ret; +} - if (!namelen) { - status = -EINVAL; - mlog_errno(status); - goto bail; - } +static int ocfs2_find_dir_space_el(struct inode *dir, const char *name, + int namelen, struct buffer_head **ret_de_bh) +{ + unsigned long offset; + struct buffer_head *bh = NULL; + unsigned short rec_len; + struct ocfs2_dir_entry *de; + struct super_block *sb = dir->i_sb; + int status; bh = ocfs2_bread(dir, 0, &status, 0); if (!bh) { @@ -568,17 +1652,11 @@ int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb, bh = NULL; if (i_size_read(dir) <= offset) { - status = ocfs2_extend_dir(osb, - dir, - parent_fe_bh, - &bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - BUG_ON(!bh); - *ret_de_bh = bh; - get_bh(*ret_de_bh); + /* + * Caller will have to expand this + * directory. + */ + status = -ENOSPC; goto bail; } bh = ocfs2_bread(dir, @@ -600,10 +1678,7 @@ int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb, status = -EEXIST; goto bail; } - if (((le64_to_cpu(de->inode) == 0) && - (le16_to_cpu(de->rec_len) >= rec_len)) || - (le16_to_cpu(de->rec_len) >= - (OCFS2_DIR_REC_LEN(de->name_len) + rec_len))) { + if (ocfs2_dirent_would_fit(de, rec_len)) { /* Ok, we found a spot. Return this bh and let * the caller actually fill it in. */ *ret_de_bh = bh; @@ -623,3 +1698,61 @@ bail: mlog_exit(status); return status; } + +int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb, + struct inode *dir, + struct buffer_head *parent_fe_bh, + const char *name, + int namelen, + struct buffer_head **ret_de_bh) +{ + int ret; + unsigned int blocks_wanted = 1; + struct buffer_head *bh = NULL; + + mlog(0, "getting ready to insert namelen %d into dir %llu\n", + namelen, (unsigned long long)OCFS2_I(dir)->ip_blkno); + + *ret_de_bh = NULL; + + if (!namelen) { + ret = -EINVAL; + mlog_errno(ret); + goto out; + } + + if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + ret = ocfs2_find_dir_space_id(dir, parent_fe_bh, name, + namelen, &bh, &blocks_wanted); + } else + ret = ocfs2_find_dir_space_el(dir, name, namelen, &bh); + + if (ret && ret != -ENOSPC) { + mlog_errno(ret); + goto out; + } + + if (ret == -ENOSPC) { + /* + * We have to expand the directory to add this name. + */ + BUG_ON(bh); + + ret = ocfs2_extend_dir(osb, dir, parent_fe_bh, blocks_wanted, + &bh); + if (ret) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + + BUG_ON(!bh); + } + + *ret_de_bh = bh; + bh = NULL; +out: + if (bh) + brelse(bh); + return ret; +} diff --git a/fs/ocfs2/dir.h b/fs/ocfs2/dir.h index 3f67e146864..ce48b9080d8 100644 --- a/fs/ocfs2/dir.h +++ b/fs/ocfs2/dir.h @@ -26,17 +26,49 @@ #ifndef OCFS2_DIR_H #define OCFS2_DIR_H +struct buffer_head *ocfs2_find_entry(const char *name, + int namelen, + struct inode *dir, + struct ocfs2_dir_entry **res_dir); +int ocfs2_delete_entry(handle_t *handle, + struct inode *dir, + struct ocfs2_dir_entry *de_del, + struct buffer_head *bh); +int __ocfs2_add_entry(handle_t *handle, + struct inode *dir, + const char *name, int namelen, + struct inode *inode, u64 blkno, + struct buffer_head *parent_fe_bh, + struct buffer_head *insert_bh); +static inline int ocfs2_add_entry(handle_t *handle, + struct dentry *dentry, + struct inode *inode, u64 blkno, + struct buffer_head *parent_fe_bh, + struct buffer_head *insert_bh) +{ + return __ocfs2_add_entry(handle, dentry->d_parent->d_inode, + dentry->d_name.name, dentry->d_name.len, + inode, blkno, parent_fe_bh, insert_bh); +} +int ocfs2_update_entry(struct inode *dir, handle_t *handle, + struct buffer_head *de_bh, struct ocfs2_dir_entry *de, + struct inode *new_entry_inode); + int ocfs2_check_dir_for_entry(struct inode *dir, const char *name, int namelen); -int ocfs2_empty_dir(struct inode *inode); /* FIXME: to namei.c */ +int ocfs2_empty_dir(struct inode *inode); int ocfs2_find_files_on_disk(const char *name, int namelen, u64 *blkno, struct inode *inode, struct buffer_head **dirent_bh, struct ocfs2_dir_entry **dirent); +int ocfs2_lookup_ino_from_name(struct inode *dir, const char *name, + int namelen, u64 *blkno); int ocfs2_readdir(struct file *filp, void *dirent, filldir_t filldir); +int ocfs2_dir_foreach(struct inode *inode, loff_t *f_pos, void *priv, + filldir_t filldir); int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb, struct inode *dir, struct buffer_head *parent_fe_bh, @@ -44,11 +76,11 @@ int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb, int namelen, struct buffer_head **ret_de_bh); struct ocfs2_alloc_context; -int ocfs2_do_extend_dir(struct super_block *sb, - handle_t *handle, - struct inode *dir, - struct buffer_head *parent_fe_bh, - struct ocfs2_alloc_context *data_ac, - struct ocfs2_alloc_context *meta_ac, - struct buffer_head **new_bh); +int ocfs2_fill_new_dir(struct ocfs2_super *osb, + handle_t *handle, + struct inode *parent, + struct inode *inode, + struct buffer_head *fe_bh, + struct ocfs2_alloc_context *data_ac); + #endif /* OCFS2_DIR_H */ diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index f71250ed166..41c76ff2fcf 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -1482,6 +1482,7 @@ static void __ocfs2_stuff_meta_lvb(struct inode *inode) lvb->lvb_imtime_packed = cpu_to_be64(ocfs2_pack_timespec(&inode->i_mtime)); lvb->lvb_iattr = cpu_to_be32(oi->ip_attr); + lvb->lvb_idynfeatures = cpu_to_be16(oi->ip_dyn_features); lvb->lvb_igeneration = cpu_to_be32(inode->i_generation); out: @@ -1515,6 +1516,7 @@ static void ocfs2_refresh_inode_from_lvb(struct inode *inode) i_size_write(inode, be64_to_cpu(lvb->lvb_isize)); oi->ip_attr = be32_to_cpu(lvb->lvb_iattr); + oi->ip_dyn_features = be16_to_cpu(lvb->lvb_idynfeatures); ocfs2_set_inode_flags(inode); /* fast-symlinks are a special case */ diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h index 492bad32a8c..87a785e4120 100644 --- a/fs/ocfs2/dlmglue.h +++ b/fs/ocfs2/dlmglue.h @@ -29,12 +29,12 @@ #include "dcache.h" -#define OCFS2_LVB_VERSION 4 +#define OCFS2_LVB_VERSION 5 struct ocfs2_meta_lvb { __u8 lvb_version; __u8 lvb_reserved0; - __be16 lvb_reserved1; + __be16 lvb_idynfeatures; __be32 lvb_iclusters; __be32 lvb_iuid; __be32 lvb_igid; diff --git a/fs/ocfs2/export.c b/fs/ocfs2/export.c index bc48177bd18..c3bbc198f9c 100644 --- a/fs/ocfs2/export.c +++ b/fs/ocfs2/export.c @@ -88,8 +88,6 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) struct dentry *parent; struct inode *inode; struct inode *dir = child->d_inode; - struct buffer_head *dirent_bh = NULL; - struct ocfs2_dir_entry *dirent; mlog_entry("(0x%p, '%.*s')\n", child, child->d_name.len, child->d_name.name); @@ -105,8 +103,7 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) goto bail; } - status = ocfs2_find_files_on_disk("..", 2, &blkno, dir, &dirent_bh, - &dirent); + status = ocfs2_lookup_ino_from_name(dir, "..", 2, &blkno); if (status < 0) { parent = ERR_PTR(-ENOENT); goto bail_unlock; @@ -131,9 +128,6 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) bail_unlock: ocfs2_meta_unlock(dir, 0); - if (dirent_bh) - brelse(dirent_bh); - bail: mlog_exit_ptr(parent); diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c index 03c1d365c78..c58668a326f 100644 --- a/fs/ocfs2/extent_map.c +++ b/fs/ocfs2/extent_map.c @@ -387,6 +387,12 @@ int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, struct ocfs2_extent_rec *rec; u32 coff; + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + ret = -ERANGE; + mlog_errno(ret); + goto out; + } + ret = ocfs2_extent_map_lookup(inode, v_cluster, p_cluster, num_clusters, extent_flags); if (ret == 0) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index f3bc3658e7a..a62b14eb406 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -397,6 +397,15 @@ static int ocfs2_truncate_file(struct inode *inode, unmap_mapping_range(inode->i_mapping, new_i_size + PAGE_SIZE - 1, 0, 1); truncate_inode_pages(inode->i_mapping, new_i_size); + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + status = ocfs2_truncate_inline(inode, di_bh, new_i_size, + i_size_read(inode), 0); + if (status) + mlog_errno(status); + + goto bail_unlock_data; + } + /* alright, we're going to need to do a full blown alloc size * change. Orphan the inode so that recovery can complete the * truncate if necessary. This does the task of marking @@ -779,25 +788,6 @@ leave: return status; } -static int ocfs2_extend_allocation(struct inode *inode, u32 logical_start, - u32 clusters_to_add, int mark_unwritten) -{ - int ret; - - /* - * The alloc sem blocks peope in read/write from reading our - * allocation until we're done changing it. We depend on - * i_mutex to block other extend/truncate calls while we're - * here. - */ - down_write(&OCFS2_I(inode)->ip_alloc_sem); - ret = __ocfs2_extend_allocation(inode, logical_start, clusters_to_add, - mark_unwritten); - up_write(&OCFS2_I(inode)->ip_alloc_sem); - - return ret; -} - /* Some parts of this taken from generic_cont_expand, which turned out * to be too fragile to do exactly what we need without us having to * worry about recursive locking in ->prepare_write() and @@ -889,25 +879,48 @@ out: return ret; } -/* - * A tail_to_skip value > 0 indicates that we're being called from - * ocfs2_file_aio_write(). This has the following implications: - * - * - we don't want to update i_size - * - di_bh will be NULL, which is fine because it's only used in the - * case where we want to update i_size. - * - ocfs2_zero_extend() will then only be filling the hole created - * between i_size and the start of the write. - */ +int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size, u64 zero_to) +{ + int ret; + u32 clusters_to_add; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + + clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size); + if (clusters_to_add < oi->ip_clusters) + clusters_to_add = 0; + else + clusters_to_add -= oi->ip_clusters; + + if (clusters_to_add) { + ret = __ocfs2_extend_allocation(inode, oi->ip_clusters, + clusters_to_add, 0); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + /* + * Call this even if we don't add any clusters to the tree. We + * still need to zero the area between the old i_size and the + * new i_size. + */ + ret = ocfs2_zero_extend(inode, zero_to); + if (ret < 0) + mlog_errno(ret); + +out: + return ret; +} + static int ocfs2_extend_file(struct inode *inode, struct buffer_head *di_bh, - u64 new_i_size, - size_t tail_to_skip) + u64 new_i_size) { - int ret = 0; - u32 clusters_to_add = 0; + int ret = 0, data_locked = 0; + struct ocfs2_inode_info *oi = OCFS2_I(inode); - BUG_ON(!tail_to_skip && !di_bh); + BUG_ON(!di_bh); /* setattr sometimes calls us like this. */ if (new_i_size == 0) @@ -917,13 +930,18 @@ static int ocfs2_extend_file(struct inode *inode, goto out; BUG_ON(new_i_size < i_size_read(inode)); - if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) { - BUG_ON(tail_to_skip != 0); + /* + * Fall through for converting inline data, even if the fs + * supports sparse files. + * + * The check for inline data here is legal - nobody can add + * the feature since we have i_mutex. We must check it again + * after acquiring ip_alloc_sem though, as paths like mmap + * might have raced us to converting the inode to extents. + */ + if (!(oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) + && ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) goto out_update_size; - } - - clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size) - - OCFS2_I(inode)->ip_clusters; /* * protect the pages that ocfs2_zero_extend is going to be @@ -937,39 +955,52 @@ static int ocfs2_extend_file(struct inode *inode, mlog_errno(ret); goto out; } + data_locked = 1; + + /* + * The alloc sem blocks people in read/write from reading our + * allocation until we're done changing it. We depend on + * i_mutex to block other extend/truncate calls while we're + * here. + */ + down_write(&oi->ip_alloc_sem); + + if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + /* + * We can optimize small extends by keeping the inodes + * inline data. + */ + if (ocfs2_size_fits_inline_data(di_bh, new_i_size)) { + up_write(&oi->ip_alloc_sem); + goto out_update_size; + } + + ret = ocfs2_convert_inline_data_to_extents(inode, di_bh); + if (ret) { + up_write(&oi->ip_alloc_sem); - if (clusters_to_add) { - ret = ocfs2_extend_allocation(inode, - OCFS2_I(inode)->ip_clusters, - clusters_to_add, 0); - if (ret < 0) { mlog_errno(ret); goto out_unlock; } } - /* - * Call this even if we don't add any clusters to the tree. We - * still need to zero the area between the old i_size and the - * new i_size. - */ - ret = ocfs2_zero_extend(inode, (u64)new_i_size - tail_to_skip); + if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) + ret = ocfs2_extend_no_holes(inode, new_i_size, new_i_size); + + up_write(&oi->ip_alloc_sem); + if (ret < 0) { mlog_errno(ret); goto out_unlock; } out_update_size: - if (!tail_to_skip) { - /* We're being called from ocfs2_setattr() which wants - * us to update i_size */ - ret = ocfs2_simple_size_update(inode, di_bh, new_i_size); - if (ret < 0) - mlog_errno(ret); - } + ret = ocfs2_simple_size_update(inode, di_bh, new_i_size); + if (ret < 0) + mlog_errno(ret); out_unlock: - if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) + if (data_locked) ocfs2_data_unlock(inode, 1); out: @@ -1035,7 +1066,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) if (i_size_read(inode) > attr->ia_size) status = ocfs2_truncate_file(inode, bh, attr->ia_size); else - status = ocfs2_extend_file(inode, bh, attr->ia_size, 0); + status = ocfs2_extend_file(inode, bh, attr->ia_size); if (status < 0) { if (status != -ENOSPC) mlog_errno(status); @@ -1243,6 +1274,31 @@ static int ocfs2_allocate_unwritten_extents(struct inode *inode, { int ret; u32 cpos, phys_cpos, clusters, alloc_size; + u64 end = start + len; + struct buffer_head *di_bh = NULL; + + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), + OCFS2_I(inode)->ip_blkno, &di_bh, + OCFS2_BH_CACHED, inode); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * Nothing to do if the requested reservation range + * fits within the inode. + */ + if (ocfs2_size_fits_inline_data(di_bh, end)) + goto out; + + ret = ocfs2_convert_inline_data_to_extents(inode, di_bh); + if (ret) { + mlog_errno(ret); + goto out; + } + } /* * We consider both start and len to be inclusive. @@ -1288,6 +1344,8 @@ next: ret = 0; out: + + brelse(di_bh); return ret; } @@ -1469,6 +1527,14 @@ static int ocfs2_remove_inode_range(struct inode *inode, if (byte_len == 0) return 0; + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + ret = ocfs2_truncate_inline(inode, di_bh, byte_start, + byte_start + byte_len, 1); + if (ret) + mlog_errno(ret); + return ret; + } + trunc_start = ocfs2_clusters_for_bytes(osb->sb, byte_start); trunc_len = (byte_start + byte_len) >> osb->s_clustersize_bits; if (trunc_len >= trunc_start) @@ -1713,15 +1779,13 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry, int appending, int *direct_io) { - int ret = 0, meta_level = appending; + int ret = 0, meta_level = 0; struct inode *inode = dentry->d_inode; - u32 clusters; - loff_t newsize, saved_pos; + loff_t saved_pos, end; /* - * We sample i_size under a read level meta lock to see if our write - * is extending the file, if it is we back off and get a write level - * meta lock. + * We start with a read level meta lock and only jump to an ex + * if we need to make modifications here. */ for(;;) { ret = ocfs2_meta_lock(inode, NULL, meta_level); @@ -1763,87 +1827,47 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry, saved_pos = *ppos; } - if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) { - loff_t end = saved_pos + count; + end = saved_pos + count; - /* - * Skip the O_DIRECT checks if we don't need - * them. - */ - if (!direct_io || !(*direct_io)) - break; - - /* - * Allowing concurrent direct writes means - * i_size changes wouldn't be synchronized, so - * one node could wind up truncating another - * nodes writes. - */ - if (end > i_size_read(inode)) { - *direct_io = 0; - break; - } - - /* - * We don't fill holes during direct io, so - * check for them here. If any are found, the - * caller will have to retake some cluster - * locks and initiate the io as buffered. - */ - ret = ocfs2_check_range_for_holes(inode, saved_pos, - count); - if (ret == 1) { - *direct_io = 0; - ret = 0; - } else if (ret < 0) - mlog_errno(ret); + /* + * Skip the O_DIRECT checks if we don't need + * them. + */ + if (!direct_io || !(*direct_io)) break; - } /* - * The rest of this loop is concerned with legacy file - * systems which don't support sparse files. + * There's no sane way to do direct writes to an inode + * with inline data. */ - - newsize = count + saved_pos; - - mlog(0, "pos=%lld newsize=%lld cursize=%lld\n", - (long long) saved_pos, (long long) newsize, - (long long) i_size_read(inode)); - - /* No need for a higher level metadata lock if we're - * never going past i_size. */ - if (newsize <= i_size_read(inode)) + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + *direct_io = 0; break; - - if (meta_level == 0) { - ocfs2_meta_unlock(inode, meta_level); - meta_level = 1; - continue; } - spin_lock(&OCFS2_I(inode)->ip_lock); - clusters = ocfs2_clusters_for_bytes(inode->i_sb, newsize) - - OCFS2_I(inode)->ip_clusters; - spin_unlock(&OCFS2_I(inode)->ip_lock); - - mlog(0, "Writing at EOF, may need more allocation: " - "i_size = %lld, newsize = %lld, need %u clusters\n", - (long long) i_size_read(inode), (long long) newsize, - clusters); - - /* We only want to continue the rest of this loop if - * our extend will actually require more - * allocation. */ - if (!clusters) + /* + * Allowing concurrent direct writes means + * i_size changes wouldn't be synchronized, so + * one node could wind up truncating another + * nodes writes. + */ + if (end > i_size_read(inode)) { + *direct_io = 0; break; - - ret = ocfs2_extend_file(inode, NULL, newsize, count); - if (ret < 0) { - if (ret != -ENOSPC) - mlog_errno(ret); - goto out_unlock; } + + /* + * We don't fill holes during direct io, so + * check for them here. If any are found, the + * caller will have to retake some cluster + * locks and initiate the io as buffered. + */ + ret = ocfs2_check_range_for_holes(inode, saved_pos, count); + if (ret == 1) { + *direct_io = 0; + ret = 0; + } else if (ret < 0) + mlog_errno(ret); break; } diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index 36fe27f268e..066f14add3a 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h @@ -47,6 +47,8 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb, struct ocfs2_alloc_context *data_ac, struct ocfs2_alloc_context *meta_ac, enum ocfs2_alloc_restarted *reason_ret); +int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size, + u64 zero_to); int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac, diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index c53a6763bbb..1d5e0cb0fda 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -241,6 +241,7 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters); OCFS2_I(inode)->ip_attr = le32_to_cpu(fe->i_attr); + OCFS2_I(inode)->ip_dyn_features = le16_to_cpu(fe->i_dyn_features); inode->i_version = 1; inode->i_generation = le32_to_cpu(fe->i_generation); @@ -513,6 +514,10 @@ static int ocfs2_truncate_for_delete(struct ocfs2_super *osb, fe = (struct ocfs2_dinode *) fe_bh->b_data; + /* + * This check will also skip truncate of inodes with inline + * data and fast symlinks. + */ if (fe->i_clusters) { handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); if (IS_ERR(handle)) { @@ -1220,6 +1225,7 @@ int ocfs2_mark_inode_dirty(handle_t *handle, fe->i_clusters = cpu_to_le32(OCFS2_I(inode)->ip_clusters); ocfs2_get_inode_flags(OCFS2_I(inode)); fe->i_attr = cpu_to_le32(OCFS2_I(inode)->ip_attr); + fe->i_dyn_features = cpu_to_le16(OCFS2_I(inode)->ip_dyn_features); spin_unlock(&OCFS2_I(inode)->ip_lock); fe->i_size = cpu_to_le64(i_size_read(inode)); @@ -1257,6 +1263,7 @@ void ocfs2_refresh_inode(struct inode *inode, OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters); OCFS2_I(inode)->ip_attr = le32_to_cpu(fe->i_attr); + OCFS2_I(inode)->ip_dyn_features = le16_to_cpu(fe->i_dyn_features); ocfs2_set_inode_flags(inode); i_size_write(inode, le64_to_cpu(fe->i_size)); inode->i_nlink = le16_to_cpu(fe->i_links_count); diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index a41d0817121..70e881c5553 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -51,6 +51,7 @@ struct ocfs2_inode_info u32 ip_flags; /* see below */ u32 ip_attr; /* inode attributes */ + u16 ip_dyn_features; /* protected by recovery_lock. */ struct inode *ip_next_orphan; diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index dbfb20bb27e..f9d01e25298 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -35,13 +35,13 @@ #include "ocfs2.h" #include "alloc.h" +#include "dir.h" #include "dlmglue.h" #include "extent_map.h" #include "heartbeat.h" #include "inode.h" #include "journal.h" #include "localalloc.h" -#include "namei.h" #include "slot_map.h" #include "super.h" #include "vote.h" @@ -1213,17 +1213,49 @@ bail: return status; } +struct ocfs2_orphan_filldir_priv { + struct inode *head; + struct ocfs2_super *osb; +}; + +static int ocfs2_orphan_filldir(void *priv, const char *name, int name_len, + loff_t pos, u64 ino, unsigned type) +{ + struct ocfs2_orphan_filldir_priv *p = priv; + struct inode *iter; + + if (name_len == 1 && !strncmp(".", name, 1)) + return 0; + if (name_len == 2 && !strncmp("..", name, 2)) + return 0; + + /* Skip bad inodes so that recovery can continue */ + iter = ocfs2_iget(p->osb, ino, + OCFS2_FI_FLAG_ORPHAN_RECOVERY); + if (IS_ERR(iter)) + return 0; + + mlog(0, "queue orphan %llu\n", + (unsigned long long)OCFS2_I(iter)->ip_blkno); + /* No locking is required for the next_orphan queue as there + * is only ever a single process doing orphan recovery. */ + OCFS2_I(iter)->ip_next_orphan = p->head; + p->head = iter; + + return 0; +} + static int ocfs2_queue_orphans(struct ocfs2_super *osb, int slot, struct inode **head) { int status; struct inode *orphan_dir_inode = NULL; - struct inode *iter; - unsigned long offset, blk, local; - struct buffer_head *bh = NULL; - struct ocfs2_dir_entry *de; - struct super_block *sb = osb->sb; + struct ocfs2_orphan_filldir_priv priv; + loff_t pos = 0; + + priv.osb = osb; + priv.head = *head; orphan_dir_inode = ocfs2_get_system_file_inode(osb, ORPHAN_DIR_SYSTEM_INODE, @@ -1241,77 +1273,15 @@ static int ocfs2_queue_orphans(struct ocfs2_super *osb, goto out; } - offset = 0; - iter = NULL; - while(offset < i_size_read(orphan_dir_inode)) { - blk = offset >> sb->s_blocksize_bits; - - bh = ocfs2_bread(orphan_dir_inode, blk, &status, 0); - if (!bh) - status = -EINVAL; - if (status < 0) { - if (bh) - brelse(bh); - mlog_errno(status); - goto out_unlock; - } - - local = 0; - while(offset < i_size_read(orphan_dir_inode) - && local < sb->s_blocksize) { - de = (struct ocfs2_dir_entry *) (bh->b_data + local); - - if (!ocfs2_check_dir_entry(orphan_dir_inode, - de, bh, local)) { - status = -EINVAL; - mlog_errno(status); - brelse(bh); - goto out_unlock; - } - - local += le16_to_cpu(de->rec_len); - offset += le16_to_cpu(de->rec_len); - - /* I guess we silently fail on no inode? */ - if (!le64_to_cpu(de->inode)) - continue; - if (de->file_type > OCFS2_FT_MAX) { - mlog(ML_ERROR, - "block %llu contains invalid de: " - "inode = %llu, rec_len = %u, " - "name_len = %u, file_type = %u, " - "name='%.*s'\n", - (unsigned long long)bh->b_blocknr, - (unsigned long long)le64_to_cpu(de->inode), - le16_to_cpu(de->rec_len), - de->name_len, - de->file_type, - de->name_len, - de->name); - continue; - } - if (de->name_len == 1 && !strncmp(".", de->name, 1)) - continue; - if (de->name_len == 2 && !strncmp("..", de->name, 2)) - continue; - - iter = ocfs2_iget(osb, le64_to_cpu(de->inode), - OCFS2_FI_FLAG_ORPHAN_RECOVERY); - if (IS_ERR(iter)) - continue; - - mlog(0, "queue orphan %llu\n", - (unsigned long long)OCFS2_I(iter)->ip_blkno); - /* No locking is required for the next_orphan - * queue as there is only ever a single - * process doing orphan recovery. */ - OCFS2_I(iter)->ip_next_orphan = *head; - *head = iter; - } - brelse(bh); + status = ocfs2_dir_foreach(orphan_dir_inode, &pos, &priv, + ocfs2_orphan_filldir); + if (status) { + mlog_errno(status); + goto out; } -out_unlock: + *head = priv.head; + ocfs2_meta_unlock(orphan_dir_inode, 0); out: mutex_unlock(&orphan_dir_inode->i_mutex); diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index ce60aab013a..4b32e096156 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -282,6 +282,9 @@ int ocfs2_journal_dirty_data(handle_t *handle, * prev. group desc. if we relink. */ #define OCFS2_SUBALLOC_ALLOC (3) +#define OCFS2_INLINE_TO_EXTENTS_CREDITS (OCFS2_SUBALLOC_ALLOC \ + + OCFS2_INODE_UPDATE_CREDITS) + /* dinode + group descriptor update. We don't relink on free yet. */ #define OCFS2_SUBALLOC_FREE (2) diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 701e6d04ed5..729259016c1 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -64,29 +64,6 @@ #include "buffer_head_io.h" -#define NAMEI_RA_CHUNKS 2 -#define NAMEI_RA_BLOCKS 4 -#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) -#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) - -static int inline ocfs2_search_dirblock(struct buffer_head *bh, - struct inode *dir, - const char *name, int namelen, - unsigned long offset, - struct ocfs2_dir_entry **res_dir); - -static int ocfs2_delete_entry(handle_t *handle, - struct inode *dir, - struct ocfs2_dir_entry *de_del, - struct buffer_head *bh); - -static int __ocfs2_add_entry(handle_t *handle, - struct inode *dir, - const char *name, int namelen, - struct inode *inode, u64 blkno, - struct buffer_head *parent_fe_bh, - struct buffer_head *insert_bh); - static int ocfs2_mknod_locked(struct ocfs2_super *osb, struct inode *dir, struct dentry *dentry, int mode, @@ -97,13 +74,6 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb, struct inode **ret_inode, struct ocfs2_alloc_context *inode_ac); -static int ocfs2_fill_new_dir(struct ocfs2_super *osb, - handle_t *handle, - struct inode *parent, - struct inode *inode, - struct buffer_head *fe_bh, - struct ocfs2_alloc_context *data_ac); - static int ocfs2_prepare_orphan_dir(struct ocfs2_super *osb, struct inode **ret_orphan_dir, struct inode *inode, @@ -123,17 +93,6 @@ static int ocfs2_create_symlink_data(struct ocfs2_super *osb, struct inode *inode, const char *symname); -static inline int ocfs2_add_entry(handle_t *handle, - struct dentry *dentry, - struct inode *inode, u64 blkno, - struct buffer_head *parent_fe_bh, - struct buffer_head *insert_bh) -{ - return __ocfs2_add_entry(handle, dentry->d_parent->d_inode, - dentry->d_name.name, dentry->d_name.len, - inode, blkno, parent_fe_bh, insert_bh); -} - /* An orphan dir name is an 8 byte value, printed as a hex string */ #define OCFS2_ORPHAN_NAMELEN ((int)(2 * sizeof(u64))) @@ -142,10 +101,8 @@ static struct dentry *ocfs2_lookup(struct inode *dir, struct dentry *dentry, { int status; u64 blkno; - struct buffer_head *dirent_bh = NULL; struct inode *inode = NULL; struct dentry *ret; - struct ocfs2_dir_entry *dirent; struct ocfs2_inode_info *oi; mlog_entry("(0x%p, 0x%p, '%.*s')\n", dir, dentry, @@ -167,9 +124,8 @@ static struct dentry *ocfs2_lookup(struct inode *dir, struct dentry *dentry, goto bail; } - status = ocfs2_find_files_on_disk(dentry->d_name.name, - dentry->d_name.len, &blkno, - dir, &dirent_bh, &dirent); + status = ocfs2_lookup_ino_from_name(dir, dentry->d_name.name, + dentry->d_name.len, &blkno); if (status < 0) goto bail_add; @@ -224,83 +180,12 @@ bail_unlock: ocfs2_meta_unlock(dir, 0); bail: - if (dirent_bh) - brelse(dirent_bh); mlog_exit_ptr(ret); return ret; } -static int ocfs2_fill_new_dir(struct ocfs2_super *osb, - handle_t *handle, - struct inode *parent, - struct inode *inode, - struct buffer_head *fe_bh, - struct ocfs2_alloc_context *data_ac) -{ - int status; - struct buffer_head *new_bh = NULL; - struct ocfs2_dir_entry *de = NULL; - - mlog_entry_void(); - - status = ocfs2_do_extend_dir(osb->sb, handle, inode, fe_bh, - data_ac, NULL, &new_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - ocfs2_set_new_buffer_uptodate(inode, new_bh); - - status = ocfs2_journal_access(handle, inode, new_bh, - OCFS2_JOURNAL_ACCESS_CREATE); - if (status < 0) { - mlog_errno(status); - goto bail; - } - memset(new_bh->b_data, 0, osb->sb->s_blocksize); - - de = (struct ocfs2_dir_entry *) new_bh->b_data; - de->inode = cpu_to_le64(OCFS2_I(inode)->ip_blkno); - de->name_len = 1; - de->rec_len = - cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len)); - strcpy(de->name, "."); - ocfs2_set_de_type(de, S_IFDIR); - de = (struct ocfs2_dir_entry *) ((char *)de + le16_to_cpu(de->rec_len)); - de->inode = cpu_to_le64(OCFS2_I(parent)->ip_blkno); - de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize - - OCFS2_DIR_REC_LEN(1)); - de->name_len = 2; - strcpy(de->name, ".."); - ocfs2_set_de_type(de, S_IFDIR); - - status = ocfs2_journal_dirty(handle, new_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - i_size_write(inode, inode->i_sb->s_blocksize); - inode->i_nlink = 2; - inode->i_blocks = ocfs2_inode_sector_count(inode); - status = ocfs2_mark_inode_dirty(handle, inode, fe_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - status = 0; -bail: - if (new_bh) - brelse(new_bh); - - mlog_exit(status); - return status; -} - static int ocfs2_mknod(struct inode *dir, struct dentry *dentry, int mode, @@ -365,9 +250,8 @@ static int ocfs2_mknod(struct inode *dir, goto leave; } - /* are we making a directory? If so, reserve a cluster for his - * 1st extent. */ - if (S_ISDIR(mode)) { + /* Reserve a cluster if creating an extent based directory. */ + if (S_ISDIR(mode) && !ocfs2_supports_inline_data(osb)) { status = ocfs2_reserve_clusters(osb, 1, &data_ac); if (status < 0) { if (status != -ENOSPC) @@ -564,10 +448,21 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb, cpu_to_le32(CURRENT_TIME.tv_nsec); fe->i_dtime = 0; - fel = &fe->id2.i_list; - fel->l_tree_depth = 0; - fel->l_next_free_rec = 0; - fel->l_count = cpu_to_le16(ocfs2_extent_recs_per_inode(osb->sb)); + /* + * If supported, directories start with inline data. + */ + if (S_ISDIR(mode) && ocfs2_supports_inline_data(osb)) { + u16 feat = le16_to_cpu(fe->i_dyn_features); + + fe->i_dyn_features = cpu_to_le16(feat | OCFS2_INLINE_DATA_FL); + + fe->id2.i_data.id_count = cpu_to_le16(ocfs2_max_inline_data(osb->sb)); + } else { + fel = &fe->id2.i_list; + fel->l_tree_depth = 0; + fel->l_next_free_rec = 0; + fel->l_count = cpu_to_le16(ocfs2_extent_recs_per_inode(osb->sb)); + } status = ocfs2_journal_dirty(handle, *new_fe_bh); if (status < 0) { @@ -1048,11 +943,6 @@ static void ocfs2_double_unlock(struct inode *inode1, struct inode *inode2) ocfs2_meta_unlock(inode2, 1); } -#define PARENT_INO(buffer) \ - ((struct ocfs2_dir_entry *) \ - ((char *)buffer + \ - le16_to_cpu(((struct ocfs2_dir_entry *)buffer)->rec_len)))->inode - static int ocfs2_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, @@ -1070,12 +960,12 @@ static int ocfs2_rename(struct inode *old_dir, struct buffer_head *old_inode_bh = NULL; struct buffer_head *insert_entry_bh = NULL; struct ocfs2_super *osb = NULL; - u64 newfe_blkno; + u64 newfe_blkno, old_de_ino; handle_t *handle = NULL; struct buffer_head *old_dir_bh = NULL; struct buffer_head *new_dir_bh = NULL; - struct ocfs2_dir_entry *old_de = NULL, *new_de = NULL; // dirent for old_dentry - // and new_dentry + struct ocfs2_dir_entry *old_inode_dot_dot_de = NULL, *old_de = NULL, + *new_de = NULL; struct buffer_head *new_de_bh = NULL, *old_de_bh = NULL; // bhs for above struct buffer_head *old_inode_de_bh = NULL; // if old_dentry is a dir, // this is the 1st dirent bh @@ -1159,27 +1049,35 @@ static int ocfs2_rename(struct inode *old_dir, } if (S_ISDIR(old_inode->i_mode)) { - status = -EIO; - old_inode_de_bh = ocfs2_bread(old_inode, 0, &status, 0); - if (!old_inode_de_bh) + u64 old_inode_parent; + + status = ocfs2_find_files_on_disk("..", 2, &old_inode_parent, + old_inode, &old_inode_de_bh, + &old_inode_dot_dot_de); + if (status) { + status = -EIO; goto bail; + } - status = -EIO; - if (le64_to_cpu(PARENT_INO(old_inode_de_bh->b_data)) != - OCFS2_I(old_dir)->ip_blkno) + if (old_inode_parent != OCFS2_I(old_dir)->ip_blkno) { + status = -EIO; goto bail; - status = -EMLINK; - if (!new_inode && new_dir!=old_dir && - new_dir->i_nlink >= OCFS2_LINK_MAX) + } + + if (!new_inode && new_dir != old_dir && + new_dir->i_nlink >= OCFS2_LINK_MAX) { + status = -EMLINK; goto bail; + } } - status = -ENOENT; - old_de_bh = ocfs2_find_entry(old_dentry->d_name.name, - old_dentry->d_name.len, - old_dir, &old_de); - if (!old_de_bh) + status = ocfs2_lookup_ino_from_name(old_dir, old_dentry->d_name.name, + old_dentry->d_name.len, + &old_de_ino); + if (status) { + status = -ENOENT; goto bail; + } /* * Check for inode number is _not_ due to possible IO errors. @@ -1187,8 +1085,10 @@ static int ocfs2_rename(struct inode *old_dir, * and merrily kill the link to whatever was created under the * same name. Goodbye sticky bit ;-< */ - if (le64_to_cpu(old_de->inode) != OCFS2_I(old_inode)->ip_blkno) + if (old_de_ino != OCFS2_I(old_inode)->ip_blkno) { + status = -ENOENT; goto bail; + } /* check if the target already exists (in which case we need * to delete it */ @@ -1321,20 +1221,13 @@ static int ocfs2_rename(struct inode *old_dir, } /* change the dirent to point to the correct inode */ - status = ocfs2_journal_access(handle, new_dir, new_de_bh, - OCFS2_JOURNAL_ACCESS_WRITE); + status = ocfs2_update_entry(new_dir, handle, new_de_bh, + new_de, old_inode); if (status < 0) { mlog_errno(status); goto bail; } - new_de->inode = cpu_to_le64(OCFS2_I(old_inode)->ip_blkno); - new_de->file_type = old_de->file_type; new_dir->i_version++; - status = ocfs2_journal_dirty(handle, new_de_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } if (S_ISDIR(new_inode->i_mode)) newfe->i_links_count = 0; @@ -1370,7 +1263,21 @@ static int ocfs2_rename(struct inode *old_dir, } else mlog_errno(status); - /* now that the name has been added to new_dir, remove the old name */ + /* + * Now that the name has been added to new_dir, remove the old name. + * + * We don't keep any directory entry context around until now + * because the insert might have changed the type of directory + * we're dealing with. + */ + old_de_bh = ocfs2_find_entry(old_dentry->d_name.name, + old_dentry->d_name.len, + old_dir, &old_de); + if (!old_de_bh) { + status = -EIO; + goto bail; + } + status = ocfs2_delete_entry(handle, old_dir, old_de, old_de_bh); if (status < 0) { mlog_errno(status); @@ -1383,12 +1290,8 @@ static int ocfs2_rename(struct inode *old_dir, } old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; if (old_inode_de_bh) { - status = ocfs2_journal_access(handle, old_inode, - old_inode_de_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - PARENT_INO(old_inode_de_bh->b_data) = - cpu_to_le64(OCFS2_I(new_dir)->ip_blkno); - status = ocfs2_journal_dirty(handle, old_inode_de_bh); + status = ocfs2_update_entry(old_inode, handle, old_inode_de_bh, + old_inode_dot_dot_de, new_dir); old_dir->i_nlink--; if (new_inode) { new_inode->i_nlink--; @@ -1767,329 +1670,6 @@ bail: return status; } -int ocfs2_check_dir_entry(struct inode * dir, - struct ocfs2_dir_entry * de, - struct buffer_head * bh, - unsigned long offset) -{ - const char *error_msg = NULL; - const int rlen = le16_to_cpu(de->rec_len); - - if (rlen < OCFS2_DIR_REC_LEN(1)) - error_msg = "rec_len is smaller than minimal"; - else if (rlen % 4 != 0) - error_msg = "rec_len % 4 != 0"; - else if (rlen < OCFS2_DIR_REC_LEN(de->name_len)) - error_msg = "rec_len is too small for name_len"; - else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize) - error_msg = "directory entry across blocks"; - - if (error_msg != NULL) - mlog(ML_ERROR, "bad entry in directory #%llu: %s - " - "offset=%lu, inode=%llu, rec_len=%d, name_len=%d\n", - (unsigned long long)OCFS2_I(dir)->ip_blkno, error_msg, - offset, (unsigned long long)le64_to_cpu(de->inode), rlen, - de->name_len); - return error_msg == NULL ? 1 : 0; -} - -/* we don't always have a dentry for what we want to add, so people - * like orphan dir can call this instead. - * - * If you pass me insert_bh, I'll skip the search of the other dir - * blocks and put the record in there. - */ -static int __ocfs2_add_entry(handle_t *handle, - struct inode *dir, - const char *name, int namelen, - struct inode *inode, u64 blkno, - struct buffer_head *parent_fe_bh, - struct buffer_head *insert_bh) -{ - unsigned long offset; - unsigned short rec_len; - struct ocfs2_dir_entry *de, *de1; - struct super_block *sb; - int retval, status; - - mlog_entry_void(); - - sb = dir->i_sb; - - if (!namelen) - return -EINVAL; - - rec_len = OCFS2_DIR_REC_LEN(namelen); - offset = 0; - de = (struct ocfs2_dir_entry *) insert_bh->b_data; - while (1) { - BUG_ON((char *)de >= sb->s_blocksize + insert_bh->b_data); - /* These checks should've already been passed by the - * prepare function, but I guess we can leave them - * here anyway. */ - if (!ocfs2_check_dir_entry(dir, de, insert_bh, offset)) { - retval = -ENOENT; - goto bail; - } - if (ocfs2_match(namelen, name, de)) { - retval = -EEXIST; - goto bail; - } - if (((le64_to_cpu(de->inode) == 0) && - (le16_to_cpu(de->rec_len) >= rec_len)) || - (le16_to_cpu(de->rec_len) >= - (OCFS2_DIR_REC_LEN(de->name_len) + rec_len))) { - dir->i_mtime = dir->i_ctime = CURRENT_TIME; - retval = ocfs2_mark_inode_dirty(handle, dir, parent_fe_bh); - if (retval < 0) { - mlog_errno(retval); - goto bail; - } - - status = ocfs2_journal_access(handle, dir, insert_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - /* By now the buffer is marked for journaling */ - offset += le16_to_cpu(de->rec_len); - if (le64_to_cpu(de->inode)) { - de1 = (struct ocfs2_dir_entry *)((char *) de + - OCFS2_DIR_REC_LEN(de->name_len)); - de1->rec_len = - cpu_to_le16(le16_to_cpu(de->rec_len) - - OCFS2_DIR_REC_LEN(de->name_len)); - de->rec_len = cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len)); - de = de1; - } - de->file_type = OCFS2_FT_UNKNOWN; - if (blkno) { - de->inode = cpu_to_le64(blkno); - ocfs2_set_de_type(de, inode->i_mode); - } else - de->inode = 0; - de->name_len = namelen; - memcpy(de->name, name, namelen); - - dir->i_version++; - status = ocfs2_journal_dirty(handle, insert_bh); - retval = 0; - goto bail; - } - offset += le16_to_cpu(de->rec_len); - de = (struct ocfs2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len)); - } - - /* when you think about it, the assert above should prevent us - * from ever getting here. */ - retval = -ENOSPC; -bail: - - mlog_exit(retval); - return retval; -} - - -/* - * ocfs2_delete_entry deletes a directory entry by merging it with the - * previous entry - */ -static int ocfs2_delete_entry(handle_t *handle, - struct inode *dir, - struct ocfs2_dir_entry *de_del, - struct buffer_head *bh) -{ - struct ocfs2_dir_entry *de, *pde; - int i, status = -ENOENT; - - mlog_entry("(0x%p, 0x%p, 0x%p, 0x%p)\n", handle, dir, de_del, bh); - - i = 0; - pde = NULL; - de = (struct ocfs2_dir_entry *) bh->b_data; - while (i < bh->b_size) { - if (!ocfs2_check_dir_entry(dir, de, bh, i)) { - status = -EIO; - mlog_errno(status); - goto bail; - } - if (de == de_del) { - status = ocfs2_journal_access(handle, dir, bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - status = -EIO; - mlog_errno(status); - goto bail; - } - if (pde) - pde->rec_len = - cpu_to_le16(le16_to_cpu(pde->rec_len) + - le16_to_cpu(de->rec_len)); - else - de->inode = 0; - dir->i_version++; - status = ocfs2_journal_dirty(handle, bh); - goto bail; - } - i += le16_to_cpu(de->rec_len); - pde = de; - de = (struct ocfs2_dir_entry *)((char *)de + le16_to_cpu(de->rec_len)); - } -bail: - mlog_exit(status); - return status; -} - -/* - * Returns 0 if not found, -1 on failure, and 1 on success - */ -static int inline ocfs2_search_dirblock(struct buffer_head *bh, - struct inode *dir, - const char *name, int namelen, - unsigned long offset, - struct ocfs2_dir_entry **res_dir) -{ - struct ocfs2_dir_entry *de; - char *dlimit, *de_buf; - int de_len; - int ret = 0; - - mlog_entry_void(); - - de_buf = bh->b_data; - dlimit = de_buf + dir->i_sb->s_blocksize; - - while (de_buf < dlimit) { - /* this code is executed quadratically often */ - /* do minimal checking `by hand' */ - - de = (struct ocfs2_dir_entry *) de_buf; - - if (de_buf + namelen <= dlimit && - ocfs2_match(namelen, name, de)) { - /* found a match - just to be sure, do a full check */ - if (!ocfs2_check_dir_entry(dir, de, bh, offset)) { - ret = -1; - goto bail; - } - *res_dir = de; - ret = 1; - goto bail; - } - - /* prevent looping on a bad block */ - de_len = le16_to_cpu(de->rec_len); - if (de_len <= 0) { - ret = -1; - goto bail; - } - - de_buf += de_len; - offset += de_len; - } - -bail: - mlog_exit(ret); - return ret; -} - -struct buffer_head *ocfs2_find_entry(const char *name, int namelen, - struct inode *dir, - struct ocfs2_dir_entry **res_dir) -{ - struct super_block *sb; - struct buffer_head *bh_use[NAMEI_RA_SIZE]; - struct buffer_head *bh, *ret = NULL; - unsigned long start, block, b; - int ra_max = 0; /* Number of bh's in the readahead - buffer, bh_use[] */ - int ra_ptr = 0; /* Current index into readahead - buffer */ - int num = 0; - int nblocks, i, err; - - mlog_entry_void(); - - *res_dir = NULL; - sb = dir->i_sb; - - nblocks = i_size_read(dir) >> sb->s_blocksize_bits; - start = OCFS2_I(dir)->ip_dir_start_lookup; - if (start >= nblocks) - start = 0; - block = start; - -restart: - do { - /* - * We deal with the read-ahead logic here. - */ - if (ra_ptr >= ra_max) { - /* Refill the readahead buffer */ - ra_ptr = 0; - b = block; - for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) { - /* - * Terminate if we reach the end of the - * directory and must wrap, or if our - * search has finished at this block. - */ - if (b >= nblocks || (num && block == start)) { - bh_use[ra_max] = NULL; - break; - } - num++; - - bh = ocfs2_bread(dir, b++, &err, 1); - bh_use[ra_max] = bh; - } - } - if ((bh = bh_use[ra_ptr++]) == NULL) - goto next; - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - /* read error, skip block & hope for the best */ - ocfs2_error(dir->i_sb, "reading directory %llu, " - "offset %lu\n", - (unsigned long long)OCFS2_I(dir)->ip_blkno, - block); - brelse(bh); - goto next; - } - i = ocfs2_search_dirblock(bh, dir, name, namelen, - block << sb->s_blocksize_bits, - res_dir); - if (i == 1) { - OCFS2_I(dir)->ip_dir_start_lookup = block; - ret = bh; - goto cleanup_and_exit; - } else { - brelse(bh); - if (i < 0) - goto cleanup_and_exit; - } - next: - if (++block >= nblocks) - block = 0; - } while (block != start); - - /* - * If the directory has grown while we were searching, then - * search the last part of the directory before giving up. - */ - block = nblocks; - nblocks = i_size_read(dir) >> sb->s_blocksize_bits; - if (block < nblocks) { - start = 0; - goto restart; - } - -cleanup_and_exit: - /* Clean up the read-ahead blocks */ - for (; ra_ptr < ra_max; ra_ptr++) - brelse(bh_use[ra_ptr]); - - mlog_exit_ptr(ret); - return ret; -} - static int ocfs2_blkno_stringify(u64 blkno, char *name) { int status, namelen; diff --git a/fs/ocfs2/namei.h b/fs/ocfs2/namei.h index 0975c7b7212..688aef64c87 100644 --- a/fs/ocfs2/namei.h +++ b/fs/ocfs2/namei.h @@ -30,29 +30,10 @@ extern const struct inode_operations ocfs2_dir_iops; struct dentry *ocfs2_get_parent(struct dentry *child); -int ocfs2_check_dir_entry (struct inode *dir, - struct ocfs2_dir_entry *de, - struct buffer_head *bh, - unsigned long offset); -struct buffer_head *ocfs2_find_entry(const char *name, - int namelen, - struct inode *dir, - struct ocfs2_dir_entry **res_dir); int ocfs2_orphan_del(struct ocfs2_super *osb, handle_t *handle, struct inode *orphan_dir_inode, struct inode *inode, struct buffer_head *orphan_dir_bh); -static inline int ocfs2_match(int len, - const char * const name, - struct ocfs2_dir_entry *de) -{ - if (len != de->name_len) - return 0; - if (!de->inode) - return 0; - return !memcmp(name, de->name, len); -} - #endif /* OCFS2_NAMEI_H */ diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 58307853fb4..60a23e1906b 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -319,6 +319,13 @@ static inline int ocfs2_writes_unwritten_extents(struct ocfs2_super *osb) return 0; } +static inline int ocfs2_supports_inline_data(struct ocfs2_super *osb) +{ + if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_INLINE_DATA) + return 1; + return 0; +} + /* set / clear functions because cluster events can make these happen * in parallel so we want the transitions to be atomic. this also * means that any future flags osb_flags must be protected by spinlock diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index 82f8a75b207..6ef876759a7 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -87,7 +87,8 @@ #define OCFS2_FEATURE_COMPAT_SUPP OCFS2_FEATURE_COMPAT_BACKUP_SB #define OCFS2_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT \ - | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) + | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \ + | OCFS2_FEATURE_INCOMPAT_INLINE_DATA) #define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN /* @@ -111,6 +112,20 @@ #define OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC 0x0010 /* + * Tunefs sets this incompat flag before starting an operation which + * would require cleanup on abort. This is done to protect users from + * inadvertently mounting the fs after an aborted run without + * fsck-ing. + * + * s_tunefs_flags on the super block describes precisely which + * operations were in progress. + */ +#define OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG 0x0020 + +/* Support for data packed into inode blocks */ +#define OCFS2_FEATURE_INCOMPAT_INLINE_DATA 0x0040 + +/* * backup superblock flag is used to indicate that this volume * has backup superblocks. */ @@ -130,6 +145,11 @@ #define OCFS2_MAX_BACKUP_SUPERBLOCKS 6 /* + * Flags on ocfs2_super_block.s_tunefs_flags + */ +#define OCFS2_TUNEFS_INPROG_REMOVE_SLOT 0x0001 /* Removing slots */ + +/* * Flags on ocfs2_dinode.i_flags */ #define OCFS2_VALID_FL (0x00000001) /* Inode is valid */ @@ -146,6 +166,17 @@ #define OCFS2_CHAIN_FL (0x00000400) /* Chain allocator */ #define OCFS2_DEALLOC_FL (0x00000800) /* Truncate log */ +/* + * Flags on ocfs2_dinode.i_dyn_features + * + * These can change much more often than i_flags. When adding flags, + * keep in mind that i_dyn_features is only 16 bits wide. + */ +#define OCFS2_INLINE_DATA_FL (0x0001) /* Data stored in inode block */ +#define OCFS2_HAS_XATTR_FL (0x0002) +#define OCFS2_INLINE_XATTR_FL (0x0004) +#define OCFS2_INDEXED_DIR_FL (0x0008) + /* Inode attributes, keep in sync with EXT2 */ #define OCFS2_SECRM_FL (0x00000001) /* Secure deletion */ #define OCFS2_UNRM_FL (0x00000002) /* Undelete */ @@ -447,8 +478,8 @@ struct ocfs2_super_block { __le32 s_clustersize_bits; /* Clustersize for this fs */ /*40*/ __le16 s_max_slots; /* Max number of simultaneous mounts before tunefs required */ - __le16 s_reserved1; - __le32 s_reserved2; + __le16 s_tunefs_flag; + __le32 s_reserved1; __le64 s_first_cluster_group; /* Block offset of 1st cluster * group header */ /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ @@ -471,6 +502,19 @@ struct ocfs2_local_alloc }; /* + * Data-in-inode header. This is only used if i_dyn_features has + * OCFS2_INLINE_DATA_FL set. + */ +struct ocfs2_inline_data +{ +/*00*/ __le16 id_count; /* Number of bytes that can be used + * for data, starting at id_data */ + __le16 id_reserved0; + __le32 id_reserved1; + __u8 id_data[0]; /* Start of user data */ +}; + +/* * On disk inode for OCFS2 */ struct ocfs2_dinode { @@ -502,7 +546,7 @@ struct ocfs2_dinode { __le32 i_attr; __le16 i_orphaned_slot; /* Only valid when OCFS2_ORPHANED_FL was set in i_flags */ - __le16 i_reserved1; + __le16 i_dyn_features; /*70*/ __le64 i_reserved2[8]; /*B8*/ union { __le64 i_pad1; /* Generic way to refer to this @@ -528,6 +572,7 @@ struct ocfs2_dinode { struct ocfs2_chain_list i_chain; struct ocfs2_extent_list i_list; struct ocfs2_truncate_log i_dealloc; + struct ocfs2_inline_data i_data; __u8 i_symlink[0]; } id2; /* Actual on-disk size is one block */ @@ -577,6 +622,12 @@ static inline int ocfs2_fast_symlink_chars(struct super_block *sb) offsetof(struct ocfs2_dinode, id2.i_symlink); } +static inline int ocfs2_max_inline_data(struct super_block *sb) +{ + return sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_data.id_data); +} + static inline int ocfs2_extent_recs_per_inode(struct super_block *sb) { int size; @@ -656,6 +707,11 @@ static inline int ocfs2_fast_symlink_chars(int blocksize) return blocksize - offsetof(struct ocfs2_dinode, id2.i_symlink); } +static inline int ocfs2_max_inline_data(int blocksize) +{ + return blocksize - offsetof(struct ocfs2_dinode, id2.i_data.id_data); +} + static inline int ocfs2_extent_recs_per_inode(int blocksize) { int size; diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index c034b5129c1..0e2a1b45bf9 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -39,6 +39,7 @@ #include <linux/parser.h> #include <linux/crc32.h> #include <linux/debugfs.h> +#include <linux/mount.h> #include <cluster/nodemanager.h> @@ -91,6 +92,7 @@ struct mount_options static int ocfs2_parse_options(struct super_block *sb, char *options, struct mount_options *mopt, int is_remount); +static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt); static void ocfs2_put_super(struct super_block *sb); static int ocfs2_mount_volume(struct super_block *sb); static int ocfs2_remount(struct super_block *sb, int *flags, char *data); @@ -105,7 +107,7 @@ static int ocfs2_sync_fs(struct super_block *sb, int wait); static int ocfs2_init_global_system_inodes(struct ocfs2_super *osb); static int ocfs2_init_local_system_inodes(struct ocfs2_super *osb); -static int ocfs2_release_system_inodes(struct ocfs2_super *osb); +static void ocfs2_release_system_inodes(struct ocfs2_super *osb); static int ocfs2_fill_local_node_info(struct ocfs2_super *osb); static int ocfs2_check_volume(struct ocfs2_super *osb); static int ocfs2_verify_volume(struct ocfs2_dinode *di, @@ -133,6 +135,7 @@ static const struct super_operations ocfs2_sops = { .write_super = ocfs2_write_super, .put_super = ocfs2_put_super, .remount_fs = ocfs2_remount, + .show_options = ocfs2_show_options, }; enum { @@ -177,7 +180,7 @@ static void ocfs2_write_super(struct super_block *sb) static int ocfs2_sync_fs(struct super_block *sb, int wait) { - int status = 0; + int status; tid_t target; struct ocfs2_super *osb = OCFS2_SB(sb); @@ -275,9 +278,9 @@ bail: return status; } -static int ocfs2_release_system_inodes(struct ocfs2_super *osb) +static void ocfs2_release_system_inodes(struct ocfs2_super *osb) { - int status = 0, i; + int i; struct inode *inode; mlog_entry_void(); @@ -302,8 +305,7 @@ static int ocfs2_release_system_inodes(struct ocfs2_super *osb) osb->root_inode = NULL; } - mlog_exit(status); - return status; + mlog_exit(0); } /* We're allocating fs objects, use GFP_NOFS */ @@ -453,7 +455,7 @@ static int ocfs2_sb_probe(struct super_block *sb, struct buffer_head **bh, int *sector_size) { - int status = 0, tmpstat; + int status, tmpstat; struct ocfs1_vol_disk_hdr *hdr; struct ocfs2_dinode *di; int blksize; @@ -830,6 +832,41 @@ bail: return status; } +static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) +{ + struct ocfs2_super *osb = OCFS2_SB(mnt->mnt_sb); + unsigned long opts = osb->s_mount_opt; + + if (opts & OCFS2_MOUNT_HB_LOCAL) + seq_printf(s, ",_netdev,heartbeat=local"); + else + seq_printf(s, ",heartbeat=none"); + + if (opts & OCFS2_MOUNT_NOINTR) + seq_printf(s, ",nointr"); + + if (opts & OCFS2_MOUNT_DATA_WRITEBACK) + seq_printf(s, ",data=writeback"); + else + seq_printf(s, ",data=ordered"); + + if (opts & OCFS2_MOUNT_BARRIER) + seq_printf(s, ",barrier=1"); + + if (opts & OCFS2_MOUNT_ERRORS_PANIC) + seq_printf(s, ",errors=panic"); + else + seq_printf(s, ",errors=remount-ro"); + + if (osb->preferred_slot != OCFS2_INVALID_SLOT) + seq_printf(s, ",preferred_slot=%d", osb->preferred_slot); + + if (osb->s_atime_quantum != OCFS2_DEFAULT_ATIME_QUANTUM) + seq_printf(s, ",atime_quantum=%u", osb->s_atime_quantum); + + return 0; +} + static int __init ocfs2_init(void) { int status; @@ -1209,12 +1246,13 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) tmp = ocfs2_request_umount_vote(osb); if (tmp < 0) mlog_errno(tmp); + } - if (osb->slot_num != OCFS2_INVALID_SLOT) - ocfs2_put_slot(osb); + if (osb->slot_num != OCFS2_INVALID_SLOT) + ocfs2_put_slot(osb); + if (osb->dlm) ocfs2_super_unlock(osb, 1); - } ocfs2_release_system_inodes(osb); @@ -1275,7 +1313,7 @@ static int ocfs2_initialize_super(struct super_block *sb, struct buffer_head *bh, int sector_size) { - int status = 0; + int status; int i, cbits, bbits; struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; struct inode *inode = NULL; @@ -1596,7 +1634,7 @@ static int ocfs2_verify_volume(struct ocfs2_dinode *di, static int ocfs2_check_volume(struct ocfs2_super *osb) { - int status = 0; + int status; int dirty; int local; struct ocfs2_dinode *local_alloc = NULL; /* only used if we diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c index 5df6e35d09b..fd2e846e3e6 100644 --- a/fs/ocfs2/sysfile.c +++ b/fs/ocfs2/sysfile.c @@ -100,17 +100,14 @@ static struct inode * _ocfs2_get_system_file_inode(struct ocfs2_super *osb, char namebuf[40]; struct inode *inode = NULL; u64 blkno; - struct buffer_head *dirent_bh = NULL; - struct ocfs2_dir_entry *de = NULL; int status = 0; ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, slot); - status = ocfs2_find_files_on_disk(namebuf, strlen(namebuf), - &blkno, osb->sys_root_inode, - &dirent_bh, &de); + status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, namebuf, + strlen(namebuf), &blkno); if (status < 0) { goto bail; } @@ -122,8 +119,7 @@ static struct inode * _ocfs2_get_system_file_inode(struct ocfs2_super *osb, goto bail; } bail: - if (dirent_bh) - brelse(dirent_bh); + return inode; } diff --git a/include/asm-s390/cache.h b/include/asm-s390/cache.h index cdf431b061b..9b866816863 100644 --- a/include/asm-s390/cache.h +++ b/include/asm-s390/cache.h @@ -14,8 +14,6 @@ #define L1_CACHE_BYTES 256 #define L1_CACHE_SHIFT 8 -#define ARCH_KMALLOC_MINALIGN 8 - #define __read_mostly __attribute__((__section__(".data.read_mostly"))) #endif diff --git a/include/asm-s390/ccwdev.h b/include/asm-s390/ccwdev.h index 1aeda27d5a8..066aa70518c 100644 --- a/include/asm-s390/ccwdev.h +++ b/include/asm-s390/ccwdev.h @@ -67,36 +67,55 @@ ccw_device_id_match(const struct ccw_device_id *array, return NULL; } -/* The struct ccw device is our replacement for the globally accessible - * ioinfo array. ioinfo will mutate into a subchannel device later. +/** + * struct ccw_device - channel attached device + * @ccwlock: pointer to device lock + * @id: id of this device + * @drv: ccw driver for this device + * @dev: embedded device structure + * @online: online status of device + * @handler: interrupt handler * - * Reference: Documentation/s390/driver-model.txt */ + * @handler is a member of the device rather than the driver since a driver + * can have different interrupt handlers for different ccw devices + * (multi-subchannel drivers). + */ struct ccw_device { spinlock_t *ccwlock; +/* private: */ struct ccw_device_private *private; /* cio private information */ - struct ccw_device_id id; /* id of this device, driver_info is - set by ccw_find_driver */ - struct ccw_driver *drv; /* */ - struct device dev; /* */ +/* public: */ + struct ccw_device_id id; + struct ccw_driver *drv; + struct device dev; int online; - /* This is sick, but a driver can have different interrupt handlers - for different ccw_devices (multi-subchannel drivers)... */ void (*handler) (struct ccw_device *, unsigned long, struct irb *); }; -/* Each ccw driver registers with the ccw root bus */ +/** + * struct ccw driver - device driver for channel attached devices + * @owner: owning module + * @ids: ids supported by this driver + * @probe: function called on probe + * @remove: function called on remove + * @set_online: called when setting device online + * @set_offline: called when setting device offline + * @notify: notify driver of device state changes + * @shutdown: called at device shutdown + * @driver: embedded device driver structure + * @name: device driver name + */ struct ccw_driver { - struct module *owner; /* for automatic MOD_INC_USE_COUNT */ - struct ccw_device_id *ids; /* probe driver with these devs */ - int (*probe) (struct ccw_device *); /* ask driver to probe dev */ + struct module *owner; + struct ccw_device_id *ids; + int (*probe) (struct ccw_device *); void (*remove) (struct ccw_device *); - /* device is no longer available */ int (*set_online) (struct ccw_device *); int (*set_offline) (struct ccw_device *); int (*notify) (struct ccw_device *, int); - struct device_driver driver; /* higher level structure, don't init - this from your driver */ + void (*shutdown) (struct ccw_device *); + struct device_driver driver; char *name; }; @@ -124,36 +143,10 @@ extern void ccw_device_clear_options(struct ccw_device *, unsigned long); /* Allow forced onlining of boxed devices. */ #define CCWDEV_ALLOW_FORCE 0x0008 -/* - * ccw_device_start() - * - * Start a S/390 channel program. When the interrupt arrives, the - * IRQ handler is called, either immediately, delayed (dev-end missing, - * or sense required) or never (no IRQ handler registered). - * Depending on the action taken, ccw_device_start() returns: - * 0 - Success - * -EBUSY - Device busy, or status pending - * -ENODEV - Device not operational - * -EINVAL - Device invalid for operation - */ extern int ccw_device_start(struct ccw_device *, struct ccw1 *, unsigned long, __u8, unsigned long); -/* - * ccw_device_start_timeout() - * - * This function notifies the device driver if the channel program has not - * completed during the specified time. If a timeout occurs, the channel - * program is terminated via xsch(), hsch() or csch(). - */ extern int ccw_device_start_timeout(struct ccw_device *, struct ccw1 *, unsigned long, __u8, unsigned long, int); -/* - * ccw_device_start_key() - * ccw_device_start_key_timeout() - * - * Same as ccw_device_start() and ccw_device_start_timeout(), except a - * storage key != default key can be provided for the I/O. - */ extern int ccw_device_start_key(struct ccw_device *, struct ccw1 *, unsigned long, __u8, __u8, unsigned long); extern int ccw_device_start_timeout_key(struct ccw_device *, struct ccw1 *, diff --git a/include/asm-s390/ccwgroup.h b/include/asm-s390/ccwgroup.h index 925b3ddfa14..7109c7cab87 100644 --- a/include/asm-s390/ccwgroup.h +++ b/include/asm-s390/ccwgroup.h @@ -4,19 +4,41 @@ struct ccw_device; struct ccw_driver; +/** + * struct ccwgroup_device - ccw group device + * @creator_id: unique number of the driver + * @state: online/offline state + * @count: number of attached slave devices + * @dev: embedded device structure + * @cdev: variable number of slave devices, allocated as needed + */ struct ccwgroup_device { - unsigned long creator_id; /* unique number of the driver */ + unsigned long creator_id; enum { CCWGROUP_OFFLINE, CCWGROUP_ONLINE, } state; +/* private: */ atomic_t onoff; struct mutex reg_mutex; - unsigned int count; /* number of attached slave devices */ - struct device dev; /* master device */ - struct ccw_device *cdev[0]; /* variable number, allocate as needed */ +/* public: */ + unsigned int count; + struct device dev; + struct ccw_device *cdev[0]; }; +/** + * struct ccwgroup_driver - driver for ccw group devices + * @owner: driver owner + * @name: driver name + * @max_slaves: maximum number of slave devices + * @driver_id: unique id + * @probe: function called on probe + * @remove: function called on remove + * @set_online: function called when device is set online + * @set_offline: function called when device is set offline + * @driver: embedded driver structure + */ struct ccwgroup_driver { struct module *owner; char *name; @@ -28,7 +50,7 @@ struct ccwgroup_driver { int (*set_online) (struct ccwgroup_device *); int (*set_offline) (struct ccwgroup_device *); - struct device_driver driver; /* this driver */ + struct device_driver driver; }; extern int ccwgroup_driver_register (struct ccwgroup_driver *cdriver); diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h index 1982fb34416..2f08c16e44a 100644 --- a/include/asm-s390/cio.h +++ b/include/asm-s390/cio.h @@ -15,30 +15,50 @@ #define LPM_ANYPATH 0xff #define __MAX_CSSID 0 -/* - * subchannel status word +/** + * struct scsw - subchannel status word + * @key: subchannel key + * @sctl: suspend control + * @eswf: esw format + * @cc: deferred condition code + * @fmt: format + * @pfch: prefetch + * @isic: initial-status interruption control + * @alcc: adress-limit checking control + * @ssi: supress-suspended interruption + * @zcc: zero condition code + * @ectl: extended control + * @pno: path not operational + * @res: reserved + * @fctl: function control + * @actl: activity control + * @stctl: status control + * @cpa: channel program address + * @dstat: device status + * @cstat: subchannel status + * @count: residual count */ struct scsw { - __u32 key : 4; /* subchannel key */ - __u32 sctl : 1; /* suspend control */ - __u32 eswf : 1; /* ESW format */ - __u32 cc : 2; /* deferred condition code */ - __u32 fmt : 1; /* format */ - __u32 pfch : 1; /* prefetch */ - __u32 isic : 1; /* initial-status interruption control */ - __u32 alcc : 1; /* address-limit checking control */ - __u32 ssi : 1; /* supress-suspended interruption */ - __u32 zcc : 1; /* zero condition code */ - __u32 ectl : 1; /* extended control */ - __u32 pno : 1; /* path not operational */ - __u32 res : 1; /* reserved */ - __u32 fctl : 3; /* function control */ - __u32 actl : 7; /* activity control */ - __u32 stctl : 5; /* status control */ - __u32 cpa; /* channel program address */ - __u32 dstat : 8; /* device status */ - __u32 cstat : 8; /* subchannel status */ - __u32 count : 16; /* residual count */ + __u32 key : 4; + __u32 sctl : 1; + __u32 eswf : 1; + __u32 cc : 2; + __u32 fmt : 1; + __u32 pfch : 1; + __u32 isic : 1; + __u32 alcc : 1; + __u32 ssi : 1; + __u32 zcc : 1; + __u32 ectl : 1; + __u32 pno : 1; + __u32 res : 1; + __u32 fctl : 3; + __u32 actl : 7; + __u32 stctl : 5; + __u32 cpa; + __u32 dstat : 8; + __u32 cstat : 8; + __u32 count : 16; } __attribute__ ((packed)); #define SCSW_FCTL_CLEAR_FUNC 0x1 @@ -110,11 +130,22 @@ struct scsw { #define SNS2_ENV_DATA_PRESENT 0x10 #define SNS2_INPRECISE_END 0x04 +/** + * struct ccw1 - channel command word + * @cmd_code: command code + * @flags: flags, like IDA adressing, etc. + * @count: byte count + * @cda: data address + * + * The ccw is the basic structure to build channel programs that perform + * operations with the device or the control unit. Only Format-1 channel + * command words are supported. + */ struct ccw1 { - __u8 cmd_code; /* command code */ - __u8 flags; /* flags, like IDA addressing, etc. */ - __u16 count; /* byte count */ - __u32 cda; /* data address */ + __u8 cmd_code; + __u8 flags; + __u16 count; + __u32 cda; } __attribute__ ((packed,aligned(8))); #define CCW_FLAG_DC 0x80 @@ -140,102 +171,162 @@ struct ccw1 { #define SENSE_MAX_COUNT 0x20 +/** + * struct erw - extended report word + * @res0: reserved + * @auth: authorization check + * @pvrf: path-verification-required flag + * @cpt: channel-path timeout + * @fsavf: failing storage address validity flag + * @cons: concurrent sense + * @scavf: secondary ccw address validity flag + * @fsaf: failing storage address format + * @scnt: sense count, if @cons == %1 + * @res16: reserved + */ struct erw { - __u32 res0 : 3; /* reserved */ - __u32 auth : 1; /* Authorization check */ - __u32 pvrf : 1; /* path-verification-required flag */ - __u32 cpt : 1; /* channel-path timeout */ - __u32 fsavf : 1; /* Failing storage address validity flag */ - __u32 cons : 1; /* concurrent-sense */ - __u32 scavf : 1; /* Secondary ccw address validity flag */ - __u32 fsaf : 1; /* Failing storage address format */ - __u32 scnt : 6; /* sense count if cons == 1 */ - __u32 res16 : 16; /* reserved */ + __u32 res0 : 3; + __u32 auth : 1; + __u32 pvrf : 1; + __u32 cpt : 1; + __u32 fsavf : 1; + __u32 cons : 1; + __u32 scavf : 1; + __u32 fsaf : 1; + __u32 scnt : 6; + __u32 res16 : 16; } __attribute__ ((packed)); -/* - * subchannel logout area +/** + * struct sublog - subchannel logout area + * @res0: reserved + * @esf: extended status flags + * @lpum: last path used mask + * @arep: ancillary report + * @fvf: field-validity flags + * @sacc: storage access code + * @termc: termination code + * @devsc: device-status check + * @serr: secondary error + * @ioerr: i/o-error alert + * @seqc: sequence code */ struct sublog { - __u32 res0 : 1; /* reserved */ - __u32 esf : 7; /* extended status flags */ - __u32 lpum : 8; /* last path used mask */ - __u32 arep : 1; /* ancillary report */ - __u32 fvf : 5; /* field-validity flags */ - __u32 sacc : 2; /* storage access code */ - __u32 termc : 2; /* termination code */ - __u32 devsc : 1; /* device-status check */ - __u32 serr : 1; /* secondary error */ - __u32 ioerr : 1; /* i/o-error alert */ - __u32 seqc : 3; /* sequence code */ + __u32 res0 : 1; + __u32 esf : 7; + __u32 lpum : 8; + __u32 arep : 1; + __u32 fvf : 5; + __u32 sacc : 2; + __u32 termc : 2; + __u32 devsc : 1; + __u32 serr : 1; + __u32 ioerr : 1; + __u32 seqc : 3; } __attribute__ ((packed)); -/* - * Format 0 Extended Status Word (ESW) +/** + * struct esw0 - Format 0 Extended Status Word (ESW) + * @sublog: subchannel logout + * @erw: extended report word + * @faddr: failing storage address + * @saddr: secondary ccw address */ struct esw0 { - struct sublog sublog; /* subchannel logout */ - struct erw erw; /* extended report word */ - __u32 faddr[2]; /* failing storage address */ - __u32 saddr; /* secondary ccw address */ + struct sublog sublog; + struct erw erw; + __u32 faddr[2]; + __u32 saddr; } __attribute__ ((packed)); -/* - * Format 1 Extended Status Word (ESW) +/** + * struct esw1 - Format 1 Extended Status Word (ESW) + * @zero0: reserved zeros + * @lpum: last path used mask + * @zero16: reserved zeros + * @erw: extended report word + * @zeros: three fullwords of zeros */ struct esw1 { - __u8 zero0; /* reserved zeros */ - __u8 lpum; /* last path used mask */ - __u16 zero16; /* reserved zeros */ - struct erw erw; /* extended report word */ - __u32 zeros[3]; /* 2 fullwords of zeros */ + __u8 zero0; + __u8 lpum; + __u16 zero16; + struct erw erw; + __u32 zeros[3]; } __attribute__ ((packed)); -/* - * Format 2 Extended Status Word (ESW) +/** + * struct esw2 - Format 2 Extended Status Word (ESW) + * @zero0: reserved zeros + * @lpum: last path used mask + * @dcti: device-connect-time interval + * @erw: extended report word + * @zeros: three fullwords of zeros */ struct esw2 { - __u8 zero0; /* reserved zeros */ - __u8 lpum; /* last path used mask */ - __u16 dcti; /* device-connect-time interval */ - struct erw erw; /* extended report word */ - __u32 zeros[3]; /* 2 fullwords of zeros */ + __u8 zero0; + __u8 lpum; + __u16 dcti; + struct erw erw; + __u32 zeros[3]; } __attribute__ ((packed)); -/* - * Format 3 Extended Status Word (ESW) +/** + * struct esw3 - Format 3 Extended Status Word (ESW) + * @zero0: reserved zeros + * @lpum: last path used mask + * @res: reserved + * @erw: extended report word + * @zeros: three fullwords of zeros */ struct esw3 { - __u8 zero0; /* reserved zeros */ - __u8 lpum; /* last path used mask */ - __u16 res; /* reserved */ - struct erw erw; /* extended report word */ - __u32 zeros[3]; /* 2 fullwords of zeros */ + __u8 zero0; + __u8 lpum; + __u16 res; + struct erw erw; + __u32 zeros[3]; } __attribute__ ((packed)); -/* - * interruption response block +/** + * struct irb - interruption response block + * @scsw: subchannel status word + * @esw: extened status word, 4 formats + * @ecw: extended control word + * + * The irb that is handed to the device driver when an interrupt occurs. For + * solicited interrupts, the common I/O layer already performs checks whether + * a field is valid; a field not being valid is always passed as %0. + * If a unit check occured, @ecw may contain sense data; this is retrieved + * by the common I/O layer itself if the device doesn't support concurrent + * sense (so that the device driver never needs to perform basic sene itself). + * For unsolicited interrupts, the irb is passed as-is (expect for sense data, + * if applicable). */ struct irb { - struct scsw scsw; /* subchannel status word */ - union { /* extended status word, 4 formats */ + struct scsw scsw; + union { struct esw0 esw0; struct esw1 esw1; struct esw2 esw2; struct esw3 esw3; } esw; - __u8 ecw[32]; /* extended control word */ + __u8 ecw[32]; } __attribute__ ((packed,aligned(4))); -/* - * command information word (CIW) layout +/** + * struct ciw - command information word (CIW) layout + * @et: entry type + * @reserved: reserved bits + * @ct: command type + * @cmd: command code + * @count: command count */ struct ciw { - __u32 et : 2; /* entry type */ - __u32 reserved : 2; /* reserved */ - __u32 ct : 4; /* command type */ - __u32 cmd : 8; /* command */ - __u32 count : 16; /* coun */ + __u32 et : 2; + __u32 reserved : 2; + __u32 ct : 4; + __u32 cmd : 8; + __u32 count : 16; } __attribute__ ((packed)); #define CIW_TYPE_RCD 0x0 /* read configuration data */ @@ -258,11 +349,32 @@ struct ciw { /* Sick revalidation of device. */ #define CIO_REVALIDATE 0x0008 +/** + * struct ccw_dev_id - unique identifier for ccw devices + * @ssid: subchannel set id + * @devno: device number + * + * This structure is not directly based on any hardware structure. The + * hardware identifies a device by its device number and its subchannel, + * which is in turn identified by its id. In order to get a unique identifier + * for ccw devices across subchannel sets, @struct ccw_dev_id has been + * introduced. + */ struct ccw_dev_id { u8 ssid; u16 devno; }; +/** + * ccw_device_id_is_equal() - compare two ccw_dev_ids + * @dev_id1: a ccw_dev_id + * @dev_id2: another ccw_dev_id + * Returns: + * %1 if the two structures are equal field-by-field, + * %0 if not. + * Context: + * any + */ static inline int ccw_dev_id_is_equal(struct ccw_dev_id *dev_id1, struct ccw_dev_id *dev_id2) { diff --git a/include/asm-s390/cmb.h b/include/asm-s390/cmb.h index 021e7c3223e..50196857d27 100644 --- a/include/asm-s390/cmb.h +++ b/include/asm-s390/cmb.h @@ -1,29 +1,29 @@ #ifndef S390_CMB_H #define S390_CMB_H /** - * struct cmbdata -- channel measurement block data for user space + * struct cmbdata - channel measurement block data for user space + * @size: size of the stored data + * @elapsed_time: time since last sampling + * @ssch_rsch_count: number of ssch and rsch + * @sample_count: number of samples + * @device_connect_time: time of device connect + * @function_pending_time: time of function pending + * @device_disconnect_time: time of device disconnect + * @control_unit_queuing_time: time of control unit queuing + * @device_active_only_time: time of device active only + * @device_busy_time: time of device busy (ext. format) + * @initial_command_response_time: initial command response time (ext. format) * - * @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 + * 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 + * Potentially, more fields could be added, which would result in a * new ioctl number. - **/ + */ struct cmbdata { __u64 size; __u64 elapsed_time; @@ -41,53 +41,18 @@ struct cmbdata { }; /* enable channel measurement */ -#define BIODASDCMFENABLE _IO(DASD_IOCTL_LETTER,32) +#define BIODASDCMFENABLE _IO(DASD_IOCTL_LETTER, 32) /* enable channel measurement */ -#define BIODASDCMFDISABLE _IO(DASD_IOCTL_LETTER,33) +#define BIODASDCMFDISABLE _IO(DASD_IOCTL_LETTER, 33) /* read channel measurement data */ -#define BIODASDREADALLCMB _IOWR(DASD_IOCTL_LETTER,33,struct cmbdata) +#define BIODASDREADALLCMB _IOWR(DASD_IOCTL_LETTER, 33, struct cmbdata) #ifdef __KERNEL__ struct ccw_device; -/** - * 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 int cmf_readall(struct ccw_device *cdev, struct cmbdata *data); #endif /* __KERNEL__ */ #endif /* S390_CMB_H */ diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index f326451ed6e..ceec3826a67 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h @@ -9,11 +9,12 @@ #ifndef _S390_PAGE_H #define _S390_PAGE_H +#include <linux/const.h> #include <asm/types.h> /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 -#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) #define PAGE_DEFAULT_ACC 0 #define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4) diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index 3208dc6c412..39bb5192dc3 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -107,11 +107,18 @@ extern char empty_zero_page[PAGE_SIZE]; * any out-of-bounds memory accesses will hopefully be caught. * The vmalloc() routines leaves a hole of 4kB between each vmalloced * area for the same reason. ;) + * vmalloc area starts at 4GB to prevent syscall table entry exchanging + * from modules. */ extern unsigned long vmalloc_end; -#define VMALLOC_OFFSET (8*1024*1024) -#define VMALLOC_START (((unsigned long) high_memory + VMALLOC_OFFSET) \ - & ~(VMALLOC_OFFSET-1)) + +#ifdef CONFIG_64BIT +#define VMALLOC_ADDR (max(0x100000000UL, (unsigned long) high_memory)) +#else +#define VMALLOC_ADDR ((unsigned long) high_memory) +#endif +#define VMALLOC_OFFSET (8*1024*1024) +#define VMALLOC_START ((VMALLOC_ADDR + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_END vmalloc_end /* diff --git a/include/asm-s390/s390_ext.h b/include/asm-s390/s390_ext.h index 1e72362cad7..2afc060266a 100644 --- a/include/asm-s390/s390_ext.h +++ b/include/asm-s390/s390_ext.h @@ -5,7 +5,7 @@ * include/asm-s390/s390_ext.h * * S390 version - * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright IBM Corp. 1999,2007 * Author(s): Holger Smolinski (Holger.Smolinski@de.ibm.com), * Martin Schwidefsky (schwidefsky@de.ibm.com) */ @@ -14,15 +14,11 @@ typedef void (*ext_int_handler_t)(__u16 code); -/* - * Warning: if you change ext_int_info_t you have to change the - * external interrupt handler in entry.S too. - */ typedef struct ext_int_info_t { struct ext_int_info_t *next; ext_int_handler_t handler; __u16 code; -} __attribute__ ((packed)) ext_int_info_t; +} ext_int_info_t; extern ext_int_info_t *ext_int_hash[]; diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 64a3cd05cae..d866d338555 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -130,6 +130,8 @@ extern void pfault_fini(void); __ret; \ }) +extern void __xchg_called_with_bad_pointer(void); + static inline unsigned long __xchg(unsigned long x, void * ptr, int size) { unsigned long addr, old; @@ -150,8 +152,7 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) : "=&d" (old), "=m" (*(int *) addr) : "d" (x << shift), "d" (~(255 << shift)), "a" (addr), "m" (*(int *) addr) : "memory", "cc", "0"); - x = old >> shift; - break; + return old >> shift; case 2: addr = (unsigned long) ptr; shift = (2 ^ (addr & 2)) << 3; @@ -166,8 +167,7 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) : "=&d" (old), "=m" (*(int *) addr) : "d" (x << shift), "d" (~(65535 << shift)), "a" (addr), "m" (*(int *) addr) : "memory", "cc", "0"); - x = old >> shift; - break; + return old >> shift; case 4: asm volatile( " l %0,0(%3)\n" @@ -176,8 +176,7 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) : "=&d" (old), "=m" (*(int *) ptr) : "d" (x), "a" (ptr), "m" (*(int *) ptr) : "memory", "cc"); - x = old; - break; + return old; #ifdef __s390x__ case 8: asm volatile( @@ -187,11 +186,11 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) : "=&d" (old), "=m" (*(long *) ptr) : "d" (x), "a" (ptr), "m" (*(long *) ptr) : "memory", "cc"); - x = old; - break; + return old; #endif /* __s390x__ */ - } - return x; + } + __xchg_called_with_bad_pointer(); + return x; } /* @@ -206,6 +205,8 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ (unsigned long)(n),sizeof(*(ptr)))) +extern void __cmpxchg_called_with_bad_pointer(void); + static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) { @@ -270,7 +271,8 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) return prev; #endif /* __s390x__ */ } - return old; + __cmpxchg_called_with_bad_pointer(); + return old; } /* diff --git a/include/asm-s390/zcrypt.h b/include/asm-s390/zcrypt.h index b90e55888a5..a5dada61775 100644 --- a/include/asm-s390/zcrypt.h +++ b/include/asm-s390/zcrypt.h @@ -91,7 +91,7 @@ struct ica_rsa_modexpo_crt { * VUD block * key block */ -struct ica_CPRBX { +struct CPRBX { unsigned short cprb_len; /* CPRB length 220 */ unsigned char cprb_ver_id; /* CPRB version id. 0x02 */ unsigned char pad_000[3]; /* Alignment pad bytes */ @@ -130,7 +130,7 @@ struct ica_CPRBX { unsigned char cntrl_domain[4];/* Control domain */ unsigned char S390enf_mask[4];/* S/390 enforcement mask */ unsigned char pad_004[36]; /* reserved */ -}; +} __attribute__((packed)); /** * xcRB diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 95be0ac57e7..5ed888b04b2 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -20,20 +20,6 @@ #include <asm/scatterlist.h> -#ifdef CONFIG_LBD -# include <asm/div64.h> -# define sector_div(a, b) do_div(a, b) -#else -# define sector_div(n, b)( \ -{ \ - int _res; \ - _res = (n) % (b); \ - (n) /= (b); \ - _res; \ -} \ -) -#endif - struct scsi_ioctl_command; struct request_queue; diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 2e105a12fe2..7e11d23ac36 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -290,12 +290,7 @@ static inline void blk_add_trace_remap(struct request_queue *q, struct bio *bio, #define blk_add_trace_generic(q, rq, rw, what) do { } while (0) #define blk_add_trace_pdu_int(q, what, bio, pdu) do { } while (0) #define blk_add_trace_remap(q, bio, dev, f, t) do {} while (0) -static inline int do_blk_trace_setup(struct request_queue *q, - struct block_device *bdev, - struct blk_user_trace_setup *buts) -{ - return 0; -} +#define do_blk_trace_setup(q, bdev, buts) (-ENOTTY) #endif /* CONFIG_BLK_DEV_IO_TRACE */ #endif /* __KERNEL__ */ #endif diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h index a44a6a078f0..c3c19f926e6 100644 --- a/include/linux/gfs2_ondisk.h +++ b/include/linux/gfs2_ondisk.h @@ -170,6 +170,33 @@ struct gfs2_rgrp { }; /* + * quota linked list: user quotas and group quotas form two separate + * singly linked lists. ll_next stores uids or gids of next quotas in the + * linked list. + +Given the uid/gid, how to calculate the quota file offsets for the corresponding +gfs2_quota structures on disk: + +for user quotas, given uid, +offset = uid * sizeof(struct gfs2_quota); + +for group quotas, given gid, +offset = (gid * sizeof(struct gfs2_quota)) + sizeof(struct gfs2_quota); + + + uid:0 gid:0 uid:12 gid:12 uid:17 gid:17 uid:5142 gid:5142 ++-------+-------+ +-------+-------+ +-------+- - - -+ +- - - -+-------+ +| valid | valid | :: | valid | valid | :: | valid | inval | :: | inval | valid | ++-------+-------+ +-------+-------+ +-------+- - - -+ +- - - -+-------+ +next:12 next:12 next:17 next:5142 next:NULL next:NULL + | | | | |<-- user quota list | + \______|___________/ \______|___________/ group quota list -->| + | | | + \__________________/ \_______________________________________/ + +*/ + +/* * quota structure */ @@ -177,7 +204,8 @@ struct gfs2_quota { __be64 qu_limit; __be64 qu_warn; __be64 qu_value; - __u8 qu_reserved[64]; + __be32 qu_ll_next; /* location of next quota in list */ + __u8 qu_reserved[60]; }; /* diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 47160fe378c..d9725a28a26 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -42,6 +42,20 @@ extern const char linux_proc_banner[]; #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#ifdef CONFIG_LBD +# include <asm/div64.h> +# define sector_div(a, b) do_div(a, b) +#else +# define sector_div(n, b)( \ +{ \ + int _res; \ + _res = (n) % (b); \ + (n) /= (b); \ + _res; \ +} \ +) +#endif + /** * upper_32_bits - return bits 32-63 of a number * @n: the number we're accessing |