From b7b0f33ff804ab7ee14ee6f2a4eac2023aff75ce Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 22 Jan 2004 22:58:38 -0800 Subject: [SCTP] Add sysctl parameters to update socket send/receive buffers. --- include/linux/sysctl.h | 2 ++ include/net/sctp/constants.h | 2 +- include/net/sctp/structs.h | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index f2f4a8a613c4..0ea4f81689e9 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -578,6 +578,8 @@ enum { NET_SCTP_PRESERVE_ENABLE = 11, NET_SCTP_MAX_BURST = 12, NET_SCTP_ADDIP_ENABLE = 13, + NET_SCTP_RMEM = 14, + NET_SCTP_WMEM = 15, }; /* /proc/sys/net/bridge */ diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index f031313bb3ef..6112e85f7a9c 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -323,7 +323,7 @@ typedef enum { #define SCTP_DEFAULT_COOKIE_LIFE_USEC 0 /* microseconds */ #define SCTP_DEFAULT_MINWINDOW 1500 /* default minimum rwnd size */ -#define SCTP_DEFAULT_MAXWINDOW 32768 /* default rwnd size */ +#define SCTP_DEFAULT_MAXWINDOW 65535 /* default rwnd size */ #define SCTP_DEFAULT_MAXSEGMENT 1500 /* MTU size, this is the limit * to which we will raise the P-MTU. */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 92394f3de76a..fa98456f5a0b 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -193,6 +193,10 @@ extern struct sctp_globals { /* Flag to indicate if addip is enabled. */ int addip_enable; + + /* socket receive and send buffer sizes. */ + int rmem; + int wmem; } sctp_globals; #define sctp_rto_initial (sctp_globals.rto_initial) @@ -221,6 +225,8 @@ extern struct sctp_globals { #define sctp_local_addr_list (sctp_globals.local_addr_list) #define sctp_local_addr_lock (sctp_globals.local_addr_lock) #define sctp_addip_enable (sctp_globals.addip_enable) +#define sctp_rmem (sctp_globals.rmem) +#define sctp_wmem (sctp_globals.wmem) /* SCTP Socket type: UDP or TCP style. */ typedef enum { -- cgit v1.2.3 From a2e1b35b1ccdd67e9f1b7a0c83d39968059286ca Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Tue, 27 Jan 2004 13:54:34 -0500 Subject: [ACPI] Check for overflow when parsing MADT entries from Jes Sorensen --- arch/i386/kernel/acpi/boot.c | 19 ++++++++++--------- arch/ia64/kernel/acpi.c | 22 +++++++--------------- arch/ia64/kernel/iosapic.c | 2 +- arch/x86_64/kernel/acpi/boot.c | 20 +++++++++++--------- drivers/acpi/numa.c | 13 ++++++++----- drivers/acpi/tables.c | 28 +++++++++++++++++----------- include/asm-ia64/iosapic.h | 3 +++ include/linux/acpi.h | 4 ++-- 8 files changed, 59 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c index 8aac4a6b9606..d14ddab92d87 100644 --- a/arch/i386/kernel/acpi/boot.c +++ b/arch/i386/kernel/acpi/boot.c @@ -43,8 +43,8 @@ int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */ int acpi_ht __initdata = 1; /* enable HT */ -int acpi_lapic = 0; -int acpi_ioapic = 0; +int acpi_lapic; +int acpi_ioapic; /* -------------------------------------------------------------------------- Boot-time Configuration @@ -456,7 +456,7 @@ acpi_boot_init (void) * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value). */ - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); return result; @@ -464,7 +464,8 @@ acpi_boot_init (void) mp_register_lapic_address(acpi_lapic_addr); - result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic, + MAX_APICS); if (!result) { printk(KERN_ERR PREFIX "No LAPIC entries present\n"); /* TBD: Cleanup to allow fallback to MPS */ @@ -476,7 +477,7 @@ acpi_boot_init (void) return result; } - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); /* TBD: Cleanup to allow fallback to MPS */ @@ -513,8 +514,8 @@ acpi_boot_init (void) return 1; } - result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic); - if (!result) { + result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic, MAX_IO_APICS); + if (!result) { printk(KERN_ERR PREFIX "No IOAPIC entries present\n"); return -ENODEV; } @@ -526,14 +527,14 @@ acpi_boot_init (void) /* Build a default routing table for legacy (ISA) interrupts. */ mp_config_acpi_legacy_irqs(); - result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr); + result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, NR_IRQ_VECTORS); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); /* TBD: Cleanup to allow fallback to MPS */ return result; } - result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); + result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, NR_IRQ_VECTORS); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); /* TBD: Cleanup to allow fallback to MPS */ diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index d757134a2175..b3ceb1a018eb 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -189,8 +189,6 @@ acpi_parse_lsapic (acpi_table_entry_header *header) if (!lsapic->flags.enabled) printk(" disabled"); - else if (available_cpus >= NR_CPUS) - printk(" ignored (increase NR_CPUS)"); else { printk(" enabled"); #ifdef CONFIG_SMP @@ -393,12 +391,6 @@ acpi_numa_memory_affinity_init (struct acpi_table_memory_affinity *ma) size = ma->length_hi; size = (size << 32) | ma->length_lo; - if (num_memblks >= NR_MEMBLKS) { - printk(KERN_ERR "Too many mem chunks in SRAT. Ignoring %ld MBytes at %lx\n", - size/(1024*1024), paddr); - return; - } - /* Ignore disabled entries */ if (!ma->flags.enabled) return; @@ -550,29 +542,29 @@ acpi_boot_init (void) /* Local APIC */ - if (acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr) < 0) + if (acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0) < 0) printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); - if (acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic) < 1) + if (acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic, NR_CPUS) < 1) printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries\n"); - if (acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi) < 0) + if (acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0) < 0) printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); /* I/O APIC */ - if (acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic) < 1) + if (acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic, NR_IOSAPICS) < 1) printk(KERN_ERR PREFIX "Error parsing MADT - no IOSAPIC entries\n"); /* System-Level Interrupt Routing */ - if (acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src) < 0) + if (acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src, ACPI_MAX_PLATFORM_INTERRUPTS) < 0) printk(KERN_ERR PREFIX "Error parsing platform interrupt source entry\n"); - if (acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr) < 0) + if (acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, 0) < 0) printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); - if (acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src) < 0) + if (acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, 0) < 0) printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); skip_madt: diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c index 9e30b08ba40e..879f566dd1b1 100644 --- a/arch/ia64/kernel/iosapic.c +++ b/arch/ia64/kernel/iosapic.c @@ -114,7 +114,7 @@ static struct iosapic { char *addr; /* base address of IOSAPIC */ unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ unsigned short num_rte; /* number of RTE in this IOSAPIC */ -} iosapic_lists[256]; +} iosapic_lists[NR_IOSAPICS]; static int num_iosapic; diff --git a/arch/x86_64/kernel/acpi/boot.c b/arch/x86_64/kernel/acpi/boot.c index 33a40edea70b..d46c2d41b759 100644 --- a/arch/x86_64/kernel/acpi/boot.c +++ b/arch/x86_64/kernel/acpi/boot.c @@ -51,8 +51,8 @@ int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */ int acpi_ht __initdata = 1; /* enable HT */ -int acpi_lapic = 0; -int acpi_ioapic = 0; +int acpi_lapic; +int acpi_ioapic; /* -------------------------------------------------------------------------- Boot-time Configuration @@ -439,7 +439,7 @@ acpi_boot_init (void) * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value). */ - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); return result; @@ -447,7 +447,8 @@ acpi_boot_init (void) mp_register_lapic_address(acpi_lapic_addr); - result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic, + MAX_APICS); if (!result) { printk(KERN_ERR PREFIX "No LAPIC entries present\n"); /* TBD: Cleanup to allow fallback to MPS */ @@ -459,7 +460,7 @@ acpi_boot_init (void) return result; } - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); /* TBD: Cleanup to allow fallback to MPS */ @@ -496,8 +497,8 @@ acpi_boot_init (void) return 1; } - result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic); - if (!result) { + result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic, MAX_IO_APICS); + if (!result) { printk(KERN_ERR PREFIX "No IOAPIC entries present\n"); return -ENODEV; } @@ -509,14 +510,15 @@ acpi_boot_init (void) /* Build a default routing table for legacy (ISA) interrupts. */ mp_config_acpi_legacy_irqs(); - result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr); + result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, NR_IRQ_VECTORS); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); /* TBD: Cleanup to allow fallback to MPS */ return result; } - result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); + result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, + NR_IRQ_VECTORS); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); /* TBD: Cleanup to allow fallback to MPS */ diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 62a484e4f6f8..ad63798aa625 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -31,7 +31,7 @@ #include #include -extern int __init acpi_table_parse_madt_family (enum acpi_table_id id, unsigned long madt_size, int entry_id, acpi_madt_entry_handler handler); +extern int __init acpi_table_parse_madt_family (enum acpi_table_id id, unsigned long madt_size, int entry_id, acpi_madt_entry_handler handler, unsigned int max_entries); void __init acpi_table_print_srat_entry ( @@ -149,10 +149,11 @@ acpi_parse_srat (unsigned long phys_addr, unsigned long size) int __init acpi_table_parse_srat ( enum acpi_srat_entry_id id, - acpi_madt_entry_handler handler) + acpi_madt_entry_handler handler, + unsigned int max_entries) { return acpi_table_parse_madt_family(ACPI_SRAT, sizeof(struct acpi_table_srat), - id, handler); + id, handler, max_entries); } @@ -166,9 +167,11 @@ acpi_numa_init() if (result > 0) { result = acpi_table_parse_srat(ACPI_SRAT_PROCESSOR_AFFINITY, - acpi_parse_processor_affinity); + acpi_parse_processor_affinity, + NR_CPUS); result = acpi_table_parse_srat(ACPI_SRAT_MEMORY_AFFINITY, - acpi_parse_memory_affinity); + acpi_parse_memory_affinity, + NR_MEMBLKS); } else { /* FIXME */ printk("Warning: acpi_table_parse(ACPI_SRAT) returned %d!\n",result); diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index e997203d754b..2583a0fcf2ce 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -302,13 +302,14 @@ acpi_table_parse_madt_family ( enum acpi_table_id id, unsigned long madt_size, int entry_id, - acpi_madt_entry_handler handler) + acpi_madt_entry_handler handler, + unsigned int max_entries) { void *madt = NULL; - acpi_table_entry_header *entry = NULL; - unsigned long count = 0; - unsigned long madt_end = 0; - unsigned int i = 0; + acpi_table_entry_header *entry; + unsigned int count = 0; + unsigned long madt_end; + unsigned int i; if (!handler) return -EINVAL; @@ -342,13 +343,18 @@ acpi_table_parse_madt_family ( ((unsigned long) madt + madt_size); while (((unsigned long) entry) < madt_end) { - if (entry->type == entry_id) { - count++; + if (entry->type == entry_id && + (!max_entries || count++ < max_entries)) handler(entry); - } + entry = (acpi_table_entry_header *) ((unsigned long) entry + entry->length); } + if (max_entries && count > max_entries) { + printk(KERN_WARNING PREFIX "[%s:0x%02x] ignored %i entries of " + "%i found\n", acpi_table_signatures[id], entry_id, + count - max_entries, count); + } return count; } @@ -357,10 +363,11 @@ acpi_table_parse_madt_family ( int __init acpi_table_parse_madt ( enum acpi_madt_entry_id id, - acpi_madt_entry_handler handler) + acpi_madt_entry_handler handler, + unsigned int max_entries) { return acpi_table_parse_madt_family(ACPI_APIC, sizeof(struct acpi_table_madt), - id, handler); + id, handler, max_entries); } @@ -585,4 +592,3 @@ acpi_table_init (void) return 0; } - diff --git a/include/asm-ia64/iosapic.h b/include/asm-ia64/iosapic.h index 68fa169e7a5b..ba8a35f418a2 100644 --- a/include/asm-ia64/iosapic.h +++ b/include/asm-ia64/iosapic.h @@ -52,6 +52,9 @@ #ifndef __ASSEMBLY__ #ifdef CONFIG_IOSAPIC + +#define NR_IOSAPICS 256 + extern void __init iosapic_system_init (int pcat_compat); extern void __init iosapic_init (unsigned long address, unsigned int gsi_base); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 8e4eba21e7ea..f851b34903e3 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -355,8 +355,8 @@ int acpi_numa_init (void); int acpi_table_init (void); int acpi_table_parse (enum acpi_table_id id, acpi_table_handler handler); int acpi_get_table_header_early (enum acpi_table_id id, struct acpi_table_header **header); -int acpi_table_parse_madt (enum acpi_madt_entry_id id, acpi_madt_entry_handler handler); -int acpi_table_parse_srat (enum acpi_srat_entry_id id, acpi_madt_entry_handler handler); +int acpi_table_parse_madt (enum acpi_madt_entry_id id, acpi_madt_entry_handler handler, unsigned int max_entries); +int acpi_table_parse_srat (enum acpi_srat_entry_id id, acpi_madt_entry_handler handler, unsigned int max_entries); void acpi_table_print (struct acpi_table_header *header, unsigned long phys_addr); void acpi_table_print_madt_entry (acpi_table_entry_header *madt); void acpi_table_print_srat_entry (acpi_table_entry_header *srat); -- cgit v1.2.3 From b65fa0c7e6e7757e48b7607eca2590480581346a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 28 Jan 2004 07:06:29 -0500 Subject: [ACPI] update _PPC handling -- from Dominik Brodowski updates the _PPC handling. It is handled as a CPUfreq policy notifier which adjusts the maximum CPU speed according to the current platform limit. --- arch/i386/kernel/cpu/cpufreq/acpi.c | 5 -- drivers/acpi/processor.c | 170 ++++++++++++++++++++++++++---------- include/acpi/processor.h | 2 - 3 files changed, 123 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/cpu/cpufreq/acpi.c b/arch/i386/kernel/cpu/cpufreq/acpi.c index 1134d2bd3f2a..e79bfd4dbf5f 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi.c @@ -591,10 +591,6 @@ acpi_processor_get_performance_info ( if (result) return_VALUE(result); - result = acpi_processor_get_platform_limit(perf->pr); - if (result) - return_VALUE(result); - return_VALUE(0); } @@ -694,7 +690,6 @@ acpi_cpufreq_init (void) int result = 0; int i = 0; struct acpi_processor *pr = NULL; - struct acpi_processor_performance *perf = NULL; ACPI_FUNCTION_TRACE("acpi_cpufreq_init"); diff --git a/drivers/acpi/processor.c b/drivers/acpi/processor.c index ad2374d33896..79bda68eb140 100644 --- a/drivers/acpi/processor.c +++ b/drivers/acpi/processor.c @@ -746,7 +746,62 @@ acpi_processor_get_power_info ( /* -------------------------------------------------------------------------- Performance Management -------------------------------------------------------------------------- */ -int +#ifdef CONFIG_CPU_FREQ + +static DECLARE_MUTEX(performance_sem); + +/* + * _PPC support is implemented as a CPUfreq policy notifier: + * This means each time a CPUfreq driver registered also with + * the ACPI core is asked to change the speed policy, the maximum + * value is adjusted so that it is within the platform limit. + * + * Also, when a new platform limit value is detected, the CPUfreq + * policy is adjusted accordingly. + */ + +static int acpi_processor_ppc_is_init = 0; + +static int acpi_processor_ppc_notifier(struct notifier_block *nb, + unsigned long event, + void *data) +{ + struct cpufreq_policy *policy = data; + struct acpi_processor *pr; + unsigned int ppc = 0; + + down(&performance_sem); + + if (event != CPUFREQ_INCOMPATIBLE) + goto out; + + pr = processors[policy->cpu]; + if (!pr || !pr->performance) + goto out; + + ppc = (unsigned int) pr->performance_platform_limit; + if (!ppc) + goto out; + + if (ppc > pr->performance->state_count) + goto out; + + cpufreq_verify_within_limits(policy, 0, + pr->performance->states[ppc].core_frequency * 1000); + + out: + up(&performance_sem); + + return 0; +} + + +static struct notifier_block acpi_ppc_notifier_block = { + .notifier_call = acpi_processor_ppc_notifier, +}; + + +static int acpi_processor_get_platform_limit ( struct acpi_processor* pr) { @@ -770,12 +825,38 @@ acpi_processor_get_platform_limit ( pr->performance_platform_limit = (int) ppc; - acpi_processor_get_limit_info(pr); - return_VALUE(0); } EXPORT_SYMBOL(acpi_processor_get_platform_limit); + +static int acpi_processor_ppc_has_changed( + struct acpi_processor *pr) +{ + int ret = acpi_processor_get_platform_limit(pr); + if (ret < 0) + return (ret); + else + return cpufreq_update_policy(pr->id); +} + + +static void acpi_processor_ppc_init(void) { + if (!cpufreq_register_notifier(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER)) + acpi_processor_ppc_is_init = 1; + else + printk(KERN_DEBUG "Warning: Processor Platform Limit not supported.\n"); +} + + +static void acpi_processor_ppc_exit(void) { + if (acpi_processor_ppc_is_init) + cpufreq_unregister_notifier(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER); + + acpi_processor_ppc_is_init = 0; +} + + int acpi_processor_register_performance ( struct acpi_processor_performance * performance, @@ -784,21 +865,49 @@ acpi_processor_register_performance ( { ACPI_FUNCTION_TRACE("acpi_processor_register_performance"); + if (!acpi_processor_ppc_is_init) + return_VALUE(-EINVAL); + + down(&performance_sem); + *pr = processors[cpu]; - if (!*pr) + if (!*pr) { + up(&performance_sem); return_VALUE(-ENODEV); + } - if ((*pr)->performance) + if ((*pr)->performance) { + up(&performance_sem); return_VALUE(-EBUSY); + } (*pr)->performance = performance; performance->pr = *pr; + + up(&performance_sem); return 0; } EXPORT_SYMBOL(acpi_processor_register_performance); -/* for the rest of it, check cpufreq/acpi.c */ +/* for the rest of it, check arch/i386/kernel/cpu/cpufreq/acpi.c */ + +#else /* !CONFIG_CPU_FREQ */ + +static void acpi_processor_ppc_init(void) { return; } +static void acpi_processor_ppc_exit(void) { return; } + +static int acpi_processor_ppc_has_changed(struct acpi_processor *pr) { + static unsigned int printout = 1; + if (printout) { + printk(KERN_WARNING "Warning: Processor Platform Limit event detected, but not handled.\n"); + printk(KERN_WARNING "Consider compiling CPUfreq support into your kernel.\n"); + printout = 0; + } + return 0; +} + +#endif /* CONFIG_CPU_FREQ */ /* -------------------------------------------------------------------------- Throttling Control @@ -1043,27 +1152,6 @@ acpi_processor_apply_limit ( if (!pr->flags.limit) return_VALUE(-ENODEV); -#ifdef CONFIG_CPU_FREQ - if (pr->flags.performance) { - px = pr->performance_platform_limit; - if (pr->limit.user.px > px) - px = pr->limit.user.px; - if (pr->limit.thermal.px > px) - px = pr->limit.thermal.px; - { - struct cpufreq_policy policy; - policy.cpu = pr->id; - cpufreq_get_policy(&policy, pr->id); - policy.max = pr->performance->states[px].core_frequency * 1000; /* racy */ - result = cpufreq_set_policy(&policy); - } - if (result) - goto end; - } else if (pr->performance_platform_limit) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Platform limit event detected. Consider using ACPI P-States CPUfreq driver\n")); - } -#endif - if (pr->flags.throttling) { if (pr->limit.user.tx > tx) tx = pr->limit.user.tx; @@ -1338,14 +1426,12 @@ static int acpi_processor_info_seq_show(struct seq_file *seq, void *offset) "bus mastering control: %s\n" "power management: %s\n" "throttling control: %s\n" - "performance management: %s\n" "limit interface: %s\n", pr->id, pr->acpi_id, pr->flags.bm_control ? "yes" : "no", pr->flags.power ? "yes" : "no", pr->flags.throttling ? "yes" : "no", - pr->flags.performance ? "yes" : "no", pr->flags.limit ? "yes" : "no"); end: @@ -1502,11 +1588,9 @@ static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) } seq_printf(seq, "active limit: P%d:T%d\n" - "platform limit: P%d:T0\n" "user limit: P%d:T%d\n" "thermal limit: P%d:T%d\n", pr->limit.state.px, pr->limit.state.tx, - pr->flags.performance?pr->performance_platform_limit:0, pr->limit.user.px, pr->limit.user.tx, pr->limit.thermal.px, pr->limit.thermal.tx); @@ -1553,15 +1637,6 @@ acpi_processor_write_limit ( return_VALUE(-EINVAL); } - if (pr->flags.performance) { - if ((px < pr->performance_platform_limit) - || (px > (pr->performance->state_count - 1))) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid px\n")); - return_VALUE(-EINVAL); - } - pr->limit.user.px = px; - } - if (pr->flags.throttling) { if ((tx < 0) || (tx > (pr->throttling.state_count - 1))) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid tx\n")); @@ -1741,9 +1816,9 @@ acpi_processor_get_info ( } acpi_processor_get_power_info(pr); - pr->flags.performance = 0; - pr->performance_platform_limit = 0; - acpi_processor_get_platform_limit(pr); +#ifdef CONFIG_CPU_FREQ + acpi_processor_ppc_has_changed(pr); +#endif acpi_processor_get_throttling_info(pr); acpi_processor_get_limit_info(pr); @@ -1757,7 +1832,6 @@ acpi_processor_notify ( u32 event, void *data) { - int result = 0; struct acpi_processor *pr = (struct acpi_processor *) data; struct acpi_device *device = NULL; @@ -1771,9 +1845,7 @@ acpi_processor_notify ( switch (event) { case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: - result = acpi_processor_get_platform_limit(pr); - if (!result) - acpi_processor_apply_limit(pr); + acpi_processor_ppc_has_changed(pr); acpi_bus_generate_event(device, event, pr->performance_platform_limit); break; @@ -1921,6 +1993,8 @@ acpi_processor_init (void) acpi_thermal_cpufreq_init(); + acpi_processor_ppc_init(); + return_VALUE(0); } @@ -1930,6 +2004,8 @@ acpi_processor_exit (void) { ACPI_FUNCTION_TRACE("acpi_processor_exit"); + acpi_processor_ppc_exit(); + acpi_thermal_cpufreq_exit(); acpi_bus_unregister_driver(&acpi_processor_driver); diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 985cc0616dd1..ade6751584d6 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -133,8 +133,6 @@ struct acpi_processor { struct acpi_processor_limit limit; }; -extern int acpi_processor_get_platform_limit ( - struct acpi_processor* pr); extern int acpi_processor_register_performance ( struct acpi_processor_performance * performance, struct acpi_processor ** pr, -- cgit v1.2.3 From cf9b129e5db461835f8b593a0c39182abba0bccc Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 28 Jan 2004 07:34:24 -0500 Subject: [ACPI] acpi-cpufreq fixups from Dominik Brodowski - remove unnecessary usage of flags.performance - remove double check of _PPC in acpi-cpufreq driver as it's handled in drivers/acpi/processor.c already - remove unneeded EXPORT_SYMBOL - allocation of memory only for probed CPUs - add unregistration function to the core - fix OOPS when PST has core_frequency values of zero - fix cpufreq_get() output - fix /proc/acpi/processor/*/performance write support [deprecated] --- arch/i386/kernel/cpu/cpufreq/acpi.c | 173 +++++++++++++++--------------------- drivers/acpi/processor.c | 50 ++++++++--- include/acpi/processor.h | 4 +- 3 files changed, 116 insertions(+), 111 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/cpu/cpufreq/acpi.c b/arch/i386/kernel/cpu/cpufreq/acpi.c index f894ef7d3c18..e38ddb406381 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi.c @@ -52,7 +52,7 @@ MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME); MODULE_LICENSE("GPL"); -static struct acpi_processor_performance *performance; +static struct acpi_processor_performance *performance[NR_CPUS]; static int @@ -188,9 +188,6 @@ acpi_processor_get_performance_states ( else perf->state_count = pss->package.count; - if (perf->state_count > 1) - perf->pr->flags.performance = 1; - for (i = 0; i < perf->state_count; i++) { struct acpi_processor_px *px = &(perf->states[i]); @@ -217,6 +214,12 @@ acpi_processor_get_performance_states ( (u32) px->bus_master_latency, (u32) px->control, (u32) px->status)); + + if (!px->core_frequency) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "core_frequency is 0\n")); + result = -EFAULT; + goto end; + } } end: @@ -279,22 +282,12 @@ acpi_processor_set_performance ( if (!perf || !perf->pr) return_VALUE(-EINVAL); - if (!perf->pr->flags.performance) - return_VALUE(-ENODEV); - if (state >= perf->state_count) { ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid target state (P%d)\n", state)); return_VALUE(-ENODEV); } - if (state < perf->pr->performance_platform_limit) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "Platform limit (P%d) overrides target state (P%d)\n", - perf->pr->performance_platform_limit, state)); - return_VALUE(-ENODEV); - } - if (state == perf->state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Already at target state (P%d)\n", state)); @@ -306,8 +299,8 @@ acpi_processor_set_performance ( /* cpufreq frequency struct */ cpufreq_freqs.cpu = perf->pr->id; - cpufreq_freqs.old = perf->states[perf->state].core_frequency; - cpufreq_freqs.new = perf->states[state].core_frequency; + cpufreq_freqs.old = perf->states[perf->state].core_frequency * 1000; + cpufreq_freqs.new = perf->states[state].core_frequency * 1000; /* notify cpufreq */ cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE); @@ -401,7 +394,7 @@ static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset) if (!pr) goto end; - if (!pr->flags.performance || !pr->performance) { + if (!pr->performance) { seq_puts(seq, "\n"); goto end; } @@ -437,14 +430,20 @@ acpi_processor_write_performance ( loff_t *data) { int result = 0; - struct acpi_processor *pr = (struct acpi_processor *) data; + struct seq_file *m = (struct seq_file *) file->private_data; + struct acpi_processor *pr = (struct acpi_processor *) m->private; + struct acpi_processor_performance *perf; char state_string[12] = {'\0'}; unsigned int new_state = 0; struct cpufreq_policy policy; ACPI_FUNCTION_TRACE("acpi_processor_write_performance"); - if (!pr || !pr->performance || (count > sizeof(state_string) - 1)) + if (!pr || (count > sizeof(state_string) - 1)) + return_VALUE(-EINVAL); + + perf = pr->performance; + if (!perf) return_VALUE(-EINVAL); if (copy_from_user(state_string, buffer, count)) @@ -453,10 +452,14 @@ acpi_processor_write_performance ( state_string[count] = '\0'; new_state = simple_strtoul(state_string, NULL, 0); + if (new_state >= perf->state_count) + return_VALUE(-EINVAL); + cpufreq_get_policy(&policy, pr->id); policy.cpu = pr->id; - policy.max = pr->performance->states[new_state].core_frequency * 1000; + policy.min = perf->states[new_state].core_frequency * 1000; + policy.max = perf->states[new_state].core_frequency * 1000; result = cpufreq_set_policy(&policy); if (result) @@ -522,14 +525,14 @@ acpi_cpufreq_target ( unsigned int target_freq, unsigned int relation) { - struct acpi_processor_performance *perf = &performance[policy->cpu]; + struct acpi_processor_performance *perf = performance[policy->cpu]; unsigned int next_state = 0; unsigned int result = 0; ACPI_FUNCTION_TRACE("acpi_cpufreq_setpolicy"); result = cpufreq_frequency_table_target(policy, - &perf->freq_table[perf->pr->limit.state.px], + perf->freq_table, target_freq, relation, &next_state); @@ -547,17 +550,17 @@ acpi_cpufreq_verify ( struct cpufreq_policy *policy) { unsigned int result = 0; - struct acpi_processor_performance *perf = &performance[policy->cpu]; + struct acpi_processor_performance *perf = performance[policy->cpu]; ACPI_FUNCTION_TRACE("acpi_cpufreq_verify"); result = cpufreq_frequency_table_verify(policy, - &perf->freq_table[perf->pr->limit.state.px]); + perf->freq_table); cpufreq_verify_within_limits( policy, perf->states[perf->state_count - 1].core_frequency * 1000, - perf->states[perf->pr->limit.state.px].core_frequency * 1000); + perf->states[0].core_frequency * 1000); return_VALUE(result); } @@ -601,35 +604,43 @@ acpi_cpufreq_cpu_init ( { unsigned int i; unsigned int cpu = policy->cpu; - struct acpi_processor *pr = NULL; - struct acpi_processor_performance *perf = &performance[policy->cpu]; - struct acpi_device *device; + struct acpi_processor_performance *perf; unsigned int result = 0; ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_init"); - acpi_processor_register_performance(perf, &pr, cpu); + perf = kmalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL); + if (!perf) + return_VALUE(-ENOMEM); + memset(perf, 0, sizeof(struct acpi_processor_performance)); + + performance[cpu] = perf; - pr = performance[cpu].pr; - if (!pr) - return_VALUE(-ENODEV); + result = acpi_processor_register_performance(perf, cpu); + if (result) + goto err_free; result = acpi_processor_get_performance_info(perf); if (result) - return_VALUE(-ENODEV); + goto err_unreg; /* capability check */ - if (!pr->flags.performance) - return_VALUE(-ENODEV); + if (perf->state_count <= 1) + goto err_unreg; /* detect transition latency */ policy->cpuinfo.transition_latency = 0; - for (i=0;istate_count;i++) { + for (i=0; istate_count; i++) { if ((perf->states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency) policy->cpuinfo.transition_latency = perf->states[i].transition_latency * 1000; } policy->governor = CPUFREQ_DEFAULT_GOVERNOR; - policy->cur = perf->states[pr->limit.state.px].core_frequency * 1000; + + /* + * The current speed is unknown and not detectable by ACPI... argh! Assume + * it's P0, it will be set to this value later during initialization. + */ + policy->cur = perf->states[0].core_frequency * 1000; /* table init */ for (i=0; i<=perf->state_count; i++) @@ -643,19 +654,25 @@ acpi_cpufreq_cpu_init ( result = cpufreq_frequency_table_cpuinfo(policy, &perf->freq_table[0]); - acpi_cpufreq_add_file(pr); + acpi_cpufreq_add_file(perf->pr); - if (acpi_bus_get_device(pr->handle, &device)) - device = NULL; - - printk(KERN_INFO "cpufreq: %s - ACPI performance management activated.\n", - device ? acpi_device_bid(device) : "CPU??"); - for (i = 0; i < pr->performance->state_count; i++) + printk(KERN_INFO "cpufreq: CPU%u - ACPI performance management activated.\n", + cpu); + for (i = 0; i < perf->state_count; i++) printk(KERN_INFO "cpufreq: %cP%d: %d MHz, %d mW, %d uS\n", - (i == pr->performance->state?'*':' '), i, - (u32) pr->performance->states[i].core_frequency, - (u32) pr->performance->states[i].power, - (u32) pr->performance->states[i].transition_latency); + (i == perf->state?'*':' '), i, + (u32) perf->states[i].core_frequency, + (u32) perf->states[i].power, + (u32) perf->states[i].transition_latency); + + return_VALUE(result); + + err_unreg: + acpi_processor_unregister_performance(perf, cpu); + err_free: + kfree(perf); + performance[cpu] = NULL; + return_VALUE(result); } @@ -664,11 +681,16 @@ static int acpi_cpufreq_cpu_exit ( struct cpufreq_policy *policy) { - struct acpi_processor *pr = performance[policy->cpu].pr; + struct acpi_processor_performance *perf = performance[policy->cpu]; ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_exit"); - acpi_cpufreq_remove_file(pr); + if (perf) { + acpi_cpufreq_remove_file(perf->pr); + performance[policy->cpu] = NULL; + acpi_processor_unregister_performance(perf, policy->cpu); + kfree(perf); + } return_VALUE(0); } @@ -688,41 +710,10 @@ static int __init acpi_cpufreq_init (void) { int result = 0; - int i = 0; - struct acpi_processor *pr = NULL; ACPI_FUNCTION_TRACE("acpi_cpufreq_init"); - /* alloc memory */ - performance = kmalloc(NR_CPUS * sizeof(struct acpi_processor_performance), GFP_KERNEL); - if (!performance) - return_VALUE(-ENOMEM); - memset(performance, 0, NR_CPUS * sizeof(struct acpi_processor_performance)); - - /* register struct acpi_processor_performance performance */ - for (i=0; iflags.performance = 0; - performance[i].pr->performance = NULL; - performance[i].pr = NULL; - } - } - kfree(performance); - } return_VALUE(result); } @@ -731,27 +722,9 @@ acpi_cpufreq_init (void) static void __exit acpi_cpufreq_exit (void) { - int i = 0; - ACPI_FUNCTION_TRACE("acpi_cpufreq_exit"); - for (i=0; iflags.performance = 0; - } - - cpufreq_unregister_driver(&acpi_cpufreq_driver); - - /* unregister struct acpi_processor_performance performance */ - for (i=0; iflags.performance = 0; - performance[i].pr->performance = NULL; - performance[i].pr = NULL; - } - } - - kfree(performance); + cpufreq_unregister_driver(&acpi_cpufreq_driver); return_VOID; } diff --git a/drivers/acpi/processor.c b/drivers/acpi/processor.c index 79bda68eb140..5e2d29669b87 100644 --- a/drivers/acpi/processor.c +++ b/drivers/acpi/processor.c @@ -3,6 +3,7 @@ * * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004 Dominik Brodowski * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -55,7 +56,6 @@ #define ACPI_PROCESSOR_DEVICE_NAME "Processor" #define ACPI_PROCESSOR_FILE_INFO "info" #define ACPI_PROCESSOR_FILE_POWER "power" -#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" #define ACPI_PROCESSOR_FILE_THROTTLING "throttling" #define ACPI_PROCESSOR_FILE_LIMIT "limit" #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 @@ -827,7 +827,6 @@ acpi_processor_get_platform_limit ( return_VALUE(0); } -EXPORT_SYMBOL(acpi_processor_get_platform_limit); static int acpi_processor_ppc_has_changed( @@ -860,9 +859,10 @@ static void acpi_processor_ppc_exit(void) { int acpi_processor_register_performance ( struct acpi_processor_performance * performance, - struct acpi_processor ** pr, unsigned int cpu) { + struct acpi_processor *pr; + ACPI_FUNCTION_TRACE("acpi_processor_register_performance"); if (!acpi_processor_ppc_is_init) @@ -870,26 +870,56 @@ acpi_processor_register_performance ( down(&performance_sem); - *pr = processors[cpu]; - if (!*pr) { + pr = processors[cpu]; + if (!pr) { up(&performance_sem); return_VALUE(-ENODEV); } - if ((*pr)->performance) { + if (pr->performance) { up(&performance_sem); return_VALUE(-EBUSY); } - (*pr)->performance = performance; - performance->pr = *pr; + pr->performance = performance; + performance->pr = pr; up(&performance_sem); - return 0; + return_VALUE(0); } EXPORT_SYMBOL(acpi_processor_register_performance); +void +acpi_processor_unregister_performance ( + struct acpi_processor_performance * performance, + unsigned int cpu) +{ + struct acpi_processor *pr; + + ACPI_FUNCTION_TRACE("acpi_processor_unregister_performance"); + + if (!acpi_processor_ppc_is_init) + return_VOID; + + down(&performance_sem); + + pr = processors[cpu]; + if (!pr) { + up(&performance_sem); + return_VOID; + } + + pr->performance = NULL; + performance->pr = NULL; + + up(&performance_sem); + + return_VOID; +} +EXPORT_SYMBOL(acpi_processor_unregister_performance); + + /* for the rest of it, check arch/i386/kernel/cpu/cpufreq/acpi.c */ #else /* !CONFIG_CPU_FREQ */ @@ -1399,7 +1429,7 @@ acpi_processor_get_limit_info ( if (!pr) return_VALUE(-EINVAL); - if (pr->flags.performance || pr->flags.throttling) + if (pr->flags.throttling) pr->flags.limit = 1; return_VALUE(0); diff --git a/include/acpi/processor.h b/include/acpi/processor.h index ade6751584d6..f693a665b00a 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -135,7 +135,9 @@ struct acpi_processor { extern int acpi_processor_register_performance ( struct acpi_processor_performance * performance, - struct acpi_processor ** pr, + unsigned int cpu); +extern void acpi_processor_unregister_performance ( + struct acpi_processor_performance * performance, unsigned int cpu); #endif -- cgit v1.2.3 From 93976ea58265ef4d5253e5484817db8d497e8876 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 29 Jan 2004 00:09:20 -0800 Subject: [PATCH] USB: remove unused usb-debug.c file (moving the one used function into the usb.c file.) --- drivers/usb/core/Makefile | 2 +- drivers/usb/core/usb-debug.c | 201 ----------------------------------------- drivers/usb/core/usb.c | 13 +++ drivers/usb/serial/kobil_sct.c | 4 - include/linux/usb.h | 10 -- 5 files changed, 14 insertions(+), 216 deletions(-) delete mode 100644 drivers/usb/core/usb-debug.c (limited to 'include') diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index cbbf426352bd..e3907e097fd1 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -2,7 +2,7 @@ # Makefile for USB Core files and filesystem # -usbcore-objs := usb.o usb-debug.o hub.o hcd.o urb.o message.o \ +usbcore-objs := usb.o hub.o hcd.o urb.o message.o \ config.o file.o buffer.o driverfs.o ifeq ($(CONFIG_PCI),y) diff --git a/drivers/usb/core/usb-debug.c b/drivers/usb/core/usb-debug.c deleted file mode 100644 index b7683f917fa0..000000000000 --- a/drivers/usb/core/usb-debug.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * debug.c - USB debug helper routines. - * - * I just want these out of the way where they aren't in your - * face, but so that you can still use them.. - */ -#include -#include -#include -#include -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif -#include - -static void usb_show_endpoint(struct usb_host_endpoint *endpoint) -{ - usb_show_endpoint_descriptor(&endpoint->desc); -} - -static void usb_show_interface(struct usb_host_interface *altsetting) -{ - int i; - - usb_show_interface_descriptor(&altsetting->desc); - - for (i = 0; i < altsetting->desc.bNumEndpoints; i++) - usb_show_endpoint(altsetting->endpoint + i); -} - -static void usb_show_config(struct usb_host_config *config) -{ - int i, j; - struct usb_interface *ifp; - - usb_show_config_descriptor(&config->desc); - for (i = 0; i < config->desc.bNumInterfaces; i++) { - ifp = config->interface[i]; - - if (!ifp) - break; - - printk("\n Interface: %d\n", i); - for (j = 0; j < ifp->num_altsetting; j++) - usb_show_interface(ifp->altsetting + j); - } -} - -void usb_show_device(struct usb_device *dev) -{ - int i; - - usb_show_device_descriptor(&dev->descriptor); - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) - usb_show_config(dev->config + i); -} - -/* - * Parse and show the different USB descriptors. - */ -void usb_show_device_descriptor(struct usb_device_descriptor *desc) -{ - if (!desc) - { - printk("Invalid USB device descriptor (NULL POINTER)\n"); - return; - } - printk(" Length = %2d%s\n", desc->bLength, - desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)"); - printk(" DescriptorType = %02x\n", desc->bDescriptorType); - - printk(" USB version = %x.%02x\n", - desc->bcdUSB >> 8, desc->bcdUSB & 0xff); - printk(" Vendor:Product = %04x:%04x\n", - desc->idVendor, desc->idProduct); - printk(" MaxPacketSize0 = %d\n", desc->bMaxPacketSize0); - printk(" NumConfigurations = %d\n", desc->bNumConfigurations); - printk(" Device version = %x.%02x\n", - desc->bcdDevice >> 8, desc->bcdDevice & 0xff); - - printk(" Device Class:SubClass:Protocol = %02x:%02x:%02x\n", - desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol); - switch (desc->bDeviceClass) { - case 0: - printk(" Per-interface classes\n"); - break; - case USB_CLASS_AUDIO: - printk(" Audio device class\n"); - break; - case USB_CLASS_COMM: - printk(" Communications class\n"); - break; - case USB_CLASS_HID: - printk(" Human Interface Devices class\n"); - break; - case USB_CLASS_PRINTER: - printk(" Printer device class\n"); - break; - case USB_CLASS_MASS_STORAGE: - printk(" Mass Storage device class\n"); - break; - case USB_CLASS_HUB: - printk(" Hub device class\n"); - break; - case USB_CLASS_VENDOR_SPEC: - printk(" Vendor class\n"); - break; - default: - printk(" Unknown class\n"); - } -} - -void usb_show_config_descriptor(struct usb_config_descriptor *desc) -{ - printk("Configuration:\n"); - printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == USB_DT_CONFIG_SIZE ? "" : " (!!!)"); - printk(" bDescriptorType = %02x\n", desc->bDescriptorType); - printk(" wTotalLength = %04x\n", desc->wTotalLength); - printk(" bNumInterfaces = %02x\n", desc->bNumInterfaces); - printk(" bConfigurationValue = %02x\n", desc->bConfigurationValue); - printk(" iConfiguration = %02x\n", desc->iConfiguration); - printk(" bmAttributes = %02x\n", desc->bmAttributes); - printk(" bMaxPower = %4dmA\n", desc->bMaxPower * 2); -} - -void usb_show_interface_descriptor(struct usb_interface_descriptor *desc) -{ - printk(" Alternate Setting: %2d\n", desc->bAlternateSetting); - printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)"); - printk(" bDescriptorType = %02x\n", desc->bDescriptorType); - printk(" bInterfaceNumber = %02x\n", desc->bInterfaceNumber); - printk(" bAlternateSetting = %02x\n", desc->bAlternateSetting); - printk(" bNumEndpoints = %02x\n", desc->bNumEndpoints); - printk(" bInterface Class:SubClass:Protocol = %02x:%02x:%02x\n", - desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol); - printk(" iInterface = %02x\n", desc->iInterface); -} - -void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *desc) -{ - char *LengthCommentString = (desc->bLength == - USB_DT_ENDPOINT_AUDIO_SIZE) ? " (Audio)" : (desc->bLength == - USB_DT_ENDPOINT_SIZE) ? "" : " (!!!)"; - char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" }; - - printk(" Endpoint:\n"); - printk(" bLength = %4d%s\n", - desc->bLength, LengthCommentString); - printk(" bDescriptorType = %02x\n", desc->bDescriptorType); - printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress, - (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == - USB_ENDPOINT_XFER_CONTROL ? "i/o" : - (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? "in" : "out"); - printk(" bmAttributes = %02x (%s)\n", desc->bmAttributes, - EndpointType[USB_ENDPOINT_XFERTYPE_MASK & desc->bmAttributes]); - printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize); - printk(" bInterval = %02x\n", desc->bInterval); - - /* Audio extensions to the endpoint descriptor */ - if (desc->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) { - printk(" bRefresh = %02x\n", desc->bRefresh); - printk(" bSynchAddress = %02x\n", desc->bSynchAddress); - } -} - -void usb_show_string(struct usb_device *dev, char *id, int index) -{ - char *buf; - - if (!index) - return; - if (!(buf = kmalloc(256, GFP_KERNEL))) - return; - if (usb_string(dev, index, buf, 256) > 0) - dev_printk(KERN_INFO, &dev->dev, "%s: %s\n", id, buf); - kfree(buf); -} - -void usb_dump_urb (struct urb *urb) -{ - printk ("urb :%p\n", urb); - printk ("dev :%p\n", urb->dev); - printk ("pipe :%08X\n", urb->pipe); - printk ("status :%d\n", urb->status); - printk ("transfer_flags :%08X\n", urb->transfer_flags); - printk ("transfer_buffer :%p\n", urb->transfer_buffer); - printk ("transfer_buffer_length:%d\n", urb->transfer_buffer_length); - printk ("actual_length :%d\n", urb->actual_length); - printk ("setup_packet :%p\n", urb->setup_packet); - printk ("start_frame :%d\n", urb->start_frame); - printk ("number_of_packets :%d\n", urb->number_of_packets); - printk ("interval :%d\n", urb->interval); - printk ("error_count :%d\n", urb->error_count); - printk ("context :%p\n", urb->context); - printk ("complete :%p\n", urb->complete); -} - diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index e3b10d895a51..378d51ce2374 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -984,6 +984,19 @@ int usb_set_address(struct usb_device *dev) return retval; } +static inline void usb_show_string(struct usb_device *dev, char *id, int index) +{ + char *buf; + + if (!index) + return; + if (!(buf = kmalloc(256, GFP_KERNEL))) + return; + if (usb_string(dev, index, buf, 256) > 0) + dev_printk(KERN_INFO, &dev->dev, "%s: %s\n", id, buf); + kfree(buf); +} + /* * By the time we get here, we chose a new device address * and is in the default state. We need to identify the thing and diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 2bf6eb109524..1ed1d7aa64a7 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -409,8 +409,6 @@ static void kobil_read_int_callback( struct urb *purb, struct pt_regs *regs) // someone sets the dev to 0 if the close method has been called port->interrupt_in_urb->dev = port->serial->dev; - // usb_dump_urb(port->interrupt_in_urb); - result = usb_submit_urb( port->interrupt_in_urb, GFP_ATOMIC ); dbg("%s - port %d Send read URB returns: %i", __FUNCTION__, port->number, result); } @@ -496,8 +494,6 @@ static int kobil_write (struct usb_serial_port *port, int from_user, port->interrupt_in_urb->dev = port->serial->dev; // start reading - //usb_dump_urb(port->interrupt_in_urb); - result = usb_submit_urb( port->interrupt_in_urb, GFP_ATOMIC ); dbg("%s - port %d Send read URB returns: %i", __FUNCTION__, port->number, result); } diff --git a/include/linux/usb.h b/include/linux/usb.h index c405f81be765..c16b0125baa0 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1016,16 +1016,6 @@ static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int en /* -------------------------------------------------------------------------- */ -/* - * Debugging and troubleshooting/diagnostic helpers. - */ -void usb_show_device_descriptor(struct usb_device_descriptor *); -void usb_show_config_descriptor(struct usb_config_descriptor *); -void usb_show_interface_descriptor(struct usb_interface_descriptor *); -void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *); -void usb_show_device(struct usb_device *); -void usb_show_string(struct usb_device *dev, char *id, int index); - #ifdef DEBUG #define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , __FILE__ , ## arg) #else -- cgit v1.2.3 From 2451c4f6cbf081a98ab5068cf42fa0559f3fff5b Mon Sep 17 00:00:00 2001 From: Adam Belay Date: Fri, 30 Jan 2004 00:34:48 -0800 Subject: [PATCH] PCI: Remove uneeded resource structures from pci_dev The following patch remove irq_resource and dma_resource from pci_dev. It appears that the serial pci driver depends on irq_resource, however, it may be broken portions of an old quirk. I attempted to maintain the existing behavior while removing irq_resource. I changed FL_IRQRESOURCE to FL_NOIRQ. Russell, could you provide any comments? irq_resource and dma_resource are most likely remnants from when pci_dev was shared with pnp. --- drivers/serial/8250_pci.c | 27 ++++++--------------------- include/linux/pci.h | 2 -- 2 files changed, 6 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index bb13d16c8a2f..16937439bd29 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -43,20 +43,12 @@ #define FL_BASE4 0x0004 #define FL_GET_BASE(x) (x & FL_BASE_MASK) -#define FL_IRQ_MASK (0x0007 << 4) -#define FL_IRQBASE0 (0x0000 << 4) -#define FL_IRQBASE1 (0x0001 << 4) -#define FL_IRQBASE2 (0x0002 << 4) -#define FL_IRQBASE3 (0x0003 << 4) -#define FL_IRQBASE4 (0x0004 << 4) -#define FL_GET_IRQBASE(x) ((x & FL_IRQ_MASK) >> 4) - /* Use successive BARs (PCI base address registers), else use offset into some specified BAR */ #define FL_BASE_BARS 0x0008 -/* Use the irq resource table instead of dev->irq */ -#define FL_IRQRESOURCE 0x0080 +/* do not assign an irq */ +#define FL_NOIRQ 0x0080 /* Use the Base address register size to cap number of ports */ #define FL_REGION_SZ_CAP 0x0100 @@ -850,17 +842,10 @@ static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) static _INLINE_ int get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) { - int base_idx; - - if ((board->flags & FL_IRQRESOURCE) == 0) - return dev->irq; - - base_idx = FL_GET_IRQBASE(board->flags); - - if (base_idx > DEVICE_COUNT_IRQ) + if (board->flags & FL_NOIRQ) return 0; - - return dev->irq_resource[base_idx].start; + else + return dev->irq; } /* @@ -1314,7 +1299,7 @@ static struct pci_board pci_boards[] __devinitdata = { .first_offset = 0x10000, }, [pbn_sgi_ioc3] = { - .flags = FL_BASE0|FL_IRQRESOURCE, + .flags = FL_BASE0|FL_NOIRQ, .num_ports = 1, .base_baud = 458333, .uart_offset = 8, diff --git a/include/linux/pci.h b/include/linux/pci.h index bc51ca9edef1..612d14e90587 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -416,8 +416,6 @@ struct pci_dev { */ unsigned int irq; struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ - struct resource dma_resource[DEVICE_COUNT_DMA]; - struct resource irq_resource[DEVICE_COUNT_IRQ]; char * slot_name; /* pointer to dev.bus_id */ -- cgit v1.2.3 From 174b4ac0e57188b6737480ab2dca63b17682440a Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 30 Jan 2004 11:55:29 -0500 Subject: [ACPI] Move the /proc/acpi/processor/./performance output to drivers/acpi/processor.c -- it's the same for all lowlevel drivers. By doing so, the lowlevel drivers no longer need to have access to struct acpi_processor. from Dominik Brodowski --- arch/i386/kernel/cpu/cpufreq/acpi.c | 157 +----------------------------------- drivers/acpi/processor.c | 153 ++++++++++++++++++++++++++++++++++- include/acpi/processor.h | 1 - 3 files changed, 155 insertions(+), 156 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/cpu/cpufreq/acpi.c b/arch/i386/kernel/cpu/cpufreq/acpi.c index 91392692de44..7f49ac92d1cc 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi.c @@ -42,7 +42,6 @@ #define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor P-States Driver" #define ACPI_PROCESSOR_DEVICE_NAME "Processor" -#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME ("acpi_processor_perf") @@ -95,6 +94,7 @@ acpi_processor_read_port( static int acpi_processor_set_performance ( struct acpi_processor_performance *perf, + unsigned int cpu, int state) { u16 port = 0; @@ -106,7 +106,7 @@ acpi_processor_set_performance ( ACPI_FUNCTION_TRACE("acpi_processor_set_performance"); - if (!perf || !perf->pr) + if (!perf) return_VALUE(-EINVAL); if (state >= perf->state_count) { @@ -125,7 +125,7 @@ acpi_processor_set_performance ( perf->state, state)); /* cpufreq frequency struct */ - cpufreq_freqs.cpu = perf->pr->id; + cpufreq_freqs.cpu = cpu; cpufreq_freqs.old = perf->states[perf->state].core_frequency * 1000; cpufreq_freqs.new = perf->states[state].core_frequency * 1000; @@ -200,152 +200,6 @@ acpi_processor_set_performance ( } -#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF -/* /proc/acpi/processor/../performance interface (DEPRECATED) */ - -static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file); -static struct file_operations acpi_processor_perf_fops = { - .open = acpi_processor_perf_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_processor *pr = (struct acpi_processor *)seq->private; - int i = 0; - - ACPI_FUNCTION_TRACE("acpi_processor_perf_seq_show"); - - if (!pr) - goto end; - - if (!pr->performance) { - seq_puts(seq, "\n"); - goto end; - } - - seq_printf(seq, "state count: %d\n" - "active state: P%d\n", - pr->performance->state_count, - pr->performance->state); - - seq_puts(seq, "states:\n"); - for (i = 0; i < pr->performance->state_count; i++) - seq_printf(seq, " %cP%d: %d MHz, %d mW, %d uS\n", - (i == pr->performance->state?'*':' '), i, - (u32) pr->performance->states[i].core_frequency, - (u32) pr->performance->states[i].power, - (u32) pr->performance->states[i].transition_latency); - -end: - return 0; -} - -static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_processor_perf_seq_show, - PDE(inode)->data); -} - -static int -acpi_processor_write_performance ( - struct file *file, - const char __user *buffer, - size_t count, - loff_t *data) -{ - int result = 0; - struct seq_file *m = (struct seq_file *) file->private_data; - struct acpi_processor *pr = (struct acpi_processor *) m->private; - struct acpi_processor_performance *perf; - char state_string[12] = {'\0'}; - unsigned int new_state = 0; - struct cpufreq_policy policy; - - ACPI_FUNCTION_TRACE("acpi_processor_write_performance"); - - if (!pr || (count > sizeof(state_string) - 1)) - return_VALUE(-EINVAL); - - perf = pr->performance; - if (!perf) - return_VALUE(-EINVAL); - - if (copy_from_user(state_string, buffer, count)) - return_VALUE(-EFAULT); - - state_string[count] = '\0'; - new_state = simple_strtoul(state_string, NULL, 0); - - if (new_state >= perf->state_count) - return_VALUE(-EINVAL); - - cpufreq_get_policy(&policy, pr->id); - - policy.cpu = pr->id; - policy.min = perf->states[new_state].core_frequency * 1000; - policy.max = perf->states[new_state].core_frequency * 1000; - - result = cpufreq_set_policy(&policy); - if (result) - return_VALUE(result); - - return_VALUE(count); -} - -static void -acpi_cpufreq_add_file ( - struct acpi_processor *pr) -{ - struct proc_dir_entry *entry = NULL; - struct acpi_device *device = NULL; - - ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile"); - - if (acpi_bus_get_device(pr->handle, &device)) - return_VOID; - - /* add file 'performance' [R/W] */ - entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE, - S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); - if (!entry) - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Unable to create '%s' fs entry\n", - ACPI_PROCESSOR_FILE_PERFORMANCE)); - else { - entry->proc_fops = &acpi_processor_perf_fops; - entry->proc_fops->write = acpi_processor_write_performance; - entry->data = acpi_driver_data(device); - } - return_VOID; -} - -static void -acpi_cpufreq_remove_file ( - struct acpi_processor *pr) -{ - struct acpi_device *device = NULL; - - ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile"); - - if (acpi_bus_get_device(pr->handle, &device)) - return_VOID; - - /* remove file 'performance' */ - remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE, - acpi_device_dir(device)); - - return_VOID; -} - -#else -static void acpi_cpufreq_add_file (struct acpi_processor *pr) { return; } -static void acpi_cpufreq_remove_file (struct acpi_processor *pr) { return; } -#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */ - - static int acpi_cpufreq_target ( struct cpufreq_policy *policy, @@ -366,7 +220,7 @@ acpi_cpufreq_target ( if (result) return_VALUE(result); - result = acpi_processor_set_performance (perf, next_state); + result = acpi_processor_set_performance (perf, policy->cpu, next_state); return_VALUE(result); } @@ -445,8 +299,6 @@ acpi_cpufreq_cpu_init ( result = cpufreq_frequency_table_cpuinfo(policy, &perf->freq_table[0]); - acpi_cpufreq_add_file(perf->pr); - printk(KERN_INFO "cpufreq: CPU%u - ACPI performance management activated.\n", cpu); for (i = 0; i < perf->state_count; i++) @@ -477,7 +329,6 @@ acpi_cpufreq_cpu_exit ( ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_exit"); if (perf) { - acpi_cpufreq_remove_file(perf->pr); performance[policy->cpu] = NULL; acpi_processor_unregister_performance(perf, policy->cpu); kfree(perf); diff --git a/drivers/acpi/processor.c b/drivers/acpi/processor.c index 71e0a6606b58..33800a6efb64 100644 --- a/drivers/acpi/processor.c +++ b/drivers/acpi/processor.c @@ -58,6 +58,7 @@ #define ACPI_PROCESSOR_FILE_POWER "power" #define ACPI_PROCESSOR_FILE_THROTTLING "throttling" #define ACPI_PROCESSOR_FILE_LIMIT "limit" +#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 @@ -1067,6 +1068,152 @@ acpi_processor_get_performance_info ( } +#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF +/* /proc/acpi/processor/../performance interface (DEPRECATED) */ + +static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_processor_perf_fops = { + .open = acpi_processor_perf_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_processor *pr = (struct acpi_processor *)seq->private; + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_perf_seq_show"); + + if (!pr) + goto end; + + if (!pr->performance) { + seq_puts(seq, "\n"); + goto end; + } + + seq_printf(seq, "state count: %d\n" + "active state: P%d\n", + pr->performance->state_count, + pr->performance->state); + + seq_puts(seq, "states:\n"); + for (i = 0; i < pr->performance->state_count; i++) + seq_printf(seq, " %cP%d: %d MHz, %d mW, %d uS\n", + (i == pr->performance->state?'*':' '), i, + (u32) pr->performance->states[i].core_frequency, + (u32) pr->performance->states[i].power, + (u32) pr->performance->states[i].transition_latency); + +end: + return 0; +} + +static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_processor_perf_seq_show, + PDE(inode)->data); +} + +static int +acpi_processor_write_performance ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *data) +{ + int result = 0; + struct seq_file *m = (struct seq_file *) file->private_data; + struct acpi_processor *pr = (struct acpi_processor *) m->private; + struct acpi_processor_performance *perf; + char state_string[12] = {'\0'}; + unsigned int new_state = 0; + struct cpufreq_policy policy; + + ACPI_FUNCTION_TRACE("acpi_processor_write_performance"); + + if (!pr || (count > sizeof(state_string) - 1)) + return_VALUE(-EINVAL); + + perf = pr->performance; + if (!perf) + return_VALUE(-EINVAL); + + if (copy_from_user(state_string, buffer, count)) + return_VALUE(-EFAULT); + + state_string[count] = '\0'; + new_state = simple_strtoul(state_string, NULL, 0); + + if (new_state >= perf->state_count) + return_VALUE(-EINVAL); + + cpufreq_get_policy(&policy, pr->id); + + policy.cpu = pr->id; + policy.min = perf->states[new_state].core_frequency * 1000; + policy.max = perf->states[new_state].core_frequency * 1000; + + result = cpufreq_set_policy(&policy); + if (result) + return_VALUE(result); + + return_VALUE(count); +} + +static void +acpi_cpufreq_add_file ( + struct acpi_processor *pr) +{ + struct proc_dir_entry *entry = NULL; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile"); + + if (acpi_bus_get_device(pr->handle, &device)) + return_VOID; + + /* add file 'performance' [R/W] */ + entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_PROCESSOR_FILE_PERFORMANCE)); + else { + entry->proc_fops = &acpi_processor_perf_fops; + entry->proc_fops->write = acpi_processor_write_performance; + entry->data = acpi_driver_data(device); + } + return_VOID; +} + +static void +acpi_cpufreq_remove_file ( + struct acpi_processor *pr) +{ + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile"); + + if (acpi_bus_get_device(pr->handle, &device)) + return_VOID; + + /* remove file 'performance' */ + remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE, + acpi_device_dir(device)); + + return_VOID; +} + +#else +static void acpi_cpufreq_add_file (struct acpi_processor *pr) { return; } +static void acpi_cpufreq_remove_file (struct acpi_processor *pr) { return; } +#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */ + + int acpi_processor_register_performance ( struct acpi_processor_performance * performance, @@ -1093,7 +1240,6 @@ acpi_processor_register_performance ( } pr->performance = performance; - performance->pr = pr; if (acpi_processor_get_performance_info(pr)) { pr->performance = NULL; @@ -1101,6 +1247,8 @@ acpi_processor_register_performance ( return_VALUE(-EIO); } + acpi_cpufreq_add_file(pr); + up(&performance_sem); return_VALUE(0); } @@ -1128,7 +1276,8 @@ acpi_processor_unregister_performance ( } pr->performance = NULL; - performance->pr = NULL; + + acpi_cpufreq_remove_file(pr); up(&performance_sem); diff --git a/include/acpi/processor.h b/include/acpi/processor.h index f693a665b00a..325bcd7713fe 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -77,7 +77,6 @@ struct acpi_processor_performance { int state_count; struct acpi_processor_px states[ACPI_PROCESSOR_MAX_PERFORMANCE]; struct cpufreq_frequency_table freq_table[ACPI_PROCESSOR_MAX_PERFORMANCE]; - struct acpi_processor *pr; }; -- cgit v1.2.3 From 2f041d0bfecf03dd7e798b84c5502b238ddc42fc Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 30 Jan 2004 11:55:49 -0500 Subject: [ACPI] Abstract the registration method between low-level drivers and the ACPI Processor P-States driver. from Dominik Brodowski --- arch/i386/kernel/cpu/cpufreq/acpi.c | 131 ++++++++++++++++++------------------ drivers/acpi/processor.c | 31 +-------- include/acpi/processor.h | 13 ++-- 3 files changed, 73 insertions(+), 102 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/cpu/cpufreq/acpi.c b/arch/i386/kernel/cpu/cpufreq/acpi.c index 7f49ac92d1cc..37b095ff6c9d 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi.c @@ -51,7 +51,12 @@ MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME); MODULE_LICENSE("GPL"); -static struct acpi_processor_performance *performance[NR_CPUS]; +struct cpufreq_acpi_io { + struct acpi_processor_performance acpi_data; + struct cpufreq_frequency_table freq_table[ACPI_PROCESSOR_MAX_PERFORMANCE]; +}; + +static struct cpufreq_acpi_io *acpi_io_data[NR_CPUS]; static int @@ -93,7 +98,7 @@ acpi_processor_read_port( static int acpi_processor_set_performance ( - struct acpi_processor_performance *perf, + struct cpufreq_acpi_io *data, unsigned int cpu, int state) { @@ -106,28 +111,19 @@ acpi_processor_set_performance ( ACPI_FUNCTION_TRACE("acpi_processor_set_performance"); - if (!perf) - return_VALUE(-EINVAL); - - if (state >= perf->state_count) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "Invalid target state (P%d)\n", state)); - return_VALUE(-ENODEV); - } - - if (state == perf->state) { + if (state == data->acpi_data.state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Already at target state (P%d)\n", state)); return_VALUE(0); } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Transitioning from P%d to P%d\n", - perf->state, state)); + data->acpi_data.state, state)); /* cpufreq frequency struct */ cpufreq_freqs.cpu = cpu; - cpufreq_freqs.old = perf->states[perf->state].core_frequency * 1000; - cpufreq_freqs.new = perf->states[state].core_frequency * 1000; + cpufreq_freqs.old = data->freq_table[data->acpi_data.state].frequency; + cpufreq_freqs.new = data->freq_table[state].frequency; /* notify cpufreq */ cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE); @@ -137,9 +133,9 @@ acpi_processor_set_performance ( * control_register. */ - port = perf->control_register; - bit_width = perf->control_register_bit_width; - value = (u32) perf->states[state].control; + port = data->acpi_data.control_register.address; + bit_width = data->acpi_data.control_register.bit_width; + value = (u32) data->acpi_data.states[state].control; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Writing 0x%08x to port 0x%04x\n", value, port)); @@ -158,12 +154,12 @@ acpi_processor_set_performance ( * giving up. */ - port = perf->status_register; - bit_width = perf->status_register_bit_width; + port = data->acpi_data.status_register.address; + bit_width = data->acpi_data.status_register.bit_width; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Looking for 0x%08x from port 0x%04x\n", - (u32) perf->states[state].status, port)); + (u32) data->acpi_data.states[state].status, port)); for (i=0; i<100; i++) { ret = acpi_processor_read_port(port, bit_width, &value); @@ -172,7 +168,7 @@ acpi_processor_set_performance ( "Invalid port width 0x%04x\n", bit_width)); return_VALUE(ret); } - if (value == (u32) perf->states[state].status) + if (value == (u32) data->acpi_data.states[state].status) break; udelay(10); } @@ -180,7 +176,7 @@ acpi_processor_set_performance ( /* notify cpufreq */ cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE); - if (value != (u32) perf->states[state].status) { + if (value != (u32) data->acpi_data.states[state].status) { unsigned int tmp = cpufreq_freqs.new; cpufreq_freqs.new = cpufreq_freqs.old; cpufreq_freqs.old = tmp; @@ -194,7 +190,7 @@ acpi_processor_set_performance ( "Transition successful after %d microseconds\n", i * 10)); - perf->state = state; + data->acpi_data.state = state; return_VALUE(0); } @@ -206,21 +202,21 @@ acpi_cpufreq_target ( unsigned int target_freq, unsigned int relation) { - struct acpi_processor_performance *perf = performance[policy->cpu]; + struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; unsigned int next_state = 0; unsigned int result = 0; ACPI_FUNCTION_TRACE("acpi_cpufreq_setpolicy"); - result = cpufreq_frequency_table_target(policy, - perf->freq_table, + result = cpufreq_frequency_table_target(policy, + data->freq_table, target_freq, relation, &next_state); if (result) return_VALUE(result); - result = acpi_processor_set_performance (perf, policy->cpu, next_state); + result = acpi_processor_set_performance (data, policy->cpu, next_state); return_VALUE(result); } @@ -231,17 +227,12 @@ acpi_cpufreq_verify ( struct cpufreq_policy *policy) { unsigned int result = 0; - struct acpi_processor_performance *perf = performance[policy->cpu]; + struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; ACPI_FUNCTION_TRACE("acpi_cpufreq_verify"); result = cpufreq_frequency_table_verify(policy, - perf->freq_table); - - cpufreq_verify_within_limits( - policy, - perf->states[perf->state_count - 1].core_frequency * 1000, - perf->states[0].core_frequency * 1000); + data->freq_table); return_VALUE(result); } @@ -253,31 +244,40 @@ acpi_cpufreq_cpu_init ( { unsigned int i; unsigned int cpu = policy->cpu; - struct acpi_processor_performance *perf; + struct cpufreq_acpi_io *data; unsigned int result = 0; ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_init"); - perf = kmalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL); - if (!perf) + data = kmalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL); + if (!data) return_VALUE(-ENOMEM); - memset(perf, 0, sizeof(struct acpi_processor_performance)); + memset(data, 0, sizeof(struct cpufreq_acpi_io)); - performance[cpu] = perf; + acpi_io_data[cpu] = data; - result = acpi_processor_register_performance(perf, cpu); + result = acpi_processor_register_performance(&data->acpi_data, cpu); if (result) goto err_free; /* capability check */ - if (perf->state_count <= 1) + if (data->acpi_data.state_count <= 1) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No P-States\n")); + goto err_unreg; + } + if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO) || + (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported address space [%d, %d]\n", + (u32) (data->acpi_data.control_register.space_id), + (u32) (data->acpi_data.status_register.space_id))); goto err_unreg; + } /* detect transition latency */ policy->cpuinfo.transition_latency = 0; - for (i=0; istate_count; i++) { - if ((perf->states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency) - policy->cpuinfo.transition_latency = perf->states[i].transition_latency * 1000; + for (i=0; iacpi_data.state_count; i++) { + if ((data->acpi_data.states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency) + policy->cpuinfo.transition_latency = data->acpi_data.states[i].transition_latency * 1000; } policy->governor = CPUFREQ_DEFAULT_GOVERNOR; @@ -285,36 +285,36 @@ acpi_cpufreq_cpu_init ( * The current speed is unknown and not detectable by ACPI... argh! Assume * it's P0, it will be set to this value later during initialization. */ - policy->cur = perf->states[0].core_frequency * 1000; + policy->cur = data->acpi_data.states[0].core_frequency * 1000; /* table init */ - for (i=0; i<=perf->state_count; i++) + for (i=0; i<=data->acpi_data.state_count; i++) { - perf->freq_table[i].index = i; - if (istate_count) - perf->freq_table[i].frequency = perf->states[i].core_frequency * 1000; + data->freq_table[i].index = i; + if (iacpi_data.state_count) + data->freq_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000; else - perf->freq_table[i].frequency = CPUFREQ_TABLE_END; + data->freq_table[i].frequency = CPUFREQ_TABLE_END; } - result = cpufreq_frequency_table_cpuinfo(policy, &perf->freq_table[0]); + result = cpufreq_frequency_table_cpuinfo(policy, &data->freq_table[0]); printk(KERN_INFO "cpufreq: CPU%u - ACPI performance management activated.\n", cpu); - for (i = 0; i < perf->state_count; i++) + for (i = 0; i < data->acpi_data.state_count; i++) printk(KERN_INFO "cpufreq: %cP%d: %d MHz, %d mW, %d uS\n", - (i == perf->state?'*':' '), i, - (u32) perf->states[i].core_frequency, - (u32) perf->states[i].power, - (u32) perf->states[i].transition_latency); + (i == data->acpi_data.state?'*':' '), i, + (u32) data->acpi_data.states[i].core_frequency, + (u32) data->acpi_data.states[i].power, + (u32) data->acpi_data.states[i].transition_latency); return_VALUE(result); err_unreg: - acpi_processor_unregister_performance(perf, cpu); + acpi_processor_unregister_performance(&data->acpi_data, cpu); err_free: - kfree(perf); - performance[cpu] = NULL; + kfree(data); + acpi_io_data[cpu] = NULL; return_VALUE(result); } @@ -324,14 +324,15 @@ static int acpi_cpufreq_cpu_exit ( struct cpufreq_policy *policy) { - struct acpi_processor_performance *perf = performance[policy->cpu]; + struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; + ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_exit"); - if (perf) { - performance[policy->cpu] = NULL; - acpi_processor_unregister_performance(perf, policy->cpu); - kfree(perf); + if (data) { + acpi_io_data[policy->cpu] = NULL; + acpi_processor_unregister_performance(&data->acpi_data, policy->cpu); + kfree(data); } return_VALUE(0); diff --git a/drivers/acpi/processor.c b/drivers/acpi/processor.c index 33800a6efb64..530a3848aa4b 100644 --- a/drivers/acpi/processor.c +++ b/drivers/acpi/processor.c @@ -871,7 +871,6 @@ acpi_processor_get_performance_control ( struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *pct = NULL; union acpi_object obj = {0}; - struct acpi_pct_register *reg = NULL; ACPI_FUNCTION_TRACE("acpi_processor_get_performance_control"); @@ -903,19 +902,9 @@ acpi_processor_get_performance_control ( result = -EFAULT; goto end; } + memcpy(&pr->performance->control_register, obj.buffer.pointer, sizeof(struct acpi_pct_register)); - reg = (struct acpi_pct_register *) (obj.buffer.pointer); - if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Unsupported address space [%d] (control_register)\n", - (u32) reg->space_id)); - result = -EFAULT; - goto end; - } - - pr->performance->control_register = (u16) reg->address; - pr->performance->control_register_bit_width = reg->bit_width; /* * status_register */ @@ -931,23 +920,7 @@ acpi_processor_get_performance_control ( goto end; } - reg = (struct acpi_pct_register *) (obj.buffer.pointer); - - if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Unsupported address space [%d] (status_register)\n", - (u32) reg->space_id)); - result = -EFAULT; - goto end; - } - - pr->performance->status_register = (u16) reg->address; - pr->performance->status_register_bit_width = reg->bit_width; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "control_register[0x%04x] status_register[0x%04x]\n", - pr->performance->control_register, - pr->performance->status_register)); + memcpy(&pr->performance->status_register, obj.buffer.pointer, sizeof(struct acpi_pct_register)); end: acpi_os_free(buffer.pointer); diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 325bcd7713fe..3dfe16b5ccef 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -68,15 +68,12 @@ struct acpi_processor_px { }; struct acpi_processor_performance { - int state; - int platform_limit; - u16 control_register; - u16 status_register; - u8 control_register_bit_width; - u8 status_register_bit_width; - int state_count; + unsigned int state; + unsigned int platform_limit; + struct acpi_pct_register control_register; + struct acpi_pct_register status_register; + unsigned int state_count; struct acpi_processor_px states[ACPI_PROCESSOR_MAX_PERFORMANCE]; - struct cpufreq_frequency_table freq_table[ACPI_PROCESSOR_MAX_PERFORMANCE]; }; -- cgit v1.2.3 From 959a91bd16ee4d9a0abc809360b006284adad9c0 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 30 Jan 2004 11:56:11 -0500 Subject: [ACPI] add _PDC support Add support for _PDC to the ACPI processor "Performance States library" (perflib). If this field is empty, a bogus entry is passed to the _PDC method so that the default (io) access is returned again. This patch is partly based on David Moore's patch to arch/i386/kernel/cpu/cpufreq/acpi.c, sent to the cpufreq mailing list on June 24th, 2003. --- drivers/acpi/processor.c | 33 +++++++++++++++++++++++++++++++++ include/acpi/processor.h | 6 ++++++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/drivers/acpi/processor.c b/drivers/acpi/processor.c index 530a3848aa4b..0c33b088e70f 100644 --- a/drivers/acpi/processor.c +++ b/drivers/acpi/processor.c @@ -862,6 +862,33 @@ static void acpi_processor_ppc_exit(void) { * acpi_processor_performance. */ +static int acpi_processor_set_pdc (struct acpi_processor *pr) +{ + acpi_status status = AE_OK; + u32 arg0_buf[3]; + union acpi_object arg0 = {ACPI_TYPE_BUFFER}; + struct acpi_object_list no_object = {1, &arg0}; + struct acpi_object_list *pdc; + + ACPI_FUNCTION_TRACE("acpi_processor_set_pdc"); + + arg0.buffer.length = 12; + arg0.buffer.pointer = (u8 *) arg0_buf; + arg0_buf[0] = ACPI_PDC_REVISION_ID; + arg0_buf[1] = 0; + arg0_buf[2] = 0; + + pdc = (pr->performance->pdc) ? pr->performance->pdc : &no_object; + + status = acpi_evaluate_object(pr->handle, "_PDC", pdc, NULL); + + if ((ACPI_FAILURE(status)) && (pr->performance->pdc)) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Error evaluating _PDC, using legacy perf. control...\n")); + + return_VALUE(status); +} + + static int acpi_processor_get_performance_control ( struct acpi_processor *pr) @@ -1029,6 +1056,8 @@ acpi_processor_get_performance_info ( return_VALUE(-ENODEV); } + acpi_processor_set_pdc(pr); + result = acpi_processor_get_performance_control(pr); if (result) return_VALUE(result); @@ -1037,6 +1066,10 @@ acpi_processor_get_performance_info ( if (result) return_VALUE(result); + result = acpi_processor_get_platform_limit(pr); + if (result) + return_VALUE(result); + return_VALUE(0); } diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 3dfe16b5ccef..bce3ab80d0f3 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -67,6 +67,8 @@ struct acpi_processor_px { acpi_integer status; /* success indicator */ }; +#define ACPI_PDC_REVISION_ID 0x1 + struct acpi_processor_performance { unsigned int state; unsigned int platform_limit; @@ -74,9 +76,13 @@ struct acpi_processor_performance { struct acpi_pct_register status_register; unsigned int state_count; struct acpi_processor_px states[ACPI_PROCESSOR_MAX_PERFORMANCE]; + + /* the _PDC objects passed by the driver, if any */ + struct acpi_object_list *pdc; }; + /* Throttling Control */ struct acpi_processor_tx { -- cgit v1.2.3 From 3d92baa98a809112d05377e5407a21a15598d7e2 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 30 Jan 2004 11:56:37 -0500 Subject: [ACPI] make # of performance states dynamic from Dominik Brodowski --- arch/i386/kernel/cpu/cpufreq/acpi.c | 17 ++++++++++++++++- drivers/acpi/processor.c | 17 +++++++++-------- include/acpi/processor.h | 4 +--- 3 files changed, 26 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/cpu/cpufreq/acpi.c b/arch/i386/kernel/cpu/cpufreq/acpi.c index 37b095ff6c9d..12ff2d16f13f 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi.c @@ -53,7 +53,7 @@ MODULE_LICENSE("GPL"); struct cpufreq_acpi_io { struct acpi_processor_performance acpi_data; - struct cpufreq_frequency_table freq_table[ACPI_PROCESSOR_MAX_PERFORMANCE]; + struct cpufreq_frequency_table *freq_table; }; static struct cpufreq_acpi_io *acpi_io_data[NR_CPUS]; @@ -263,6 +263,7 @@ acpi_cpufreq_cpu_init ( /* capability check */ if (data->acpi_data.state_count <= 1) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No P-States\n")); + result = -ENODEV; goto err_unreg; } if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO) || @@ -270,6 +271,14 @@ acpi_cpufreq_cpu_init ( ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported address space [%d, %d]\n", (u32) (data->acpi_data.control_register.space_id), (u32) (data->acpi_data.status_register.space_id))); + result = -ENODEV; + goto err_unreg; + } + + /* alloc freq_table */ + data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * (data->acpi_data.state_count + 1), GFP_KERNEL); + if (!data->freq_table) { + result = -ENOMEM; goto err_unreg; } @@ -298,6 +307,10 @@ acpi_cpufreq_cpu_init ( } result = cpufreq_frequency_table_cpuinfo(policy, &data->freq_table[0]); + if (result) { + goto err_freqfree; + } + printk(KERN_INFO "cpufreq: CPU%u - ACPI performance management activated.\n", cpu); @@ -310,6 +323,8 @@ acpi_cpufreq_cpu_init ( return_VALUE(result); + err_freqfree: + kfree(data->freq_table); err_unreg: acpi_processor_unregister_performance(&data->acpi_data, cpu); err_free: diff --git a/drivers/acpi/processor.c b/drivers/acpi/processor.c index 0c33b088e70f..fe8a0d7121cb 100644 --- a/drivers/acpi/processor.c +++ b/drivers/acpi/processor.c @@ -23,7 +23,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * TBD: - * 1. Make # power/performance states dynamic. + * 1. Make # power states dynamic. * 2. Support duty_cycle values that span bit 4. * 3. Optimize by having scheduler determine business instead of * having us try to calculate it here. @@ -986,14 +986,12 @@ acpi_processor_get_performance_states ( ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n", pss->package.count)); - if (pss->package.count > ACPI_PROCESSOR_MAX_PERFORMANCE) { - pr->performance->state_count = ACPI_PROCESSOR_MAX_PERFORMANCE; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Limiting number of states to max (%d)\n", - ACPI_PROCESSOR_MAX_PERFORMANCE)); + pr->performance->state_count = pss->package.count; + pr->performance->states = kmalloc(sizeof(struct acpi_processor_px) * pss->package.count, GFP_KERNEL); + if (!pr->performance->states) { + result = -ENOMEM; + goto end; } - else - pr->performance->state_count = pss->package.count; for (i = 0; i < pr->performance->state_count; i++) { @@ -1009,6 +1007,7 @@ acpi_processor_get_performance_states ( if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n")); result = -EFAULT; + kfree(pr->performance->states); goto end; } @@ -1025,6 +1024,7 @@ acpi_processor_get_performance_states ( if (!px->core_frequency) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "core_frequency is 0\n")); result = -EFAULT; + kfree(pr->performance->states); goto end; } } @@ -1281,6 +1281,7 @@ acpi_processor_unregister_performance ( return_VOID; } + kfree(pr->performance->states); pr->performance = NULL; acpi_cpufreq_remove_file(pr); diff --git a/include/acpi/processor.h b/include/acpi/processor.h index bce3ab80d0f3..dab7521d8ac6 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -9,8 +9,6 @@ #define ACPI_PROCESSOR_MAX_C2_LATENCY 100 #define ACPI_PROCESSOR_MAX_C3_LATENCY 1000 -#define ACPI_PROCESSOR_MAX_PERFORMANCE 8 - #define ACPI_PROCESSOR_MAX_THROTTLING 16 #define ACPI_PROCESSOR_MAX_THROTTLE 250 /* 25% */ #define ACPI_PROCESSOR_MAX_DUTY_WIDTH 4 @@ -75,7 +73,7 @@ struct acpi_processor_performance { struct acpi_pct_register control_register; struct acpi_pct_register status_register; unsigned int state_count; - struct acpi_processor_px states[ACPI_PROCESSOR_MAX_PERFORMANCE]; + struct acpi_processor_px *states; /* the _PDC objects passed by the driver, if any */ struct acpi_object_list *pdc; -- cgit v1.2.3 From c884ee809940c086a8a11a63f88ce7cd891cb881 Mon Sep 17 00:00:00 2001 From: Deepak Saxena Date: Sun, 1 Feb 2004 19:05:17 -0800 Subject: [PATCH] PCI: Replace pci_pool with generic dma_pool, part 2 include/linux changes: - Add dma_pools member to struct device - Add include/linux/dmapool.h - Remove pools memober from struct pci_dev - Replace pci_pool_* functions with macros that map to dma_pool_* functions --- include/linux/device.h | 1 + include/linux/dmapool.h | 27 +++++++++++++++++++++++++++ include/linux/pci.h | 14 ++++++++------ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 include/linux/dmapool.h (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index 367b21888ad4..4cf6e0d490d5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -284,6 +284,7 @@ struct device { detached from its driver. */ u64 *dma_mask; /* dma mask (if dma'able device) */ + struct list_head dma_pools; /* dma pools (if dma'ble) */ void (*release)(struct device * dev); }; diff --git a/include/linux/dmapool.h b/include/linux/dmapool.h new file mode 100644 index 000000000000..e60bfdac348d --- /dev/null +++ b/include/linux/dmapool.h @@ -0,0 +1,27 @@ +/* + * include/linux/dmapool.h + * + * Allocation pools for DMAable (coherent) memory. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef LINUX_DMAPOOL_H +#define LINUX_DMAPOOL_H + +#include +#include + +struct dma_pool *dma_pool_create(const char *name, struct device *dev, + size_t size, size_t align, size_t allocation); + +void dma_pool_destroy(struct dma_pool *pool); + +void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle); + +void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr); + +#endif + diff --git a/include/linux/pci.h b/include/linux/pci.h index 612d14e90587..bde9c5e8dbf8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -393,7 +393,6 @@ struct pci_dev { 0xffffffff. You only need to change this if your device has broken DMA or supports 64-bit transfers. */ - struct list_head pools; /* pci_pools tied to this device */ u64 consistent_dma_mask;/* Like dma_mask, but for pci_alloc_consistent mappings as @@ -692,12 +691,15 @@ const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, co int pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass); /* kmem_cache style wrapper around pci_alloc_consistent() */ -struct pci_pool *pci_pool_create (const char *name, struct pci_dev *dev, - size_t size, size_t align, size_t allocation); -void pci_pool_destroy (struct pci_pool *pool); -void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle); -void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr); +#include + +#define pci_pool dma_pool +#define pci_pool_create(name, pdev, size, align, allocation) \ + dma_pool_create(name, &pdev->dev, size, align, allocation) +#define pci_pool_destroy(pool) dma_pool_destroy(pool) +#define pci_pool_alloc(pool, flags, handle) dma_pool_alloc(pool, flags, handle) +#define pci_pool_free(pool, vaddr, addr) dma_pool_free(pool, vaddr, addr) #if defined(CONFIG_ISA) || defined(CONFIG_EISA) extern struct pci_dev *isa_bridge; -- cgit v1.2.3 From 0eb9b18eb108a9d169084ee154155041f413e945 Mon Sep 17 00:00:00 2001 From: Jon Smirl Date: Sun, 1 Feb 2004 23:39:32 -0800 Subject: [PATCH] Driver core: add hotplug support for class_simple This is needed by the DRI code. --- drivers/base/class_simple.c | 18 ++++++++++++++++++ include/linux/device.h | 2 ++ 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/drivers/base/class_simple.c b/drivers/base/class_simple.c index bb29e34bd2e5..53548e0ed544 100644 --- a/drivers/base/class_simple.c +++ b/drivers/base/class_simple.c @@ -169,6 +169,24 @@ error: } EXPORT_SYMBOL(class_simple_device_add); +/** + * class_simple_set_hotplug - set the hotplug callback in the embedded struct class + * @cs: pointer to the struct class_simple to hold the pointer + * @hotplug: function pointer to the hotplug function + * + * Implement and set a hotplug function to add environment variables specific to this + * class on the hotplug event. + */ +int class_simple_set_hotplug(struct class_simple *cs, + int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size)) +{ + if ((cs == NULL) || (IS_ERR(cs))) + return -ENODEV; + cs->class.hotplug = hotplug; + return 0; +} +EXPORT_SYMBOL(class_simple_set_hotplug); + /** * class_simple_device_remove - removes a class device that was created with class_simple_device_add() * @dev: the dev_t of the device that was previously registered. diff --git a/include/linux/device.h b/include/linux/device.h index 367b21888ad4..c8f5aa7d9f5a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -253,6 +253,8 @@ extern struct class_simple *class_simple_create(struct module *owner, char *name extern void class_simple_destroy(struct class_simple *cs); extern struct class_device *class_simple_device_add(struct class_simple *cs, dev_t dev, struct device *device, const char *fmt, ...) __attribute__((format(printf,4,5))); +extern int class_simple_set_hotplug(struct class_simple *, + int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size)); extern void class_simple_device_remove(dev_t dev); -- cgit v1.2.3 From 9d6cc8f23f0e0d00a29faabf059e12cdd2c1becb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 3 Feb 2004 21:48:53 -0800 Subject: [PATCH] Driver core: remove device_unregister_wait() as it's a very bad idea. --- drivers/base/core.c | 23 ----------------------- include/linux/device.h | 2 -- 2 files changed, 25 deletions(-) (limited to 'include') diff --git a/drivers/base/core.c b/drivers/base/core.c index 84bc01e0ce59..6c84ff06026b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -76,7 +76,6 @@ static struct sysfs_ops dev_sysfs_ops = { static void device_release(struct kobject * kobj) { struct device * dev = to_dev(kobj); - struct completion * c = dev->complete; if (dev->release) dev->release(dev); @@ -86,8 +85,6 @@ static void device_release(struct kobject * kobj) dev->bus_id); WARN_ON(1); } - if (c) - complete(c); } static struct kobj_type ktype_device = { @@ -354,25 +351,6 @@ void device_unregister(struct device * dev) } -/** - * device_unregister_wait - Unregister device and wait for it to be freed. - * @dev: Device to unregister. - * - * For the cases where the caller needs to wait for all references to - * be dropped from the device before continuing (e.g. modules with - * statically allocated devices), this function uses a completion struct - * to wait, along with a matching complete() in device_release() above. - */ - -void device_unregister_wait(struct device * dev) -{ - struct completion c; - init_completion(&c); - dev->complete = &c; - device_unregister(dev); - wait_for_completion(&c); -} - /** * device_for_each_child - device child iterator. * @dev: parent struct device. @@ -421,7 +399,6 @@ EXPORT_SYMBOL(device_register); EXPORT_SYMBOL(device_del); EXPORT_SYMBOL(device_unregister); -EXPORT_SYMBOL(device_unregister_wait); EXPORT_SYMBOL(get_device); EXPORT_SYMBOL(put_device); EXPORT_SYMBOL(device_find); diff --git a/include/linux/device.h b/include/linux/device.h index c8f5aa7d9f5a..6da63dac06b0 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -265,7 +265,6 @@ struct device { struct list_head children; struct device * parent; - struct completion * complete; /* Notification for freeing device. */ struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ @@ -313,7 +312,6 @@ dev_set_drvdata (struct device *dev, void *data) */ extern int device_register(struct device * dev); extern void device_unregister(struct device * dev); -extern void device_unregister_wait(struct device * dev); extern void device_initialize(struct device * dev); extern int device_add(struct device * dev); extern void device_del(struct device * dev); -- cgit v1.2.3 From 1f4e5444b4b7302abd1bd7541453a6be13f12be0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 5 Feb 2004 15:11:48 +0100 Subject: [Bluetooth] Support for tracking the voice setting This patch makes sure that the Bluetooth core layer always knows the current voice setting. It will be read on device initialization and then stored in the hci_dev structure. --- include/net/bluetooth/hci.h | 11 +++++++++++ include/net/bluetooth/hci_core.h | 7 ++++--- net/bluetooth/hci_core.c | 3 +++ net/bluetooth/hci_event.c | 38 +++++++++++++++++++++++++++++++++++++- 4 files changed, 55 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 59523c39d6fb..39bce6f7bd5d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -253,6 +253,17 @@ struct hci_cp_write_dev_class { __u8 dev_class[3]; } __attribute__ ((packed)); +#define OCF_READ_VOICE_SETTING 0x0025 +struct hci_rp_read_voice_setting { + __u8 status; + __u16 voice_setting; +} __attribute__ ((packed)); + +#define OCF_WRITE_VOICE_SETTING 0x0026 +struct hci_cp_write_voice_setting { + __u16 voice_setting; +} __attribute__ ((packed)); + #define OCF_HOST_BUFFER_SIZE 0x0033 struct hci_cp_host_buffer_size { __u16 acl_mtu; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6a429873df9c..03c51bba58a3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -71,11 +71,12 @@ struct hci_dev { __u8 type; bdaddr_t bdaddr; __u8 features[8]; + __u16 voice_setting; __u16 pkt_type; __u16 link_policy; __u16 link_mode; - + atomic_t cmd_cnt; unsigned int acl_cnt; unsigned int sco_cnt; @@ -88,7 +89,7 @@ struct hci_dev { unsigned long cmd_last_tx; unsigned long acl_last_tx; unsigned long sco_last_tx; - + struct tasklet_struct cmd_task; struct tasklet_struct rx_task; struct tasklet_struct tx_task; @@ -119,7 +120,7 @@ struct hci_dev { #endif struct module *owner; - + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b760910ed84e..da0282f54ab2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -237,6 +237,9 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) /* Read BD Address */ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BD_ADDR, 0, NULL); + /* Read Voice Setting */ + hci_send_cmd(hdev, OGF_HOST_CTL, OCF_READ_VOICE_SETTING, 0, NULL); + /* Optional initialization */ /* Clear Event Filters */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 262571e2f521..4a9037f65956 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -123,6 +123,8 @@ static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff * static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) { __u8 status, param; + __u16 setting; + struct hci_rp_read_voice_setting *vs; void *sent; BT_DBG("%s ocf 0x%x", hdev->name, ocf); @@ -198,6 +200,7 @@ static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE); if (!sent) break; + status = *((__u8 *) skb->data); param = *((__u8 *) sent); @@ -215,6 +218,39 @@ static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb hci_req_complete(hdev, status); break; + case OCF_READ_VOICE_SETTING: + vs = (struct hci_rp_read_voice_setting *) skb->data; + + if (vs->status) { + BT_DBG("%s READ_VOICE_SETTING failed %d", hdev->name, vc->status); + break; + } + + setting = __le16_to_cpu(vs->voice_setting); + + if (hdev->voice_setting != setting ) { + hdev->voice_setting = setting; + + BT_DBG("%s: voice setting 0x%04x", hdev->name, setting); + } + break; + + case OCF_WRITE_VOICE_SETTING: + sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING); + if (!sent) + break; + + status = *((__u8 *) skb->data); + setting = __le16_to_cpu(get_unaligned((__u16 *) sent)); + + if (!status && hdev->voice_setting != setting) { + hdev->voice_setting = setting; + + BT_DBG("%s: voice setting 0x%04x", hdev->name, setting); + } + hci_req_complete(hdev, status); + break; + case OCF_HOST_BUFFER_SIZE: status = *((__u8 *) skb->data); if (status) { @@ -282,7 +318,7 @@ static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *s hdev->sco_pkts = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt); BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name, - hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts); + hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts); break; case OCF_READ_BD_ADDR: -- cgit v1.2.3 From d4f63c8a506cf37ea05681f824cdd407ea8aeaf8 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 5 Feb 2004 22:07:24 -0800 Subject: [PATCH] Char drivers: cdev_unmap() To recap my argument: the current cdev implementation keeps an uncounted reference to every cdev in cdev_map. Creators of cdevs must know to call cdev_unmap() with the same arguments they passed to cdev_add() before releasing the device, or that reference will remain and will oops the kernel should user space attempt to open the (missing) device. It's an easy mistake to make, and, IMO, entirely unnecessary; the cdev code should be able to do its own bookkeeping. --- drivers/char/tty_io.c | 1 - drivers/ieee1394/amdtp.c | 1 - drivers/ieee1394/dv1394.c | 1 - drivers/ieee1394/raw1394.c | 1 - drivers/ieee1394/video1394.c | 1 - drivers/scsi/sg.c | 1 - drivers/scsi/st.c | 4 ---- fs/char_dev.c | 7 ++++--- include/linux/cdev.h | 4 ++-- 9 files changed, 6 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 963f6fb2b5dd..33a9c5cb4ecd 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2264,7 +2264,6 @@ int tty_unregister_driver(struct tty_driver *driver) if (driver->refcount) return -EBUSY; - cdev_unmap(MKDEV(driver->major, driver->minor_start), driver->num); unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), driver->num); diff --git a/drivers/ieee1394/amdtp.c b/drivers/ieee1394/amdtp.c index 3fc004d47884..94630dcaac2d 100644 --- a/drivers/ieee1394/amdtp.c +++ b/drivers/ieee1394/amdtp.c @@ -1308,7 +1308,6 @@ static void __exit amdtp_exit_module (void) hpsb_unregister_highlevel(&amdtp_highlevel); devfs_remove("amdtp"); - cdev_unmap(IEEE1394_AMDTP_DEV, 16); cdev_del(&amdtp_cdev); HPSB_INFO("Unloaded AMDTP driver"); diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index dd16cccc2eb1..881340aa52ac 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -2609,7 +2609,6 @@ static void __exit dv1394_exit_module(void) hpsb_unregister_protocol(&dv1394_driver); hpsb_unregister_highlevel(&dv1394_highlevel); - cdev_unmap(IEEE1394_DV1394_DEV, 16); cdev_del(&dv1394_cdev); devfs_remove("ieee1394/dv"); } diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 83e5a7c5149b..405c6d59bcd9 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -2682,7 +2682,6 @@ static int __init init_raw1394(void) static void __exit cleanup_raw1394(void) { hpsb_unregister_protocol(&raw1394_driver); - cdev_unmap(IEEE1394_RAW1394_DEV, 1); cdev_del(&raw1394_cdev); devfs_remove(RAW1394_DEVICE_NAME); hpsb_unregister_highlevel(&raw1394_highlevel); diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c index 6353ef25628d..d66d517d0187 100644 --- a/drivers/ieee1394/video1394.c +++ b/drivers/ieee1394/video1394.c @@ -1447,7 +1447,6 @@ static void __exit video1394_exit_module (void) hpsb_unregister_highlevel(&video1394_highlevel); devfs_remove(VIDEO1394_DRIVER_NAME); - cdev_unmap(IEEE1394_VIDEO1394_DEV, 16); cdev_del(&video1394_cdev); PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module"); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 6c210f52c5e0..9141152f33ce 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1521,7 +1521,6 @@ sg_remove(struct class_device *cl_dev) if (sdp) { sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); class_simple_device_remove(MKDEV(SCSI_GENERIC_MAJOR, k)); - cdev_unmap(MKDEV(SCSI_GENERIC_MAJOR, k), 1); cdev_del(sdp->cdev); sdp->cdev = NULL; devfs_remove("%s/generic", scsidp->devfs_name); diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index fc5b890e6f80..d566897fecbe 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3946,8 +3946,6 @@ out_free_tape: if (cdev == STm->cdevs[j]) cdev = NULL; sysfs_remove_link(&STm->cdevs[j]->kobj, "device"); - cdev_unmap(MKDEV(SCSI_TAPE_MAJOR, - TAPE_MINOR(dev_num, mode, j)), 1); cdev_del(STm->cdevs[j]); } } @@ -3990,8 +3988,6 @@ static int st_remove(struct device *dev) for (j=0; j < 2; j++) { sysfs_remove_link(&tpnt->modes[mode].cdevs[j]->kobj, "device"); - cdev_unmap(MKDEV(SCSI_TAPE_MAJOR, - TAPE_MINOR(i, mode, j)), 1); cdev_del(tpnt->modes[mode].cdevs[j]); tpnt->modes[mode].cdevs[j] = NULL; } diff --git a/fs/char_dev.c b/fs/char_dev.c index 50ab4cf8e8e1..7a1d47c3157a 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -240,7 +240,6 @@ void unregister_chrdev_region(dev_t from, unsigned count) int unregister_chrdev(unsigned int major, const char *name) { struct char_device_struct *cd; - cdev_unmap(MKDEV(major, 0), 256); cd = __unregister_chrdev_region(major, 0, 256); if (cd && cd->cdev) cdev_del(cd->cdev); @@ -347,16 +346,19 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count) err = kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); if (err) kobject_del(&p->kobj); + p->dev = dev; + p->count = count; return err; } -void cdev_unmap(dev_t dev, unsigned count) +static void cdev_unmap(dev_t dev, unsigned count) { kobj_unmap(cdev_map, dev, count); } void cdev_del(struct cdev *p) { + cdev_unmap(p->dev, p->count); kobject_del(&p->kobj); kobject_put(&p->kobj); } @@ -458,6 +460,5 @@ EXPORT_SYMBOL(cdev_get); EXPORT_SYMBOL(cdev_put); EXPORT_SYMBOL(cdev_del); EXPORT_SYMBOL(cdev_add); -EXPORT_SYMBOL(cdev_unmap); EXPORT_SYMBOL(register_chrdev); EXPORT_SYMBOL(unregister_chrdev); diff --git a/include/linux/cdev.h b/include/linux/cdev.h index 191c800fa127..f1996ec09e96 100644 --- a/include/linux/cdev.h +++ b/include/linux/cdev.h @@ -7,6 +7,8 @@ struct cdev { struct module *owner; struct file_operations *ops; struct list_head list; + dev_t dev; + unsigned int count; }; void cdev_init(struct cdev *, struct file_operations *); @@ -21,8 +23,6 @@ int cdev_add(struct cdev *, dev_t, unsigned); void cdev_del(struct cdev *); -void cdev_unmap(dev_t, unsigned); - void cd_forget(struct inode *); #endif -- cgit v1.2.3 From 2be90e00a8e1231a3b6c1d3376f066da591b1d9b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 6 Feb 2004 03:32:56 -0800 Subject: [SPARC]: Fix AIO syscall numbering. --- include/asm-sparc/unistd.h | 14 +++++++------- include/asm-sparc64/unistd.h | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/asm-sparc/unistd.h b/include/asm-sparc/unistd.h index 044ab5ab5747..695768130a4c 100644 --- a/include/asm-sparc/unistd.h +++ b/include/asm-sparc/unistd.h @@ -285,14 +285,14 @@ #define __NR_timer_create 266 /* #define __NR_vserver 267 Reserved for VSERVER */ #define __NR_io_setup 268 -#define __NR_io_destroy 268 -#define __NR_io_submit 269 -#define __NR_io_cancel 270 -#define __NR_io_getevents 271 -/* WARNING: You MAY NOT add syscall numbers larger than 271, since +#define __NR_io_destroy 269 +#define __NR_io_submit 270 +#define __NR_io_cancel 271 +#define __NR_io_getevents 272 +/* WARNING: You MAY NOT add syscall numbers larger than 272, since * all of the syscall tables in the Sparc kernel are - * sized to have 272 entries (starting at zero). Therefore - * find a free slot in the 0-271 range. + * sized to have 273 entries (starting at zero). Therefore + * find a free slot in the 0-272 range. */ #define _syscall0(type,name) \ diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h index defdb109015b..109d44cfabfe 100644 --- a/include/asm-sparc64/unistd.h +++ b/include/asm-sparc64/unistd.h @@ -287,14 +287,14 @@ #define __NR_timer_create 266 /* #define __NR_vserver 267 Reserved for VSERVER */ #define __NR_io_setup 268 -#define __NR_io_destroy 268 -#define __NR_io_submit 269 -#define __NR_io_cancel 270 -#define __NR_io_getevents 271 -/* WARNING: You MAY NOT add syscall numbers larger than 271, since +#define __NR_io_destroy 269 +#define __NR_io_submit 270 +#define __NR_io_cancel 271 +#define __NR_io_getevents 272 +/* WARNING: You MAY NOT add syscall numbers larger than 272, since * all of the syscall tables in the Sparc kernel are - * sized to have 272 entries (starting at zero). Therefore - * find a free slot in the 0-271 range. + * sized to have 273 entries (starting at zero). Therefore + * find a free slot in the 0-272 range. */ #define _syscall0(type,name) \ -- cgit v1.2.3 From 2724a14b455e66ddac226b2955e15eb2ddef1721 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 7 Feb 2004 04:48:53 -0800 Subject: [PATCH] fix duplication of DMA {black,white}list in icside.c Always compile ide-dma.c if CONFIG_BLK_DEV_IDEDMA=y, mark PCI specific code with CONFIG_BLK_DEV_IDEDMA_PCI for now (it should migrate to ide_pcidma.c over a time). This fixes a small bug - in_drive_list() from icside.c used !strstr() instead of strstr() so it was missing two entries from a blacklist. --- drivers/ide/Makefile | 2 +- drivers/ide/arm/icside.c | 73 ++---------------------------------------------- drivers/ide/ide-dma.c | 19 ++++++++----- include/linux/ide.h | 8 ++++-- 4 files changed, 21 insertions(+), 81 deletions(-) (limited to 'include') diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 488c784ce670..7cc48f1f4c6a 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -20,7 +20,7 @@ ide-core-$(CONFIG_BLK_DEV_CMD640) += pci/cmd640.o # Core IDE code - must come before legacy ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o -ide-core-$(CONFIG_BLK_DEV_IDEDMA_PCI) += ide-dma.o +ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o ide-core-$(CONFIG_BLK_DEV_IDE_TCQ) += ide-tcq.o ide-core-$(CONFIG_PROC_FS) += ide-proc.o ide-core-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 8f582564fbb2..09d3f9414e8f 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -330,72 +330,6 @@ static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode) return on; } -/* - * The following is a sick duplication from ide-dma.c ;( - * - * This should be defined in one place only. - */ -struct drive_list_entry { - const char * id_model; - const char * id_firmware; -}; - -static const struct drive_list_entry drive_whitelist [] = { - { "Micropolis 2112A", "ALL" }, - { "CONNER CTMA 4000", "ALL" }, - { "CONNER CTT8000-A", "ALL" }, - { "ST34342A", "ALL" }, - { NULL, NULL } -}; - -static struct drive_list_entry drive_blacklist [] = { - { "WDC AC11000H", "ALL" }, - { "WDC AC22100H", "ALL" }, - { "WDC AC32500H", "ALL" }, - { "WDC AC33100H", "ALL" }, - { "WDC AC31600H", "ALL" }, - { "WDC AC32100H", "24.09P07" }, - { "WDC AC23200L", "21.10N21" }, - { "Compaq CRD-8241B", "ALL" }, - { "CRD-8400B", "ALL" }, - { "CRD-8480B", "ALL" }, - { "CRD-8480C", "ALL" }, - { "CRD-8482B", "ALL" }, - { "CRD-84", "ALL" }, - { "SanDisk SDP3B", "ALL" }, - { "SanDisk SDP3B-64", "ALL" }, - { "SANYO CD-ROM CRD", "ALL" }, - { "HITACHI CDR-8", "ALL" }, - { "HITACHI CDR-8335", "ALL" }, - { "HITACHI CDR-8435", "ALL" }, - { "Toshiba CD-ROM XM-6202B", "ALL" }, - { "CD-532E-A", "ALL" }, - { "E-IDE CD-ROM CR-840", "ALL" }, - { "CD-ROM Drive/F5A", "ALL" }, - { "RICOH CD-R/RW MP7083A", "ALL" }, - { "WPI CDD-820", "ALL" }, - { "SAMSUNG CD-ROM SC-148C", "ALL" }, - { "SAMSUNG CD-ROM SC-148F", "ALL" }, - { "SAMSUNG CD-ROM SC", "ALL" }, - { "SanDisk SDP3B-64", "ALL" }, - { "SAMSUNG CD-ROM SN-124", "ALL" }, - { "PLEXTOR CD-R PX-W8432T", "ALL" }, - { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, - { "_NEC DV5800A", "ALL" }, - { NULL, NULL } -}; - -static int -in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table) -{ - for ( ; drive_table->id_model ; drive_table++) - if ((!strcmp(drive_table->id_model, id->model)) && - ((!strstr(drive_table->id_firmware, id->fw_rev)) || - (!strcmp(drive_table->id_firmware, "ALL")))) - return 1; - return 0; -} - static int icside_dma_host_off(ide_drive_t *drive) { return 0; @@ -437,11 +371,8 @@ static int icside_dma_check(ide_drive_t *drive) /* * Consult the list of known "bad" drives */ - if (in_drive_list(id, drive_blacklist)) { - printk("%s: Disabling DMA for %s (blacklisted)\n", - drive->name, id->model); + if (__ide_dma_bad_drive(drive)) goto out; - } /* * Enable DMA on any drive that has multiword DMA @@ -454,7 +385,7 @@ static int icside_dma_check(ide_drive_t *drive) /* * Consult the list of known "good" drives */ - if (in_drive_list(id, drive_whitelist)) { + if (__ide_dma_good_drive(drive)) { if (id->eide_dma_time > 150) goto out; xfer_mode = XFER_MW_DMA_1; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index acf29b5c430a..205c28225ad9 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -90,11 +90,11 @@ #include struct drive_list_entry { - char * id_model; - char * id_firmware; + const char *id_model; + const char *id_firmware; }; -struct drive_list_entry drive_whitelist [] = { +static const struct drive_list_entry drive_whitelist [] = { { "Micropolis 2112A" , "ALL" }, { "CONNER CTMA 4000" , "ALL" }, @@ -103,7 +103,7 @@ struct drive_list_entry drive_whitelist [] = { { 0 , 0 } }; -struct drive_list_entry drive_blacklist [] = { +static const struct drive_list_entry drive_blacklist [] = { { "WDC AC11000H" , "ALL" }, { "WDC AC22100H" , "ALL" }, @@ -151,7 +151,7 @@ struct drive_list_entry drive_blacklist [] = { * Returns 1 if the drive is found in the table. */ -static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table) +static int in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table) { for ( ; drive_table->id_model ; drive_table++) if ((!strcmp(drive_table->id_model, id->model)) && @@ -161,6 +161,7 @@ static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_ return 0; } +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI /** * ide_dma_intr - IDE DMA interrupt handler * @drive: the drive the interrupt is for @@ -764,6 +765,7 @@ int __ide_dma_test_irq (ide_drive_t *drive) } EXPORT_SYMBOL(__ide_dma_test_irq); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ int __ide_dma_bad_drive (ide_drive_t *drive) { @@ -771,8 +773,9 @@ int __ide_dma_bad_drive (ide_drive_t *drive) int blacklist = in_drive_list(id, drive_blacklist); if (blacklist) { - printk(KERN_WARNING "%s: Disabling (U)DMA for %s\n", drive->name, id->model); - return(blacklist); + printk(KERN_WARNING "%s: Disabling (U)DMA for %s (blacklisted)\n", + drive->name, id->model); + return blacklist; } return 0; } @@ -787,6 +790,7 @@ int __ide_dma_good_drive (ide_drive_t *drive) EXPORT_SYMBOL(__ide_dma_good_drive); +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI /* * Used for HOST FIFO counters for VDMA * PIO over DMA, effective ATA-Bridge operator. @@ -1104,3 +1108,4 @@ void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_p } EXPORT_SYMBOL_GPL(ide_setup_dma); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ diff --git a/include/linux/ide.h b/include/linux/ide.h index f5ee5b62adee..d247474de882 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1597,6 +1597,10 @@ extern void ide_setup_pci_devices(struct pci_dev *, struct pci_dev *, ide_pci_de #define BAD_DMA_DRIVE 0 #define GOOD_DMA_DRIVE 1 +#ifdef CONFIG_BLK_DEV_IDEDMA +int __ide_dma_bad_drive(ide_drive_t *); +int __ide_dma_good_drive(ide_drive_t *); + #ifdef CONFIG_BLK_DEV_IDEDMA_PCI extern int ide_build_sglist(ide_drive_t *, struct request *); extern int ide_raw_build_sglist(ide_drive_t *, struct request *); @@ -1618,8 +1622,6 @@ extern int __ide_dma_write(ide_drive_t *); extern int __ide_dma_begin(ide_drive_t *); extern int __ide_dma_end(ide_drive_t *); extern int __ide_dma_test_irq(ide_drive_t *); -extern int __ide_dma_bad_drive(ide_drive_t *); -extern int __ide_dma_good_drive(ide_drive_t *); extern int __ide_dma_count(ide_drive_t *); extern int __ide_dma_verbose(ide_drive_t *); extern int __ide_dma_lostirq(ide_drive_t *); @@ -1637,6 +1639,8 @@ extern ide_startstop_t __ide_dma_queued_start(ide_drive_t *drive); static inline void ide_release_dma(ide_hwif_t *drive) {;} #endif +#endif /* CONFIG_BLK_DEV_IDEDMA */ + extern int ide_hwif_request_regions(ide_hwif_t *hwif); extern void ide_hwif_release_regions(ide_hwif_t* hwif); extern void ide_unregister (unsigned int index); -- cgit v1.2.3 From 8758819af6d985c6a7f2b7c7297b4c90c77561ab Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:23:52 +0100 Subject: NFSv4/RPCSEC_GSS: Ensure that RPC userland upcalls time out correctly if the corresponding userland daemon is not up and running. --- fs/nfs/idmap.c | 1 + include/linux/sunrpc/rpc_pipe_fs.h | 1 + net/sunrpc/auth_gss/auth_gss.c | 1 + net/sunrpc/clnt.c | 1 + net/sunrpc/rpc_pipe.c | 29 +++++++++++++++++++++++++++-- net/sunrpc/sunrpc_syms.c | 1 + 6 files changed, 32 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 537380371957..f984e0156fa2 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -43,6 +43,7 @@ #include #include +#include #include #include diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index eabb2ebf2289..c3752710c74c 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -27,6 +27,7 @@ struct rpc_inode { #define RPC_PIPE_WAIT_FOR_OPEN 1 int flags; struct rpc_pipe_ops *ops; + struct work_struct queue_timeout; }; static inline struct rpc_inode * diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 79dbcca25f1e..ee2438bf57a8 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index fdb5554eb246..badaa121f29c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -30,6 +30,7 @@ #include #include +#include #include #include diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index d6cf592d6c6c..1841ac8cb0da 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -25,6 +25,7 @@ #include #include +#include #include static struct vfsmount *rpc_mount; @@ -35,6 +36,8 @@ static struct file_system_type rpc_pipe_fs_type; static kmem_cache_t *rpc_inode_cachep; +#define RPC_UPCALL_TIMEOUT (30*HZ) + static void __rpc_purge_upcall(struct inode *inode, int err) { @@ -59,6 +62,18 @@ rpc_purge_upcall(struct inode *inode, int err) up(&inode->i_sem); } +static void +rpc_timeout_upcall_queue(void *data) +{ + struct rpc_inode *rpci = (struct rpc_inode *)data; + struct inode *inode = &rpci->vfs_inode; + + down(&inode->i_sem); + if (rpci->nreaders == 0 && !list_empty(&rpci->pipe)) + __rpc_purge_upcall(inode, -ETIMEDOUT); + up(&inode->i_sem); +} + int rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) { @@ -66,7 +81,13 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) int res = 0; down(&inode->i_sem); - if (rpci->nreaders || (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN)) { + if (rpci->nreaders) { + list_add_tail(&msg->list, &rpci->pipe); + rpci->pipelen += msg->len; + } else if (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN) { + if (list_empty(&rpci->pipe)) + schedule_delayed_work(&rpci->queue_timeout, + RPC_UPCALL_TIMEOUT); list_add_tail(&msg->list, &rpci->pipe); rpci->pipelen += msg->len; } else @@ -80,6 +101,9 @@ void rpc_inode_setowner(struct inode *inode, void *private) { struct rpc_inode *rpci = RPC_I(inode); + + cancel_delayed_work(&rpci->queue_timeout); + flush_scheduled_work(); down(&inode->i_sem); rpci->private = private; if (!private) @@ -133,7 +157,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp) down(&inode->i_sem); if (filp->f_mode & FMODE_READ) rpci->nreaders --; - if (!rpci->nreaders && !(rpci->flags & RPC_PIPE_WAIT_FOR_OPEN)) + if (!rpci->nreaders) __rpc_purge_upcall(inode, -EPIPE); up(&inode->i_sem); return 0; @@ -769,6 +793,7 @@ init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) INIT_LIST_HEAD(&rpci->pipe); rpci->pipelen = 0; init_waitqueue_head(&rpci->waitq); + INIT_WORK(&rpci->queue_timeout, rpc_timeout_upcall_queue, rpci); rpci->ops = NULL; } } diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index bb05bd79a133..ff8d2bb7bb18 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -21,6 +21,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 62d3ffc42969a84db8d1e045c79a5dbb7ad5f7ce Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:25:51 +0100 Subject: RPCSEC_GSS: Make the upcalls detect if the userland daemon dies while processing a request. --- include/linux/sunrpc/rpc_pipe_fs.h | 4 ++- net/sunrpc/auth_gss/auth_gss.c | 28 ++++++++++++++++ net/sunrpc/rpc_pipe.c | 67 ++++++++++++++++++++++++++------------ 3 files changed, 78 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index c3752710c74c..63929349571f 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -14,6 +14,7 @@ struct rpc_pipe_msg { struct rpc_pipe_ops { ssize_t (*upcall)(struct file *, struct rpc_pipe_msg *, char __user *, size_t); ssize_t (*downcall)(struct file *, const char __user *, size_t); + void (*release_pipe)(struct inode *); void (*destroy_msg)(struct rpc_pipe_msg *); }; @@ -21,8 +22,10 @@ struct rpc_inode { struct inode vfs_inode; void *private; struct list_head pipe; + struct list_head in_upcall; int pipelen; int nreaders; + int nwriters; wait_queue_head_t waitq; #define RPC_PIPE_WAIT_FOR_OPEN 1 int flags; @@ -36,7 +39,6 @@ RPC_I(struct inode *inode) return container_of(inode, struct rpc_inode, vfs_inode); } -extern void rpc_inode_setowner(struct inode *, void *); extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *); extern struct dentry *rpc_mkdir(char *, struct rpc_clnt *); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 6892bb79d8da..af67459e1cc4 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -482,6 +482,33 @@ err: return err; } +static void +gss_pipe_release(struct inode *inode) +{ + struct rpc_inode *rpci = RPC_I(inode); + struct rpc_clnt *clnt; + struct rpc_auth *auth; + struct gss_auth *gss_auth; + + clnt = rpci->private; + auth = clnt->cl_auth; + gss_auth = container_of(auth, struct gss_auth, rpc_auth); + spin_lock(&gss_auth->lock); + while (!list_empty(&gss_auth->upcalls)) { + struct gss_upcall_msg *gss_msg; + + gss_msg = list_entry(gss_auth->upcalls.next, + struct gss_upcall_msg, list); + gss_msg->msg.errno = -EPIPE; + atomic_inc(&gss_msg->count); + __gss_unhash_msg(gss_msg); + spin_unlock(&gss_auth->lock); + gss_release_msg(gss_msg); + spin_lock(&gss_auth->lock); + } + spin_unlock(&gss_auth->lock); +} + void gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) { @@ -774,6 +801,7 @@ static struct rpc_pipe_ops gss_upcall_ops = { .upcall = gss_pipe_upcall, .downcall = gss_pipe_downcall, .destroy_msg = gss_pipe_destroy_msg, + .release_pipe = gss_pipe_release, }; /* diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 1841ac8cb0da..409e6aef143d 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -50,18 +50,16 @@ __rpc_purge_upcall(struct inode *inode, int err) msg->errno = err; rpci->ops->destroy_msg(msg); } + while (!list_empty(&rpci->in_upcall)) { + msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list); + list_del_init(&msg->list); + msg->errno = err; + rpci->ops->destroy_msg(msg); + } rpci->pipelen = 0; wake_up(&rpci->waitq); } -void -rpc_purge_upcall(struct inode *inode, int err) -{ - down(&inode->i_sem); - __rpc_purge_upcall(inode, err); - up(&inode->i_sem); -} - static void rpc_timeout_upcall_queue(void *data) { @@ -97,20 +95,31 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) return res; } -void -rpc_inode_setowner(struct inode *inode, void *private) +static void +rpc_close_pipes(struct inode *inode) { struct rpc_inode *rpci = RPC_I(inode); cancel_delayed_work(&rpci->queue_timeout); flush_scheduled_work(); down(&inode->i_sem); - rpci->private = private; - if (!private) + if (rpci->ops != NULL) { + rpci->nreaders = 0; __rpc_purge_upcall(inode, -EPIPE); + rpci->nwriters = 0; + if (rpci->ops->release_pipe) + rpci->ops->release_pipe(inode); + rpci->ops = NULL; + } up(&inode->i_sem); } +static inline void +rpc_inode_setowner(struct inode *inode, void *private) +{ + RPC_I(inode)->private = private; +} + static struct inode * rpc_alloc_inode(struct super_block *sb) { @@ -134,9 +143,11 @@ rpc_pipe_open(struct inode *inode, struct file *filp) int res = -ENXIO; down(&inode->i_sem); - if (rpci->private != NULL) { + if (rpci->ops != NULL) { if (filp->f_mode & FMODE_READ) rpci->nreaders ++; + if (filp->f_mode & FMODE_WRITE) + rpci->nwriters ++; res = 0; } up(&inode->i_sem); @@ -149,16 +160,24 @@ rpc_pipe_release(struct inode *inode, struct file *filp) struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode); struct rpc_pipe_msg *msg; + down(&inode->i_sem); + if (rpci->ops == NULL) + goto out; msg = (struct rpc_pipe_msg *)filp->private_data; if (msg != NULL) { msg->errno = -EPIPE; + list_del_init(&msg->list); rpci->ops->destroy_msg(msg); } - down(&inode->i_sem); + if (filp->f_mode & FMODE_WRITE) + rpci->nwriters --; if (filp->f_mode & FMODE_READ) rpci->nreaders --; if (!rpci->nreaders) __rpc_purge_upcall(inode, -EPIPE); + if (rpci->ops->release_pipe) + rpci->ops->release_pipe(inode); +out: up(&inode->i_sem); return 0; } @@ -172,7 +191,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) int res = 0; down(&inode->i_sem); - if (!rpci->private) { + if (rpci->ops == NULL) { res = -EPIPE; goto out_unlock; } @@ -182,7 +201,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list); - list_del_init(&msg->list); + list_move(&msg->list, &rpci->in_upcall); rpci->pipelen -= msg->len; filp->private_data = msg; msg->copied = 0; @@ -194,6 +213,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) res = rpci->ops->upcall(filp, msg, buf, len); if (res < 0 || msg->len == msg->copied) { filp->private_data = NULL; + list_del_init(&msg->list); rpci->ops->destroy_msg(msg); } out_unlock: @@ -210,7 +230,7 @@ rpc_pipe_write(struct file *filp, const char __user *buf, size_t len, loff_t *of down(&inode->i_sem); res = -EPIPE; - if (rpci->private != NULL) + if (rpci->ops != NULL) res = rpci->ops->downcall(filp, buf, len); up(&inode->i_sem); return res; @@ -226,7 +246,7 @@ rpc_pipe_poll(struct file *filp, struct poll_table_struct *wait) poll_wait(filp, &rpci->waitq, wait); mask = POLLOUT | POLLWRNORM; - if (rpci->private == NULL) + if (rpci->ops == NULL) mask |= POLLERR | POLLHUP; if (!list_empty(&rpci->pipe)) mask |= POLLIN | POLLRDNORM; @@ -242,7 +262,7 @@ rpc_pipe_ioctl(struct inode *ino, struct file *filp, switch (cmd) { case FIONREAD: - if (!rpci->private) + if (rpci->ops == NULL) return -EPIPE; len = rpci->pipelen; if (filp->private_data) { @@ -484,6 +504,7 @@ repeat: do { dentry = dvec[--n]; if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); rpc_inode_setowner(dentry->d_inode, NULL); simple_unlink(dir, dentry); } @@ -563,7 +584,10 @@ __rpc_rmdir(struct inode *dir, struct dentry *dentry) int error; shrink_dcache_parent(dentry); - rpc_inode_setowner(dentry->d_inode, NULL); + if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); + rpc_inode_setowner(dentry->d_inode, NULL); + } if ((error = simple_rmdir(dir, dentry)) != 0) return error; if (!error) { @@ -715,6 +739,7 @@ rpc_unlink(char *path) } d_drop(dentry); if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); rpc_inode_setowner(dentry->d_inode, NULL); error = simple_unlink(dir, dentry); } @@ -790,6 +815,8 @@ init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) inode_init_once(&rpci->vfs_inode); rpci->private = NULL; rpci->nreaders = 0; + rpci->nwriters = 0; + INIT_LIST_HEAD(&rpci->in_upcall); INIT_LIST_HEAD(&rpci->pipe); rpci->pipelen = 0; init_waitqueue_head(&rpci->waitq); -- cgit v1.2.3 From b26ef9fe3e350db44fa93605316de62b742fc13b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:29:20 +0100 Subject: NFSv4: Bugfixes and cleanups for the NFSv4 client name to uid mapper. Includes a fix by Tim Woods to deal with a caching bug in the case where a user and a group share the same numerical id and/or name. --- fs/nfs/idmap.c | 372 ++++++++++++++++++++++++---------------------- fs/nfs/nfs4xdr.c | 32 ++-- include/linux/nfs_idmap.h | 17 ++- 3 files changed, 219 insertions(+), 202 deletions(-) (limited to 'include') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 155eb0debd83..bd1d1335561c 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -52,14 +52,16 @@ #include #define IDMAP_HASH_SZ 128 -#define IDMAP_HASH_TYPE_NAME 0x01 -#define IDMAP_HASH_TYPE_ID 0x02 -#define IDMAP_HASH_TYPE_INSERT 0x04 struct idmap_hashent { - uid_t ih_id; - char ih_name[IDMAP_NAMESZ]; - u_int32_t ih_namelen; + __u32 ih_id; + int ih_namelen; + char ih_name[IDMAP_NAMESZ]; +}; + +struct idmap_hashtable { + __u8 h_type; + struct idmap_hashent h_entries[IDMAP_HASH_SZ]; }; struct idmap { @@ -67,12 +69,10 @@ struct idmap { struct dentry *idmap_dentry; wait_queue_head_t idmap_wq; struct idmap_msg idmap_im; - struct nfs_server *idmap_server; - struct semaphore idmap_lock; - struct semaphore idmap_im_lock; - struct semaphore idmap_hash_lock; - struct idmap_hashent idmap_id_hash[IDMAP_HASH_SZ]; - struct idmap_hashent idmap_name_hash[IDMAP_HASH_SZ]; + struct semaphore idmap_lock; /* Serializes upcalls */ + struct semaphore idmap_im_lock; /* Protects the hashtable */ + struct idmap_hashtable idmap_user_hash; + struct idmap_hashtable idmap_group_hash; }; static ssize_t idmap_pipe_upcall(struct file *, struct rpc_pipe_msg *, char *, @@ -80,10 +80,7 @@ static ssize_t idmap_pipe_upcall(struct file *, struct rpc_pipe_msg *, char *, static ssize_t idmap_pipe_downcall(struct file *, const char *, size_t); void idmap_pipe_destroy_msg(struct rpc_pipe_msg *); -static int validate_ascii(char *, u_int32_t); - -static u_int32_t fnvhash32(void *, u_int32_t); -static int idmap_cache_lookup(struct idmap *, int, char *, u_int32_t *, uid_t *); +static unsigned int fnvhash32(const void *, size_t); static struct rpc_pipe_ops idmap_upcall_ops = { .upcall = idmap_pipe_upcall, @@ -101,20 +98,19 @@ nfs_idmap_new(struct nfs_server *server) memset(idmap, 0, sizeof(*idmap)); - idmap->idmap_server = server; - snprintf(idmap->idmap_path, sizeof(idmap->idmap_path), - "%s/idmap", idmap->idmap_server->client->cl_pathname); + "%s/idmap", server->client->cl_pathname); idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path, - idmap->idmap_server, &idmap_upcall_ops, 0); + idmap, &idmap_upcall_ops, 0); if (IS_ERR(idmap->idmap_dentry)) goto err_free; init_MUTEX(&idmap->idmap_lock); init_MUTEX(&idmap->idmap_im_lock); - init_MUTEX(&idmap->idmap_hash_lock); init_waitqueue_head(&idmap->idmap_wq); + idmap->idmap_user_hash.h_type = IDMAP_TYPE_USER; + idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP; return (idmap); @@ -135,35 +131,102 @@ nfs_idmap_delete(struct nfs_server *server) kfree(idmap); } +/* + * Helper routines for manipulating the hashtable + */ +static inline struct idmap_hashent * +idmap_name_hash(struct idmap_hashtable* h, const char *name, size_t len) +{ + return &h->h_entries[fnvhash32(name, len) % IDMAP_HASH_SZ]; +} + +static struct idmap_hashent * +idmap_lookup_name(struct idmap_hashtable *h, const char *name, size_t len) +{ + struct idmap_hashent *he = idmap_name_hash(h, name, len); + + if (he->ih_namelen != len || memcmp(he->ih_name, name, len) != 0) + return NULL; + return he; +} + +static inline struct idmap_hashent * +idmap_id_hash(struct idmap_hashtable* h, __u32 id) +{ + return &h->h_entries[fnvhash32(&id, sizeof(id)) % IDMAP_HASH_SZ]; +} + +static struct idmap_hashent * +idmap_lookup_id(struct idmap_hashtable *h, __u32 id) +{ + struct idmap_hashent *he = idmap_id_hash(h, id); + if (he->ih_id != id || he->ih_namelen == 0) + return NULL; + return he; +} + +/* + * Routines for allocating new entries in the hashtable. + * For now, we just have 1 entry per bucket, so it's all + * pretty trivial. + */ +static inline struct idmap_hashent * +idmap_alloc_name(struct idmap_hashtable *h, char *name, unsigned len) +{ + return idmap_name_hash(h, name, len); +} + +static inline struct idmap_hashent * +idmap_alloc_id(struct idmap_hashtable *h, __u32 id) +{ + return idmap_id_hash(h, id); +} + +static void +idmap_update_entry(struct idmap_hashent *he, const char *name, + size_t namelen, __u32 id) +{ + he->ih_id = id; + memcpy(he->ih_name, name, namelen); + he->ih_name[namelen] = '\0'; + he->ih_namelen = namelen; +} + /* * Name -> ID */ -int -nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name, - u_int namelen, uid_t *id) +static int +nfs_idmap_id(struct idmap *idmap, struct idmap_hashtable *h, + const char *name, size_t namelen, __u32 *id) { struct rpc_pipe_msg msg; - struct idmap *idmap = server->idmap; struct idmap_msg *im; + struct idmap_hashent *he; DECLARE_WAITQUEUE(wq, current); - int ret = -1, hashtype = IDMAP_HASH_TYPE_NAME; - u_int xnamelen = namelen; - - if (idmap == NULL) - return (-1); + int ret = -EIO; im = &idmap->idmap_im; - if (namelen > IDMAP_NAMESZ || namelen == 0) - return (-1); + /* + * String sanity checks + * Note that the userland daemon expects NUL terminated strings + */ + for (;;) { + if (namelen == 0) + return -EINVAL; + if (name[namelen-1] != '\0') + break; + namelen--; + } + if (namelen >= IDMAP_NAMESZ) + return -EINVAL; down(&idmap->idmap_lock); down(&idmap->idmap_im_lock); - if (name[xnamelen - 1] == '\0') - xnamelen--; - - if (idmap_cache_lookup(idmap, hashtype, name, &xnamelen, id) == 0) { + he = idmap_lookup_name(h, name, namelen); + if (he != NULL) { + *id = he->ih_id; ret = 0; goto out; } @@ -171,7 +234,7 @@ nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name, memset(im, 0, sizeof(*im)); memcpy(im->im_name, name, namelen); - im->im_type = type; + im->im_type = h->h_type; im->im_conv = IDMAP_CONV_NAMETOID; memset(&msg, 0, sizeof(msg)); @@ -191,16 +254,9 @@ nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name, remove_wait_queue(&idmap->idmap_wq, &wq); down(&idmap->idmap_im_lock); - /* - * XXX Race condition here, with testing for status. Go ahead - * and and do the cace lookup anyway. - */ if (im->im_status & IDMAP_STATUS_SUCCESS) { - ret = 0; *id = im->im_id; - - hashtype |= IDMAP_HASH_TYPE_INSERT; - ret = idmap_cache_lookup(idmap, hashtype, name, &xnamelen, id); + ret = 0; } out: @@ -213,35 +269,31 @@ nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name, /* * ID -> Name */ -int -nfs_idmap_name(struct nfs_server *server, u_int8_t type, uid_t id, - char *name, u_int *namelen) +static int +nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h, + __u32 id, char *name) { struct rpc_pipe_msg msg; - struct idmap *idmap = server->idmap; struct idmap_msg *im; + struct idmap_hashent *he; DECLARE_WAITQUEUE(wq, current); - int ret = -1, hashtype = IDMAP_HASH_TYPE_ID; - u_int len; - - if (idmap == NULL) - return (-1); + int ret = -EIO; + unsigned int len; im = &idmap->idmap_im; - if (*namelen < IDMAP_NAMESZ || *namelen == 0) - return (-1); - down(&idmap->idmap_lock); down(&idmap->idmap_im_lock); - if (idmap_cache_lookup(idmap, hashtype, name, namelen, &id) == 0) { - ret = 0; + he = idmap_lookup_id(h, id); + if (he != 0) { + memcpy(name, he->ih_name, he->ih_namelen); + ret = he->ih_namelen; goto out; } memset(im, 0, sizeof(*im)); - im->im_type = type; + im->im_type = h->h_type; im->im_conv = IDMAP_CONV_IDTONAME; im->im_id = id; @@ -256,9 +308,6 @@ nfs_idmap_name(struct nfs_server *server, u_int8_t type, uid_t id, goto out; } - /* - * XXX add timeouts here - */ set_current_state(TASK_UNINTERRUPTIBLE); up(&idmap->idmap_im_lock); schedule(); @@ -267,23 +316,20 @@ nfs_idmap_name(struct nfs_server *server, u_int8_t type, uid_t id, down(&idmap->idmap_im_lock); if (im->im_status & IDMAP_STATUS_SUCCESS) { - if ((len = validate_ascii(im->im_name, IDMAP_NAMESZ)) == -1) + if ((len = strnlen(im->im_name, IDMAP_NAMESZ)) == 0) goto out; - ret = 0; memcpy(name, im->im_name, len); - *namelen = len; - - hashtype |= IDMAP_HASH_TYPE_INSERT; - ret = idmap_cache_lookup(idmap, hashtype, name, namelen, &id); + ret = len; } out: memset(im, 0, sizeof(*im)); up(&idmap->idmap_im_lock); up(&idmap->idmap_lock); - return (ret); + return ret; } +/* RPC pipefs upcall/downcall routines */ static ssize_t idmap_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg, char *dst, size_t buflen) @@ -310,10 +356,12 @@ static ssize_t idmap_pipe_downcall(struct file *filp, const char *src, size_t mlen) { struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode); - struct nfs_server *server = rpci->private; - struct idmap *idmap = server->idmap; + struct idmap *idmap = (struct idmap *)rpci->private; struct idmap_msg im_in, *im = &idmap->idmap_im; - int match = 0, hashtype, badmsg = 0, namelen_in, namelen; + struct idmap_hashtable *h; + struct idmap_hashent *he = NULL; + int namelen_in; + int ret; if (mlen != sizeof(im_in)) return (-ENOSPC); @@ -323,39 +371,66 @@ idmap_pipe_downcall(struct file *filp, const char *src, size_t mlen) down(&idmap->idmap_im_lock); - namelen_in = validate_ascii(im_in.im_name, IDMAP_NAMESZ); - namelen = validate_ascii(im->im_name, IDMAP_NAMESZ); + ret = mlen; + im->im_status = im_in.im_status; + /* If we got an error, terminate now, and wake up pending upcalls */ + if (!(im_in.im_status & IDMAP_STATUS_SUCCESS)) { + wake_up(&idmap->idmap_wq); + goto out; + } + + /* Sanity checking of strings */ + ret = -EINVAL; + namelen_in = strnlen(im_in.im_name, IDMAP_NAMESZ); + if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) + goto out; - badmsg = !(im_in.im_status & IDMAP_STATUS_SUCCESS) || namelen_in <= 0; + switch (im_in.im_type) { + case IDMAP_TYPE_USER: + h = &idmap->idmap_user_hash; + break; + case IDMAP_TYPE_GROUP: + h = &idmap->idmap_group_hash; + break; + default: + goto out; + } switch (im_in.im_conv) { case IDMAP_CONV_IDTONAME: - match = im->im_id == im_in.im_id; + /* Did we match the current upcall? */ + if (im->im_conv == IDMAP_CONV_IDTONAME + && im->im_type == im_in.im_type + && im->im_id == im_in.im_id) { + /* Yes: copy string, including the terminating '\0' */ + memcpy(im->im_name, im_in.im_name, namelen_in); + im->im_name[namelen_in] = '\0'; + wake_up(&idmap->idmap_wq); + } + he = idmap_alloc_id(h, im_in.im_id); break; case IDMAP_CONV_NAMETOID: - match = namelen == namelen_in && - memcmp(im->im_name, im_in.im_name, namelen) == 0; + /* Did we match the current upcall? */ + if (im->im_conv == IDMAP_CONV_NAMETOID + && im->im_type == im_in.im_type + && strnlen(im->im_name, IDMAP_NAMESZ) == namelen_in + && memcmp(im->im_name, im_in.im_name, namelen_in) == 0) { + im->im_id = im_in.im_id; + wake_up(&idmap->idmap_wq); + } + he = idmap_alloc_name(h, im_in.im_name, namelen_in); break; default: - badmsg = 1; - break; - } - - match = match && im->im_type == im_in.im_type; - - if (match) { - memcpy(im, &im_in, sizeof(*im)); - wake_up(&idmap->idmap_wq); - } else if (!badmsg) { - hashtype = im_in.im_conv == IDMAP_CONV_IDTONAME ? - IDMAP_HASH_TYPE_ID : IDMAP_HASH_TYPE_NAME; - hashtype |= IDMAP_HASH_TYPE_INSERT; - idmap_cache_lookup(idmap, hashtype, im_in.im_name, &namelen_in, - &im_in.im_id); + goto out; } + /* If the entry is valid, also copy it to the cache */ + if (he != NULL) + idmap_update_entry(he, im_in.im_name, namelen_in, im_in.im_id); + ret = mlen; +out: up(&idmap->idmap_im_lock); - return (mlen); + return ret; } void @@ -372,108 +447,51 @@ idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg) up(&idmap->idmap_im_lock); } -static int -validate_ascii(char *string, u_int32_t len) -{ - int i; - - for (i = 0; i < len; i++) { - if (string[i] == '\0') - break; - - if (string[i] & 0x80) - return (-1); - } - - if (string[i] != '\0') - return (-1); - - return (i); -} - /* * Fowler/Noll/Vo hash * http://www.isthe.com/chongo/tech/comp/fnv/ */ -#define FNV_P_32 ((u_int32_t)0x01000193) /* 16777619 */ -#define FNV_1_32 ((u_int32_t)0x811c9dc5) /* 2166136261 */ +#define FNV_P_32 ((unsigned int)0x01000193) /* 16777619 */ +#define FNV_1_32 ((unsigned int)0x811c9dc5) /* 2166136261 */ -static u_int32_t -fnvhash32(void *buf, u_int32_t buflen) +static unsigned int fnvhash32(const void *buf, size_t buflen) { - u_char *p, *end = (u_char *)buf + buflen; - u_int32_t hash = FNV_1_32; + const unsigned char *p, *end = (const unsigned char *)buf + buflen; + unsigned int hash = FNV_1_32; for (p = buf; p < end; p++) { hash *= FNV_P_32; - hash ^= (u_int32_t)*p; + hash ^= (unsigned int)*p; } return (hash); } -/* - * ->ih_namelen == 0 indicates negative entry - */ -static int -idmap_cache_lookup(struct idmap *idmap, int type, char *name, u_int32_t *namelen, - uid_t *id) +int nfs_map_name_to_uid(struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) { - u_int32_t hash; - struct idmap_hashent *he = NULL; - int insert = type & IDMAP_HASH_TYPE_INSERT; - int ret = -1; - - /* - * XXX technically, this is not needed, since we will always - * hold idmap_im_lock when altering the hash tables. but - * semantically that just hurts. - * - * XXX cache negative responses - */ - down(&idmap->idmap_hash_lock); + struct idmap *idmap = server->idmap; - if (*namelen > IDMAP_NAMESZ || *namelen == 0) - goto out; + return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); +} - if (type & IDMAP_HASH_TYPE_NAME) { - hash = fnvhash32(name, *namelen) % IDMAP_HASH_SZ; - he = &idmap->idmap_name_hash[hash]; - - /* - * Testing he->ih_namelen == *namelen implicitly tests - * namelen != 0, and thus a non-negative entry. - */ - if (!insert && he->ih_namelen == *namelen && - memcmp(he->ih_name, name, *namelen) == 0) { - *id = he->ih_id; - ret = 0; - goto out; - } - } +int nfs_map_group_to_gid(struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +{ + struct idmap *idmap = server->idmap; - if (type & IDMAP_HASH_TYPE_ID) { - hash = fnvhash32(id, sizeof(*id)) % IDMAP_HASH_SZ; - he = &idmap->idmap_id_hash[hash]; + return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); +} - if (!insert && *id == he->ih_id && he->ih_namelen != 0 && - *namelen >= he->ih_namelen) { - memcpy(name, he->ih_name, he->ih_namelen); - *namelen = he->ih_namelen; - ret = 0; - goto out; - } - } +int nfs_map_uid_to_name(struct nfs_server *server, __u32 uid, char *buf) +{ + struct idmap *idmap = server->idmap; - if (insert && he != NULL) { - he->ih_id = *id; - memcpy(he->ih_name, name, *namelen); - he->ih_namelen = *namelen; - ret = 0; - } + return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); +} +int nfs_map_gid_to_group(struct nfs_server *server, __u32 uid, char *buf) +{ + struct idmap *idmap = server->idmap; - out: - up(&idmap->idmap_hash_lock); - return (ret); + return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); } + diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index f2a909d75468..35a28e9e7d21 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -239,10 +239,10 @@ static int encode_attrs(struct xdr_stream *xdr, struct iattr *iap, struct nfs_server *server) { - char owner_name[256]; - char owner_group[256]; - int owner_namelen = sizeof(owner_name); - int owner_grouplen = sizeof(owner_group); + char owner_name[IDMAP_NAMESZ]; + char owner_group[IDMAP_NAMESZ]; + int owner_namelen = 0; + int owner_grouplen = 0; uint32_t *p; uint32_t *q; int len; @@ -265,9 +265,8 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap, if (iap->ia_valid & ATTR_MODE) len += 4; if (iap->ia_valid & ATTR_UID) { - status = nfs_idmap_name(server, IDMAP_TYPE_USER, - iap->ia_uid, owner_name, &owner_namelen); - if (status < 0) { + owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name); + if (owner_namelen < 0) { printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n", iap->ia_uid); /* XXX */ @@ -278,9 +277,8 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap, len += 4 + (XDR_QUADLEN(owner_namelen) << 2); } if (iap->ia_valid & ATTR_GID) { - status = nfs_idmap_name(server, IDMAP_TYPE_GROUP, - iap->ia_gid, owner_group, &owner_grouplen); - if (status < 0) { + owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group); + if (owner_grouplen < 0) { printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n", iap->ia_gid); strcpy(owner_group, "nobody"); @@ -1475,10 +1473,9 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, } READ_BUF(dummy32); len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_idmap_id(server, IDMAP_TYPE_USER, - (char *)p, dummy32, &nfp->uid)) == -1) { - dprintk("read_attrs: gss_get_num failed!\n"); - /* goto out; */ + if ((status = nfs_map_name_to_uid(server, (char *)p, dummy32, + &nfp->uid)) < 0) { + dprintk("read_attrs: name-to-uid mapping failed!\n"); nfp->uid = -2; } dprintk("read_attrs: uid=%d\n", (int)nfp->uid); @@ -1493,11 +1490,10 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, } READ_BUF(dummy32); len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_idmap_id(server, IDMAP_TYPE_GROUP, - (char *)p, dummy32, &nfp->gid)) == -1) { - dprintk("read_attrs: gss_get_num failed!\n"); + if ((status = nfs_map_group_to_gid(server, (char *)p, dummy32, + &nfp->gid)) < 0) { + dprintk("read_attrs: group-to-gid mapping failed!\n"); nfp->gid = -2; - /* goto out; */ } dprintk("read_attrs: gid=%d\n", (int)nfp->gid); } diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 248adf707071..c95076e5941b 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -52,18 +52,21 @@ #define IDMAP_STATUS_SUCCESS 0x08 struct idmap_msg { - u_int8_t im_type; - u_int8_t im_conv; - char im_name[IDMAP_NAMESZ]; - u_int32_t im_id; - u_int8_t im_status; + __u8 im_type; + __u8 im_conv; + char im_name[IDMAP_NAMESZ]; + __u32 im_id; + __u8 im_status; }; #ifdef __KERNEL__ void *nfs_idmap_new(struct nfs_server *); void nfs_idmap_delete(struct nfs_server *); -int nfs_idmap_id(struct nfs_server *, u_int8_t, char *, u_int, uid_t *); -int nfs_idmap_name(struct nfs_server *, u_int8_t, uid_t, char *, u_int *); + +int nfs_map_name_to_uid(struct nfs_server *, const char *, size_t, __u32 *); +int nfs_map_group_to_gid(struct nfs_server *, const char *, size_t, __u32 *); +int nfs_map_uid_to_name(struct nfs_server *, __u32, char *); +int nfs_map_gid_to_group(struct nfs_server *, __u32, char *); #endif /* __KERNEL__ */ #endif /* NFS_IDMAP_H */ -- cgit v1.2.3 From 3f13d9aac2492d1f1c543453785f25bff0255aab Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:34:13 +0100 Subject: RPCSEC_GSS: Miscellaneous cleanups of the krb5 code required for the integrity checksumming mode. --- include/linux/sunrpc/gss_krb5.h | 20 +---- net/sunrpc/auth_gss/gss_krb5_crypto.c | 44 +++++------ net/sunrpc/auth_gss/gss_krb5_mech.c | 28 +++---- net/sunrpc/auth_gss/gss_krb5_seal.c | 43 +++-------- net/sunrpc/auth_gss/gss_krb5_unseal.c | 134 +++++++++------------------------- 5 files changed, 84 insertions(+), 185 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index 8db6d1e13a69..aac2ad4f7d56 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h @@ -50,7 +50,6 @@ struct krb5_ctx { struct crypto_tfm *seq; s32 endtime; u32 seq_send; - u32 seq_recv; struct xdr_netobj mech_used; }; @@ -73,7 +72,7 @@ enum seal_alg { SEAL_ALG_DES3KD = 0x0002 }; -#define RSA_MD5_CKSUM_LENGTH 16 +#define KRB5_CKSUM_LENGTH 8 #define CKSUMTYPE_CRC32 0x0001 #define CKSUMTYPE_RSA_MD4 0x0002 @@ -100,16 +99,6 @@ enum seal_alg { #define KG_EMPTY_CCACHE (39756044L) #define KG_NO_CTYPES (39756045L) -#define KV5M_PRINCIPAL (-1760647423L) -#define KV5M_KEYBLOCK (-1760647421L) -#define KV5M_CHECKSUM (-1760647420L) -#define KV5M_ADDRESS (-1760647390L) -#define KV5M_AUTHENTICATOR (-1760647410L) -#define KV5M_AUTH_CONTEXT (-1760647383L) -#define KV5M_AUTHDATA (-1760647414L) -#define KV5M_GSS_OID (-1760647372L) -#define KV5M_GSS_QUEUE (-1760647371L) - /* per Kerberos v5 protocol spec crypto types from the wire. * these get mapped to linux kernel crypto routines. */ @@ -126,14 +115,13 @@ enum seal_alg { #define ENCTYPE_UNKNOWN 0x01ff s32 -krb5_make_checksum(s32 cksumtype, - struct xdr_netobj *input, +krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, struct xdr_netobj *cksum); u32 krb5_make_token(struct krb5_ctx *context_handle, int qop_req, - struct xdr_netobj * input_message_buffer, - struct xdr_netobj * output_message_buffer, int toktype); + struct xdr_netobj *input_message_buffer, + struct xdr_netobj *output_message_buffer, int toktype); u32 krb5_read_token(struct krb5_ctx *context_handle, diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index c3a8f548a482..9894d83ddf6f 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -122,14 +122,23 @@ out: return(ret); } +void +buf_to_sg(struct scatterlist *sg, char *ptr, int len) { + sg->page = virt_to_page(ptr); + sg->offset = offset_in_page(ptr); + sg->length = len; +} + +/* checksum the plaintext data and the first 8 bytes of the krb5 token header, + * as specified by the rfc: */ s32 -krb5_make_checksum(s32 cksumtype, struct xdr_netobj *input, +krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, struct xdr_netobj *cksum) { - s32 ret = -EINVAL; - struct scatterlist sg[1]; - char *cksumname; - struct crypto_tfm *tfm; + char *cksumname; + struct crypto_tfm *tfm = NULL; /* XXX add to ctx? */ + struct scatterlist sg[2]; + u32 code = GSS_S_FAILURE; switch (cksumtype) { case CKSUMTYPE_RSA_MD5: @@ -143,24 +152,17 @@ krb5_make_checksum(s32 cksumtype, struct xdr_netobj *input, if (!(tfm = crypto_alloc_tfm(cksumname, 0))) goto out; cksum->len = crypto_tfm_alg_digestsize(tfm); + if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL) + goto out; - if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL) { - ret = -ENOMEM; - goto out_free_tfm; - } - sg[0].page = virt_to_page(input->data); - sg[0].offset = offset_in_page(input->data); - sg[0].length = input->len; - + buf_to_sg(&sg[0], header, 8); + buf_to_sg(&sg[1], body, body_len); crypto_digest_init(tfm); - crypto_digest_update(tfm, sg, 1); + crypto_digest_update(tfm, sg, 2); crypto_digest_final(tfm, cksum->data); - - ret = 0; - -out_free_tfm: - crypto_free_tfm(tfm); + code = 0; out: - dprintk("RPC: gss_k5cksum: returning %d\n", ret); - return (ret); + if (tfm) + crypto_free_tfm(tfm); + return code; } diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index ce31a89684e4..61282b4d9c3b 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -98,7 +98,7 @@ get_key(char **p, char *end, struct crypto_tfm **res) alg_mode = CRYPTO_TFM_MODE_CBC; break; default: - dprintk("RPC: get_key: unsupported algorithm %d", alg); + dprintk("RPC: get_key: unsupported algorithm %d\n", alg); goto out_err_free_key; } if (!(*res = crypto_alloc_tfm(alg_name, alg_mode))) @@ -168,7 +168,7 @@ out_err: return GSS_S_FAILURE; } -void +static void gss_delete_sec_context_kerberos(void *internal_ctx) { struct krb5_ctx *kctx = internal_ctx; @@ -181,16 +181,16 @@ gss_delete_sec_context_kerberos(void *internal_ctx) { kfree(kctx); } -u32 +static u32 gss_verify_mic_kerberos(struct gss_ctx *ctx, - struct xdr_netobj *signbuf, - struct xdr_netobj *checksum, - u32 *qstate) { + struct xdr_netobj *message, + struct xdr_netobj *mic_token, + u32 *qstate) { u32 maj_stat = 0; int qop_state; struct krb5_ctx *kctx = ctx->internal_ctx_id; - maj_stat = krb5_read_token(kctx, checksum, signbuf, &qop_state, + maj_stat = krb5_read_token(kctx, mic_token, message, &qop_state, KG_TOK_MIC_MSG); if (!maj_stat && qop_state) *qstate = qop_state; @@ -199,21 +199,17 @@ gss_verify_mic_kerberos(struct gss_ctx *ctx, return maj_stat; } -u32 +static u32 gss_get_mic_kerberos(struct gss_ctx *ctx, u32 qop, - struct xdr_netobj *message_buffer, - struct xdr_netobj *message_token) { + struct xdr_netobj *message, + struct xdr_netobj *mic_token) { u32 err = 0; struct krb5_ctx *kctx = ctx->internal_ctx_id; - if (!message_buffer->data) return GSS_S_FAILURE; - - dprintk("RPC: gss_get_mic_kerberos:" - " message_buffer->len %d\n",message_buffer->len); + if (!message->data) return GSS_S_FAILURE; - err = krb5_make_token(kctx, qop, message_buffer, - message_token, KG_TOK_MIC_MSG); + err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG); dprintk("RPC: gss_get_mic_kerberos returning %d\n",err); diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index 280d82d7c6dc..e664d3ea98ce 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -63,14 +63,13 @@ #include #include #include +#include #include #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH #endif -#define CKSUM_SIZE 8 - static inline int gss_krb5_padding(int blocksize, int length) { /* Most of the code is block-size independent but in practice we @@ -79,29 +78,6 @@ gss_krb5_padding(int blocksize, int length) { return 8 - (length & 7); } -/* checksum the plaintext data and the first 8 bytes of the krb5 token header, - * as specified by the rfc: */ -static u32 -compute_checksum(s32 checksum_type, char *header, char *body, int body_len, - struct xdr_netobj *md5cksum) { - char *data_ptr; - struct xdr_netobj plaind; - u32 code = GSS_S_FAILURE; - - if (!(data_ptr = kmalloc(8 + body_len, GFP_KERNEL))) - goto out; - memcpy(data_ptr, header, 8); - memcpy(data_ptr + 8, body, body_len); - plaind.len = 8 + body_len; - plaind.data = data_ptr; - code = krb5_make_checksum(checksum_type, &plaind, md5cksum); - kfree(data_ptr); - code = 0; - -out: - return code; -} - u32 krb5_make_token(struct krb5_ctx *ctx, int qop_req, struct xdr_netobj * text, struct xdr_netobj * token, @@ -113,10 +89,12 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, unsigned char *ptr, *krb5_hdr, *msg_start; s32 now; - dprintk("RPC: gss_krb5_seal"); + dprintk("RPC: gss_krb5_seal\n"); now = jiffies; + token->data = NULL; + if (qop_req != 0) goto out_err; @@ -167,8 +145,8 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, memset(msg_start + blocksize + text->len, pad, pad); - if (compute_checksum(checksum_type, krb5_hdr, msg_start, - tmsglen, &md5cksum)) + if (krb5_make_checksum(checksum_type, krb5_hdr, msg_start, + tmsglen, &md5cksum)) goto out_err; if (krb5_encrypt(ctx->enc, NULL, msg_start, msg_start, @@ -176,8 +154,8 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, goto out_err; } else { /* Sign only. */ - if (compute_checksum(checksum_type, krb5_hdr, text->data, - text->len, &md5cksum)) + if (krb5_make_checksum(checksum_type, krb5_hdr, text->data, + text->len, &md5cksum)) goto out_err; } @@ -187,10 +165,11 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, md5cksum.data, md5cksum.len)) goto out_err; memcpy(krb5_hdr + 16, - md5cksum.data + md5cksum.len - CKSUM_SIZE, CKSUM_SIZE); + md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH, + KRB5_CKSUM_LENGTH); dprintk("make_seal_token: cksum data: \n"); - print_hexl((u32 *) (krb5_hdr + 16), CKSUM_SIZE, 0); + print_hexl((u32 *) (krb5_hdr + 16), KRB5_CKSUM_LENGTH, 0); break; default: BUG(); diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index 836c683777f2..8b2795d701db 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -68,7 +68,12 @@ #endif -/* message_buffer is an input if MIC and an output if WRAP. */ +/* message_buffer is an input if toktype is MIC and an output if it is WRAP: + * If toktype is MIC: read_token is a mic token, and message_buffer is the + * data that the mic was supposedly taken over. + * If toktype is WRAP: read_token is a wrap token, and message_buffer is used + * to return the decrypted data. + */ u32 krb5_read_token(struct krb5_ctx *ctx, @@ -76,20 +81,13 @@ krb5_read_token(struct krb5_ctx *ctx, struct xdr_netobj *message_buffer, int *qop_state, int toktype) { - s32 code; - int tmsglen = 0; - int conflen = 0; int signalg; int sealalg; struct xdr_netobj token = {.len = 0, .data = NULL}; s32 checksum_type; - struct xdr_netobj cksum; struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; - struct xdr_netobj plaind; - char *data_ptr; s32 now; unsigned char *plain = NULL; - int cksum_len = 0; int plainlen = 0; int direction; s32 seqnum; @@ -97,10 +95,9 @@ krb5_read_token(struct krb5_ctx *ctx, int bodysize; u32 ret = GSS_S_DEFECTIVE_TOKEN; - dprintk("RPC: krb5_read_token\n"); + dprintk("RPC: krb5_read_token\n"); - if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used, - &bodysize, &ptr, toktype, + if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype, read_token->len)) goto out; @@ -138,40 +135,22 @@ krb5_read_token(struct krb5_ctx *ctx, signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) goto out; - /* starting with a single alg */ - switch (signalg) { - case SGN_ALG_DES_MAC_MD5: - cksum_len = 8; - break; - default: - goto out; - } - - if (toktype == KG_TOK_WRAP_MSG) - tmsglen = bodysize - (14 + cksum_len); - - /* get the token parameters */ - - /* decode the message, if WRAP */ - if (toktype == KG_TOK_WRAP_MSG) { - dprintk("RPC: krb5_read_token KG_TOK_WRAP_MSG\n"); + int conflen = crypto_tfm_alg_blocksize(ctx->enc); + int padlen; - plain = kmalloc(tmsglen, GFP_KERNEL); - ret = GSS_S_FAILURE; - if (plain == NULL) - goto out; + plainlen = bodysize - (14 + KRB5_CKSUM_LENGTH); + plain = ptr + 14 + KRB5_CKSUM_LENGTH; - code = krb5_decrypt(ctx->enc, NULL, - ptr + 14 + cksum_len, plain, - tmsglen); - if (code) + ret = krb5_decrypt(ctx->enc, NULL, plain, plain, plainlen); + if (ret) goto out; - plainlen = tmsglen; - - conflen = crypto_tfm_alg_blocksize(ctx->enc); - token.len = tmsglen - conflen - plain[tmsglen - 1]; + ret = GSS_S_FAILURE; + padlen = plain[plainlen -1]; + if ((padlen < 1) || (padlen > 8)) + goto out; + token.len = plainlen - conflen - padlen; if (token.len) { token.data = kmalloc(token.len, GFP_KERNEL); @@ -181,15 +160,13 @@ krb5_read_token(struct krb5_ctx *ctx, } } else if (toktype == KG_TOK_MIC_MSG) { - dprintk("RPC: krb5_read_token KG_TOK_MIC_MSG\n"); token = *message_buffer; plain = token.data; plainlen = token.len; } else { - token.len = 0; - token.data = NULL; - plain = token.data; - plainlen = token.len; + printk("RPC: bad toktype in krb5_read_token"); + ret = GSS_S_FAILURE; + goto out; } dprintk("RPC krb5_read_token: token.len %d plainlen %d\n", token.len, @@ -209,67 +186,26 @@ krb5_read_token(struct krb5_ctx *ctx, switch (signalg) { case SGN_ALG_DES_MAC_MD5: - dprintk("RPC krb5_read_token SGN_ALG_DES_MAC_MD5\n"); - /* compute the checksum of the message. - * 8 = bytes of token body to be checksummed according to spec - */ - - data_ptr = kmalloc(8 + plainlen, GFP_KERNEL); - ret = GSS_S_FAILURE; - if (!data_ptr) + ret = krb5_make_checksum(checksum_type, ptr - 2, plain, + plainlen, &md5cksum); + if (ret) goto out; - memcpy(data_ptr, ptr - 2, 8); - memcpy(data_ptr + 8, plain, plainlen); - - plaind.len = 8 + plainlen; - plaind.data = data_ptr; - - code = krb5_make_checksum(checksum_type, - &plaind, &md5cksum); - - kfree(data_ptr); - - if (code) + ret = krb5_encrypt(ctx->seq, NULL, md5cksum.data, + md5cksum.data, 16); + if (ret) goto out; - code = krb5_encrypt(ctx->seq, NULL, md5cksum.data, - md5cksum.data, 16); - if (code) + if (memcmp(md5cksum.data + 8, ptr + 14, 8)) { + ret = GSS_S_BAD_SIG; goto out; - - if (signalg == 0) - cksum.len = 8; - else - cksum.len = 16; - cksum.data = md5cksum.data + 16 - cksum.len; - - dprintk - ("RPC: krb5_read_token: memcmp digest cksum.len %d:\n", - cksum.len); - dprintk(" md5cksum.data\n"); - print_hexl((u32 *) md5cksum.data, 16, 0); - dprintk(" cksum.data:\n"); - print_hexl((u32 *) cksum.data, cksum.len, 0); - { - u32 *p; - - (u8 *) p = ptr + 14; - dprintk(" ptr+14:\n"); - print_hexl(p, cksum.len, 0); } - - code = memcmp(cksum.data, ptr + 14, cksum.len); break; default: ret = GSS_S_DEFECTIVE_TOKEN; goto out; } - ret = GSS_S_BAD_SIG; - if (code) - goto out; - /* it got through unscathed. Make sure the context is unexpired */ if (toktype == KG_TOK_WRAP_MSG) @@ -287,8 +223,8 @@ krb5_read_token(struct krb5_ctx *ctx, /* do sequencing checks */ ret = GSS_S_BAD_SIG; - if ((code = krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, - &seqnum))) + if ((ret = krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, + &seqnum))) goto out; if ((ctx->initiate && direction != 0xff) || @@ -298,9 +234,7 @@ krb5_read_token(struct krb5_ctx *ctx, ret = GSS_S_COMPLETE; out: if (md5cksum.data) kfree(md5cksum.data); - if (toktype == KG_TOK_WRAP_MSG) { - if (plain) kfree(plain); - if (ret && token.data) kfree(token.data); - } + if ((toktype == KG_TOK_WRAP_MSG) && ret && token.data) + kfree(token.data); return ret; } -- cgit v1.2.3 From 955ac3514fc5eb754827bd69edbe09012b95beb8 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:44:01 +0100 Subject: RPCSEC_GSS: Client-side only support for rpcsec_gss integrity protection. Since this requires checksumming an entire request, instead of just the header, and since the request may include, for example, pages with write data, we modify the gss_api routines to pass xdr_bufs instead of xdr_netobjs where necessary. We add rpcauth_wrap_req and rpcauth_unwrap_resp to rpcauth.c, wrappers for the new rpc cred ops crwrap_req and crunwrap_req, which are called just before encoding, and just after decoding, respectively. --- include/linux/sunrpc/auth.h | 6 + include/linux/sunrpc/gss_api.h | 9 +- include/linux/sunrpc/gss_krb5.h | 6 +- include/linux/sunrpc/xdr.h | 4 + net/sunrpc/auth.c | 29 +++++ net/sunrpc/auth_gss/auth_gss.c | 231 ++++++++++++++++++++++++++++------ net/sunrpc/auth_gss/gss_krb5_crypto.c | 51 ++++++-- net/sunrpc/auth_gss/gss_krb5_mech.c | 8 +- net/sunrpc/auth_gss/gss_krb5_seal.c | 23 +--- net/sunrpc/auth_gss/gss_krb5_unseal.c | 58 ++------- net/sunrpc/auth_gss/gss_mech_switch.c | 4 +- net/sunrpc/clnt.c | 6 +- net/sunrpc/sunrpc_syms.c | 3 + net/sunrpc/xdr.c | 144 ++++++++++++++++++++- 14 files changed, 450 insertions(+), 132 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 70cc3360e608..a68f18bf0a46 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -102,6 +102,10 @@ struct rpc_credops { u32 * (*crmarshal)(struct rpc_task *, u32 *, int); int (*crrefresh)(struct rpc_task *); u32 * (*crvalidate)(struct rpc_task *, u32 *); + int (*crwrap_req)(struct rpc_task *, kxdrproc_t, + void *, u32 *, void *); + int (*crunwrap_resp)(struct rpc_task *, kxdrproc_t, + void *, u32 *, void *); }; extern struct rpc_authops authunix_ops; @@ -124,6 +128,8 @@ void put_rpccred(struct rpc_cred *); void rpcauth_unbindcred(struct rpc_task *); u32 * rpcauth_marshcred(struct rpc_task *, u32 *); u32 * rpcauth_checkverf(struct rpc_task *, u32 *); +int rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, u32 *data, void *obj); +int rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, u32 *data, void *obj); int rpcauth_refreshcred(struct rpc_task *); void rpcauth_invalcred(struct rpc_task *); int rpcauth_uptodatecred(struct rpc_task *); diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h index 35988e7bfb77..cbb60ac22fd4 100644 --- a/include/linux/sunrpc/gss_api.h +++ b/include/linux/sunrpc/gss_api.h @@ -16,6 +16,7 @@ #ifdef __KERNEL__ #include +#include /* The mechanism-independent gss-api context: */ struct gss_ctx { @@ -39,11 +40,11 @@ u32 gss_import_sec_context( u32 gss_get_mic( struct gss_ctx *ctx_id, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token); u32 gss_verify_mic( struct gss_ctx *ctx_id, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate); u32 gss_delete_sec_context( @@ -95,11 +96,11 @@ struct gss_api_ops { u32 (*gss_get_mic)( struct gss_ctx *ctx_id, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token); u32 (*gss_verify_mic)( struct gss_ctx *ctx_id, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate); void (*gss_delete_sec_context)( diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index aac2ad4f7d56..9616746407f3 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h @@ -115,18 +115,18 @@ enum seal_alg { #define ENCTYPE_UNKNOWN 0x01ff s32 -krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, +krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body, struct xdr_netobj *cksum); u32 krb5_make_token(struct krb5_ctx *context_handle, int qop_req, - struct xdr_netobj *input_message_buffer, + struct xdr_buf *input_message_buffer, struct xdr_netobj *output_message_buffer, int toktype); u32 krb5_read_token(struct krb5_ctx *context_handle, struct xdr_netobj *input_token_buffer, - struct xdr_netobj *message_buffer, + struct xdr_buf *message_buffer, int *qop_state, int toktype); u32 diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 2c6f76d1cc14..8082a0029100 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -141,6 +141,10 @@ void xdr_shift_iovec(struct iovec *, int, size_t); extern int xdr_kmap(struct iovec *, struct xdr_buf *, size_t); extern void xdr_kunmap(struct xdr_buf *, size_t); extern void xdr_shift_buf(struct xdr_buf *, size_t); +extern void _copy_from_pages(char *, struct page **, size_t, size_t); +extern void xdr_buf_from_iov(struct iovec *, struct xdr_buf *); +extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int); +extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int); /* * Helper structure for copying from an sk_buff. diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index f1a73646e6ed..a89b30cadff3 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -339,6 +339,35 @@ rpcauth_checkverf(struct rpc_task *task, u32 *p) return cred->cr_ops->crvalidate(task, p); } +int +rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, + u32 *data, void *obj) +{ + struct rpc_cred *cred = task->tk_msg.rpc_cred; + + dprintk("RPC: %4d using %s cred %p to wrap rpc data\n", + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); + if (cred->cr_ops->crwrap_req) + return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); + /* By default, we encode the arguments normally. */ + return encode(rqstp, data, obj); +} + +int +rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, + u32 *data, void *obj) +{ + struct rpc_cred *cred = task->tk_msg.rpc_cred; + + dprintk("RPC: %4d using %s cred %p to unwrap rpc data\n", + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); + if (cred->cr_ops->crunwrap_resp) + return cred->cr_ops->crunwrap_resp(task, decode, rqstp, + data, obj); + /* By default, we decode the arguments normally. */ + return decode(rqstp, data, obj); +} + int rpcauth_refreshcred(struct rpc_task *task) { diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 61618c6bed5e..1a4b9f504aa2 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -51,6 +51,7 @@ #include #include #include +#include #include static struct rpc_authops authgss_ops; @@ -65,7 +66,9 @@ static struct rpc_credops gss_credops; #define GSS_CRED_EXPIRE (60 * HZ) /* XXX: reasonable? */ #define GSS_CRED_SLACK 1024 /* XXX: unused */ -#define GSS_VERF_SLACK 48 /* length of a krb5 verifier.*/ +/* length of a krb5 verifier (48), plus data added before arguments when + * using integrity (two 4-byte integers): */ +#define GSS_VERF_SLACK 56 /* XXX this define must match the gssd define * as it is passed to gssd to signal the use of @@ -669,21 +672,14 @@ gss_marshal(struct rpc_task *task, u32 *p, int ruid) struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); u32 *cred_len; struct rpc_rqst *req = task->tk_rqstp; - struct rpc_clnt *clnt = task->tk_client; - struct rpc_xprt *xprt = clnt->cl_xprt; - u32 *verfbase = req->rq_svec[0].iov_base; u32 maj_stat = 0; - struct xdr_netobj bufin,bufout; + struct xdr_netobj mic; + struct iovec iov; + struct xdr_buf verf_buf; u32 service; dprintk("RPC: gss_marshal\n"); - /* We compute the checksum for the verifier over the xdr-encoded bytes - * starting with the xid (which verfbase points to) and ending at - * the end of the credential. */ - if (xprt->stream) - verfbase++; /* See clnt.c:call_header() */ - *p++ = htonl(RPC_AUTH_GSS); cred_len = p++; @@ -704,24 +700,28 @@ gss_marshal(struct rpc_task *task, u32 *p, int ruid) p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); *cred_len = htonl((p - (cred_len + 1)) << 2); - /* Marshal verifier. */ - bufin.data = (u8 *)verfbase; - bufin.len = (p - verfbase) << 2; + /* We compute the checksum for the verifier over the xdr-encoded bytes + * starting with the xid and ending at the end of the credential: */ + iov.iov_base = req->rq_snd_buf.head[0].iov_base; + if (task->tk_client->cl_xprt->stream) + /* See clnt.c:call_header() */ + iov.iov_base += 4; + iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; + xdr_buf_from_iov(&iov, &verf_buf); /* set verifier flavor*/ *p++ = htonl(RPC_AUTH_GSS); - bufout.data = (u8 *)(p + 1); + mic.data = (u8 *)(p + 1); maj_stat = gss_get_mic(ctx->gc_gss_ctx, GSS_C_QOP_DEFAULT, - &bufin, &bufout); + &verf_buf, &mic); if(maj_stat != 0){ - printk("gss_marshal: gss_get_mic FAILED (%d)\n", - maj_stat); + printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); goto out_put_ctx; } - *p++ = htonl(bufout.len); - p += XDR_QUADLEN(bufout.len); + *p++ = htonl(mic.len); + p += XDR_QUADLEN(mic.len); gss_put_ctx(ctx); return p; out_put_ctx: @@ -749,35 +749,45 @@ static u32 * gss_validate(struct rpc_task *task, u32 *p) { struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); u32 seq, qop_state; - struct xdr_netobj bufin; - struct xdr_netobj bufout; + struct iovec iov; + struct xdr_buf verf_buf; + struct xdr_netobj mic; u32 flav,len; + u32 service; dprintk("RPC: gss_validate\n"); flav = ntohl(*p++); - if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) { - printk("RPC: giant verf size: %ld\n", (unsigned long) len); + if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) goto out_bad; - } - dprintk("RPC: gss_validate: verifier flavor %d, len %d\n", flav, len); - - if (flav != RPC_AUTH_GSS) { - printk("RPC: bad verf flavor: %ld\n", (unsigned long)flav); + if (flav != RPC_AUTH_GSS) goto out_bad; - } seq = htonl(task->tk_gss_seqno); - bufin.data = (u8 *) &seq; - bufin.len = sizeof(seq); - bufout.data = (u8 *) p; - bufout.len = len; - - if (gss_verify_mic(ctx->gc_gss_ctx, &bufin, &bufout, &qop_state) != 0) - goto out_bad; - task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2; - dprintk("RPC: GSS gss_validate: gss_verify_mic succeeded.\n"); + iov.iov_base = &seq; + iov.iov_len = sizeof(seq); + xdr_buf_from_iov(&iov, &verf_buf); + mic.data = (u8 *)p; + mic.len = len; + + if (gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state)) + goto out_bad; + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + /* verifier data, flavor, length: */ + task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2; + break; + case RPC_GSS_SVC_INTEGRITY: + /* verifier data, flavor, length, length, sequence number: */ + task->tk_auth->au_rslack = XDR_QUADLEN(len) + 4; + break; + default: + goto out_bad; + } gss_put_ctx(ctx); return p + XDR_QUADLEN(len); out_bad: @@ -785,6 +795,147 @@ out_bad: return NULL; } +static int +gss_wrap_req(struct rpc_task *task, + kxdrproc_t encode, void *rqstp, u32 *p, void *obj) +{ + struct rpc_rqst *req = (struct rpc_rqst *)rqstp; + struct xdr_buf *snd_buf = &req->rq_snd_buf; + struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); + struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); + u32 *integ_len = NULL; + int status = -EIO; + u32 maj_stat = 0; + struct xdr_buf integ_buf; + struct xdr_netobj mic; + u32 service; + u32 offset, *q; + struct iovec *iov; + + dprintk("RPC: gss_wrap_body\n"); + BUG_ON(!ctx); + if (ctx->gc_proc != RPC_GSS_PROC_DATA) { + /* The spec seems a little ambiguous here, but I think that not + * wrapping context destruction requests makes the most sense. + */ + status = encode(rqstp, p, obj); + goto out; + } + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + status = encode(rqstp, p, obj); + goto out; + case RPC_GSS_SVC_INTEGRITY: + + integ_len = p++; + offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; + *p++ = htonl(task->tk_gss_seqno); + + status = encode(rqstp, p, obj); + if (status) + goto out; + + if (xdr_buf_subsegment(snd_buf, &integ_buf, + offset, snd_buf->len - offset)) + goto out; + *integ_len = htonl(integ_buf.len); + + /* guess whether we're in the head or the tail: */ + if (snd_buf->page_len || snd_buf->tail[0].iov_len) + iov = snd_buf->tail; + else + iov = snd_buf->head; + p = iov->iov_base + iov->iov_len; + mic.data = (u8 *)(p + 1); + + maj_stat = gss_get_mic(ctx->gc_gss_ctx, + GSS_C_QOP_DEFAULT, &integ_buf, &mic); + status = -EIO; /* XXX? */ + if (maj_stat) + goto out; + q = p; + *q++ = htonl(mic.len); + q += XDR_QUADLEN(mic.len); + + offset = (u8 *)q - (u8 *)p; + iov->iov_len += offset; + snd_buf->len += offset; + break; + case RPC_GSS_SVC_PRIVACY: + default: + goto out; + } + status = 0; +out: + gss_put_ctx(ctx); + dprintk("RPC: gss_wrap_req returning %d\n", status); + return status; +} + +static int +gss_unwrap_resp(struct rpc_task *task, + kxdrproc_t decode, void *rqstp, u32 *p, void *obj) +{ + struct rpc_rqst *req = (struct rpc_rqst *)rqstp; + struct xdr_buf *rcv_buf = &req->rq_rcv_buf; + struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); + struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); + struct xdr_buf integ_buf; + struct xdr_netobj mic; + int status = -EIO; + u32 maj_stat = 0; + u32 service; + u32 data_offset, mic_offset; + u32 integ_len; + + BUG_ON(!ctx); + + if (ctx->gc_proc != RPC_GSS_PROC_DATA) + goto out_decode; + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + goto out_decode; + case RPC_GSS_SVC_INTEGRITY: + integ_len = ntohl(*p++); + if (integ_len & 3) + goto out; + data_offset = (u8 *)p - (u8 *)rcv_buf->head[0].iov_base; + mic_offset = integ_len + data_offset; + if (mic_offset > rcv_buf->len) + goto out; + if (ntohl(*p++) != task->tk_gss_seqno) + goto out; + + if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, + mic_offset - data_offset)) + goto out; + + if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset)) + goto out; + + maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, + &mic, NULL); + if (maj_stat != GSS_S_COMPLETE) + goto out; + break; + case RPC_GSS_SVC_PRIVACY: + default: + goto out; + } +out_decode: + status = decode(rqstp, p, obj); +out: + gss_put_ctx(ctx); + dprintk("RPC: gss_unwrap_resp returning %d\n", status); + return status; +} + static struct rpc_authops authgss_ops = { .owner = THIS_MODULE, .au_flavor = RPC_AUTH_GSS, @@ -802,6 +953,8 @@ static struct rpc_credops gss_credops = { .crmarshal = gss_marshal, .crrefresh = gss_refresh, .crvalidate = gss_validate, + .crwrap_req = gss_wrap_req, + .crunwrap_resp = gss_unwrap_resp, }; static struct rpc_pipe_ops gss_upcall_ops = { diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index 9894d83ddf6f..5a5f859ad628 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #ifdef RPC_DEBUG @@ -57,7 +58,7 @@ krb5_encrypt( struct scatterlist sg[1]; u8 local_iv[16] = {0}; - dprintk("RPC: gss_k5encrypt: TOP in %p out %p\nin data:\n", out, in); + dprintk("RPC: krb5_encrypt: input data:\n"); print_hexl((u32 *)in, length, 0); if (length % crypto_tfm_alg_blocksize(tfm) != 0) @@ -79,8 +80,10 @@ krb5_encrypt( ret = crypto_cipher_encrypt_iv(tfm, sg, sg, length, local_iv); + dprintk("RPC: krb5_encrypt: output data:\n"); + print_hexl((u32 *)out, length, 0); out: - dprintk("gss_k5encrypt returns %d\n",ret); + dprintk("krb5_encrypt returns %d\n",ret); return(ret); } @@ -96,8 +99,8 @@ krb5_decrypt( struct scatterlist sg[1]; u8 local_iv[16] = {0}; - dprintk("RPC: gss_k5decrypt: TOP in %p out %p\nin data:\n", in, out); - print_hexl((u32 *)in,length,0); + dprintk("RPC: krb5_decrypt: input data:\n"); + print_hexl((u32 *)in, length, 0); if (length % crypto_tfm_alg_blocksize(tfm) != 0) goto out; @@ -117,6 +120,8 @@ krb5_decrypt( ret = crypto_cipher_decrypt_iv(tfm, sg, sg, length, local_iv); + dprintk("RPC: krb5_decrypt: output_data:\n"); + print_hexl((u32 *)out, length, 0); out: dprintk("gss_k5decrypt returns %d\n",ret); return(ret); @@ -132,13 +137,15 @@ buf_to_sg(struct scatterlist *sg, char *ptr, int len) { /* checksum the plaintext data and the first 8 bytes of the krb5 token header, * as specified by the rfc: */ s32 -krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, +krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body, struct xdr_netobj *cksum) { char *cksumname; struct crypto_tfm *tfm = NULL; /* XXX add to ctx? */ - struct scatterlist sg[2]; + struct scatterlist sg[1]; u32 code = GSS_S_FAILURE; + int len, thislen, offset; + int i; switch (cksumtype) { case CKSUMTYPE_RSA_MD5: @@ -155,10 +162,36 @@ krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL) goto out; - buf_to_sg(&sg[0], header, 8); - buf_to_sg(&sg[1], body, body_len); crypto_digest_init(tfm); - crypto_digest_update(tfm, sg, 2); + buf_to_sg(sg, header, 8); + crypto_digest_update(tfm, sg, 1); + if (body->head[0].iov_len) { + buf_to_sg(sg, body->head[0].iov_base, body->head[0].iov_len); + crypto_digest_update(tfm, sg, 1); + } + + len = body->page_len; + offset = body->page_base; + i = 0; + while (len) { + sg->page = body->pages[i]; + sg->offset = offset; + offset = 0; + if (PAGE_SIZE > len) + thislen = len; + else + thislen = PAGE_SIZE; + sg->length = thislen; + kmap(sg->page); /* XXX kmap_atomic? */ + crypto_digest_update(tfm, sg, 1); + kunmap(sg->page); + len -= thislen; + i++; + } + if (body->tail[0].iov_len) { + buf_to_sg(sg, body->tail[0].iov_base, body->tail[0].iov_len); + crypto_digest_update(tfm, sg, 1); + } crypto_digest_final(tfm, cksum->data); code = 0; out: diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 61282b4d9c3b..9913dac0f415 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -183,7 +183,7 @@ gss_delete_sec_context_kerberos(void *internal_ctx) { static u32 gss_verify_mic_kerberos(struct gss_ctx *ctx, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate) { u32 maj_stat = 0; @@ -202,13 +202,11 @@ gss_verify_mic_kerberos(struct gss_ctx *ctx, static u32 gss_get_mic_kerberos(struct gss_ctx *ctx, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token) { u32 err = 0; struct krb5_ctx *kctx = ctx->internal_ctx_id; - if (!message->data) return GSS_S_FAILURE; - err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG); dprintk("RPC: gss_get_mic_kerberos returning %d\n",err); @@ -233,12 +231,14 @@ static int __init init_kerberos_module(void) printk("Failed to register kerberos gss mechanism!\n"); gm = gss_mech_get_by_OID(&gss_mech_krb5_oid); gss_register_triple(RPC_AUTH_GSS_KRB5 , gm, 0, RPC_GSS_SVC_NONE); + gss_register_triple(RPC_AUTH_GSS_KRB5I, gm, 0, RPC_GSS_SVC_INTEGRITY); gss_mech_put(gm); return 0; } static void __exit cleanup_kerberos_module(void) { + gss_unregister_triple(RPC_AUTH_GSS_KRB5I); gss_unregister_triple(RPC_AUTH_GSS_KRB5); } diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index eaf19d7c8e25..52d4e78e117f 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -80,7 +80,7 @@ gss_krb5_padding(int blocksize, int length) { u32 krb5_make_token(struct krb5_ctx *ctx, int qop_req, - struct xdr_netobj * text, struct xdr_netobj * token, + struct xdr_buf *text, struct xdr_netobj *token, int toktype) { s32 checksum_type; @@ -134,24 +134,11 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, *(u16 *)(krb5_hdr + 4) = htons(ctx->sealalg); if (toktype == KG_TOK_WRAP_MSG) { - unsigned char pad = gss_krb5_padding(blocksize, text->len); - - get_random_bytes(msg_start, blocksize); /* "confounder" */ - memcpy(msg_start + blocksize, text->data, text->len); - - memset(msg_start + blocksize + text->len, pad, pad); - - if (krb5_make_checksum(checksum_type, krb5_hdr, msg_start, - tmsglen, &md5cksum)) - goto out_err; - - if (krb5_encrypt(ctx->enc, NULL, msg_start, msg_start, - tmsglen)) - goto out_err; - + /* XXX removing support for now */ + goto out_err; } else { /* Sign only. */ - if (krb5_make_checksum(checksum_type, krb5_hdr, text->data, - text->len, &md5cksum)) + if (krb5_make_checksum(checksum_type, krb5_hdr, text, + &md5cksum)) goto out_err; } diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index 8b2795d701db..0e1c7f70f841 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -75,20 +75,19 @@ * to return the decrypted data. */ +/* XXX will need to change prototype and/or just split into a separate function + * when we add privacy (because read_token will be in pages too). */ u32 krb5_read_token(struct krb5_ctx *ctx, struct xdr_netobj *read_token, - struct xdr_netobj *message_buffer, + struct xdr_buf *message_buffer, int *qop_state, int toktype) { int signalg; int sealalg; - struct xdr_netobj token = {.len = 0, .data = NULL}; s32 checksum_type; struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; s32 now; - unsigned char *plain = NULL; - int plainlen = 0; int direction; s32 seqnum; unsigned char *ptr = (unsigned char *)read_token->data; @@ -100,10 +99,11 @@ krb5_read_token(struct krb5_ctx *ctx, if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype, read_token->len)) goto out; + /* XXX sanity-check bodysize?? */ if (toktype == KG_TOK_WRAP_MSG) { - message_buffer->len = 0; - message_buffer->data = NULL; + /* XXX gone */ + goto out; } /* get the sign and seal algorithms */ @@ -135,43 +135,6 @@ krb5_read_token(struct krb5_ctx *ctx, signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) goto out; - if (toktype == KG_TOK_WRAP_MSG) { - int conflen = crypto_tfm_alg_blocksize(ctx->enc); - int padlen; - - plainlen = bodysize - (14 + KRB5_CKSUM_LENGTH); - plain = ptr + 14 + KRB5_CKSUM_LENGTH; - - ret = krb5_decrypt(ctx->enc, NULL, plain, plain, plainlen); - if (ret) - goto out; - - ret = GSS_S_FAILURE; - padlen = plain[plainlen -1]; - if ((padlen < 1) || (padlen > 8)) - goto out; - token.len = plainlen - conflen - padlen; - - if (token.len) { - token.data = kmalloc(token.len, GFP_KERNEL); - if (token.data == NULL) - goto out; - memcpy(token.data, plain + conflen, token.len); - } - - } else if (toktype == KG_TOK_MIC_MSG) { - token = *message_buffer; - plain = token.data; - plainlen = token.len; - } else { - printk("RPC: bad toktype in krb5_read_token"); - ret = GSS_S_FAILURE; - goto out; - } - - dprintk("RPC krb5_read_token: token.len %d plainlen %d\n", token.len, - plainlen); - /* compute the checksum of the message */ /* initialize the the cksum */ @@ -186,8 +149,8 @@ krb5_read_token(struct krb5_ctx *ctx, switch (signalg) { case SGN_ALG_DES_MAC_MD5: - ret = krb5_make_checksum(checksum_type, ptr - 2, plain, - plainlen, &md5cksum); + ret = krb5_make_checksum(checksum_type, ptr - 2, + message_buffer, &md5cksum); if (ret) goto out; @@ -208,9 +171,6 @@ krb5_read_token(struct krb5_ctx *ctx, /* it got through unscathed. Make sure the context is unexpired */ - if (toktype == KG_TOK_WRAP_MSG) - *message_buffer = token; - if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; @@ -234,7 +194,5 @@ krb5_read_token(struct krb5_ctx *ctx, ret = GSS_S_COMPLETE; out: if (md5cksum.data) kfree(md5cksum.data); - if ((toktype == KG_TOK_WRAP_MSG) && ret && token.data) - kfree(token.data); return ret; } diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index b384cae37052..b360460defcd 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c @@ -196,7 +196,7 @@ gss_import_sec_context(struct xdr_netobj *input_token, u32 gss_get_mic(struct gss_ctx *context_handle, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token) { return context_handle->mech_type->gm_ops @@ -210,7 +210,7 @@ gss_get_mic(struct gss_ctx *context_handle, u32 gss_verify_mic(struct gss_ctx *context_handle, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate) { diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index badaa121f29c..cc4bfb201807 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -568,7 +568,8 @@ call_encode(struct rpc_task *task) rpc_exit(task, -EIO); return; } - if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) { + if (encode && (status = rpcauth_wrap_req(task, encode, req, p, + task->tk_msg.rpc_argp)) < 0) { printk(KERN_WARNING "%s: can't encode arguments: %d\n", clnt->cl_protname, -status); rpc_exit(task, status); @@ -827,7 +828,8 @@ call_decode(struct rpc_task *task) task->tk_action = NULL; if (decode) - task->tk_status = decode(req, p, task->tk_msg.rpc_resp); + task->tk_status = rpcauth_unwrap_resp(task, decode, req, p, + task->tk_msg.rpc_resp); dprintk("RPC: %4d call_decode result %d\n", task->tk_pid, task->tk_status); return; diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index ff8d2bb7bb18..f6bde71024e5 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -126,6 +126,9 @@ EXPORT_SYMBOL(xdr_inline_pages); EXPORT_SYMBOL(xdr_shift_buf); EXPORT_SYMBOL(xdr_write_pages); EXPORT_SYMBOL(xdr_read_pages); +EXPORT_SYMBOL(xdr_buf_from_iov); +EXPORT_SYMBOL(xdr_buf_subsegment); +EXPORT_SYMBOL(xdr_buf_read_netobj); /* Debugging symbols */ #ifdef RPC_DEBUG diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 078dad8a90e5..00e0082704ba 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -538,7 +538,7 @@ _copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len) * Copies data into an arbitrary memory location from an array of pages * The copy is assumed to be non-overlapping. */ -static void +void _copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len) { struct page **pgfrom; @@ -731,3 +731,145 @@ xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->p = (uint32_t *)((char *)iov->iov_base + padding); xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); } + +static struct iovec empty_iov = {.iov_base = NULL, .iov_len = 0}; + +void +xdr_buf_from_iov(struct iovec *iov, struct xdr_buf *buf) +{ + buf->head[0] = *iov; + buf->tail[0] = empty_iov; + buf->page_len = 0; + buf->len = iov->iov_len; +} + +/* Sets subiov to the intersection of iov with the buffer of length len + * starting base bytes after iov. Indicates empty intersection by setting + * length of subiov to zero. Decrements len by length of subiov, sets base + * to zero (or decrements it by length of iov if subiov is empty). */ +static void +iov_subsegment(struct iovec *iov, struct iovec *subiov, int *base, int *len) +{ + if (*base > iov->iov_len) { + subiov->iov_base = NULL; + subiov->iov_len = 0; + *base -= iov->iov_len; + } else { + subiov->iov_base = iov->iov_base + *base; + subiov->iov_len = min(*len, (int)iov->iov_len - *base); + *base = 0; + } + *len -= subiov->iov_len; +} + +/* Sets subbuf to the portion of buf of length len beginning base bytes + * from the start of buf. Returns -1 if base of length are out of bounds. */ +int +xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, + int base, int len) +{ + int i; + + subbuf->len = len; + iov_subsegment(buf->head, subbuf->head, &base, &len); + + if (base < buf->page_len) { + i = (base + buf->page_base) >> PAGE_CACHE_SHIFT; + subbuf->pages = &buf->pages[i]; + subbuf->page_base = (base + buf->page_base) & ~PAGE_CACHE_MASK; + subbuf->page_len = min((int)buf->page_len - base, len); + len -= subbuf->page_len; + base = 0; + } else { + base -= buf->page_len; + subbuf->page_len = 0; + } + + iov_subsegment(buf->tail, subbuf->tail, &base, &len); + if (base || len) + return -1; + return 0; +} + +/* obj is assumed to point to allocated memory of size at least len: */ +static int +read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len) +{ + struct xdr_buf subbuf; + int this_len; + int status; + + status = xdr_buf_subsegment(buf, &subbuf, base, len); + if (status) + goto out; + this_len = min(len, (int)subbuf.head[0].iov_len); + memcpy(obj, subbuf.head[0].iov_base, this_len); + len -= this_len; + obj += this_len; + this_len = min(len, (int)subbuf.page_len); + if (this_len) + _copy_from_pages(obj, subbuf.pages, subbuf.page_base, this_len); + len -= this_len; + obj += this_len; + this_len = min(len, (int)subbuf.tail[0].iov_len); + memcpy(obj, subbuf.tail[0].iov_base, this_len); +out: + return status; +} + +static int +read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) +{ + u32 raw; + int status; + + status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); + if (status) + return status; + *obj = ntohl(raw); + return 0; +} + +/* If the netobj starting offset bytes from the start of xdr_buf is contained + * entirely in the head or the tail, set object to point to it; otherwise + * try to find space for it at the end of the tail, copy it there, and + * set obj to point to it. */ +int +xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset) +{ + u32 tail_offset = buf->head[0].iov_len + buf->page_len; + u32 obj_end_offset; + + if (read_u32_from_xdr_buf(buf, offset, &obj->len)) + goto out; + obj_end_offset = offset + 4 + obj->len; + + if (obj_end_offset <= buf->head[0].iov_len) { + /* The obj is contained entirely in the head: */ + obj->data = buf->head[0].iov_base + offset + 4; + } else if (offset + 4 >= tail_offset) { + if (obj_end_offset - tail_offset + > buf->tail[0].iov_len) + goto out; + /* The obj is contained entirely in the tail: */ + obj->data = buf->tail[0].iov_base + + offset - tail_offset + 4; + } else { + /* use end of tail as storage for obj: + * (We don't copy to the beginning because then we'd have + * to worry about doing a potentially overlapping copy. + * This assumes the object is at most half the length of the + * tail.) */ + if (obj->len > buf->tail[0].iov_len) + goto out; + obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len - + obj->len; + if (read_bytes_from_xdr_buf(buf, offset + 4, + obj->data, obj->len)) + goto out; + + } + return 0; +out: + return -1; +} -- cgit v1.2.3 From cb21a7182f9d73ad544f33f5cdec6d2be9a8cd4f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:45:27 +0100 Subject: RPCSEC_GSS: Move the gss sequence number history from the task structure to the request structure, where it makes more sense. In particular, when we start storing more sequence number history (necessary to process responses to resent requests correctly), this will make it easier to initialize the necessary data structure in the right place (in xprt_request_init). --- include/linux/sunrpc/sched.h | 2 -- include/linux/sunrpc/xprt.h | 1 + net/sunrpc/auth_gss/auth_gss.c | 10 +++++----- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 5e2d23e0ce6c..1113d7f3df13 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -48,8 +48,6 @@ struct rpc_task { __u8 tk_garb_retry, tk_cred_retry, tk_suid_retry; - u32 tk_gss_seqno; /* rpcsec_gss sequence number - used on this request */ /* * timeout_fn to be executed by timer bottom half diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index e29381edeaea..8472b1c5ad2e 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -95,6 +95,7 @@ struct rpc_rqst { struct rpc_rqst * rq_next; /* free list */ int rq_cong; /* has incremented xprt->cong */ int rq_received; /* receive completed */ + u32 rq_seqno; /* gss seq no. used on req. */ struct list_head rq_list; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 1a4b9f504aa2..ef57f1942ea4 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -690,12 +690,12 @@ gss_marshal(struct rpc_task *task, u32 *p, int ruid) goto out_put_ctx; } spin_lock(&ctx->gc_seq_lock); - task->tk_gss_seqno = ctx->gc_seq++; + req->rq_seqno = ctx->gc_seq++; spin_unlock(&ctx->gc_seq_lock); *p++ = htonl((u32) RPC_GSS_VERSION); *p++ = htonl((u32) ctx->gc_proc); - *p++ = htonl((u32) task->tk_gss_seqno); + *p++ = htonl((u32) req->rq_seqno); *p++ = htonl((u32) service); p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); *cred_len = htonl((p - (cred_len + 1)) << 2); @@ -766,7 +766,7 @@ gss_validate(struct rpc_task *task, u32 *p) goto out_bad; if (flav != RPC_AUTH_GSS) goto out_bad; - seq = htonl(task->tk_gss_seqno); + seq = htonl(task->tk_rqstp->rq_seqno); iov.iov_base = &seq; iov.iov_len = sizeof(seq); xdr_buf_from_iov(&iov, &verf_buf); @@ -832,7 +832,7 @@ gss_wrap_req(struct rpc_task *task, integ_len = p++; offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; - *p++ = htonl(task->tk_gss_seqno); + *p++ = htonl(req->rq_seqno); status = encode(rqstp, p, obj); if (status) @@ -909,7 +909,7 @@ gss_unwrap_resp(struct rpc_task *task, mic_offset = integ_len + data_offset; if (mic_offset > rcv_buf->len) goto out; - if (ntohl(*p++) != task->tk_gss_seqno) + if (ntohl(*p++) != req->rq_seqno) goto out; if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, -- cgit v1.2.3 From 8d51075e1a0b96f9b787df3e851fec5c9bb2ba4a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:49:04 +0100 Subject: RPC: Add support for sharing the same RPC transport and credential caches between different mountpoints by allowing cloning of the rpc_client struct. --- include/linux/sunrpc/auth.h | 1 + include/linux/sunrpc/clnt.h | 23 ++++++++++------- net/sunrpc/auth.c | 13 ++++++++-- net/sunrpc/clnt.c | 61 ++++++++++++++++++++++++++++++++++++++++++--- net/sunrpc/pmap_clnt.c | 17 +++++++------ net/sunrpc/sunrpc_syms.c | 2 ++ net/sunrpc/xprt.c | 8 +++--- 7 files changed, 98 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index a68f18bf0a46..1f83e0f5b9d3 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -73,6 +73,7 @@ struct rpc_auth { * differ from the flavor in * au_ops->au_flavor in gss * case) */ + atomic_t au_count; /* Reference counter */ /* per-flavor data */ }; diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 99d57fec03a9..917ec29d789b 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -26,6 +26,8 @@ struct rpc_portmap { __u32 pm_vers; __u32 pm_prot; __u16 pm_port; + unsigned char pm_binding : 1; /* doing a getport() */ + struct rpc_wait_queue pm_bindwait; /* waiting on getport() */ }; struct rpc_inode; @@ -34,6 +36,7 @@ struct rpc_inode; * The high-level client handle */ struct rpc_clnt { + atomic_t cl_count; /* Number of clones */ atomic_t cl_users; /* number of references */ struct rpc_xprt * cl_xprt; /* transport */ struct rpc_procinfo * cl_procinfo; /* procedure info */ @@ -48,26 +51,27 @@ struct rpc_clnt { cl_intr : 1,/* interruptible */ cl_chatty : 1,/* be verbose */ cl_autobind : 1,/* use getport() */ - cl_binding : 1,/* doing a getport() */ cl_droppriv : 1,/* enable NFS suid hack */ cl_oneshot : 1,/* dispose after use */ cl_dead : 1;/* abandoned */ - struct rpc_rtt cl_rtt; /* RTO estimator data */ - - struct rpc_portmap cl_pmap; /* port mapping */ - struct rpc_wait_queue cl_bindwait; /* waiting on getport() */ + struct rpc_rtt * cl_rtt; /* RTO estimator data */ + struct rpc_portmap * cl_pmap; /* port mapping */ int cl_nodelen; /* nodename length */ char cl_nodename[UNX_MAXNODENAME]; char cl_pathname[30];/* Path in rpc_pipe_fs */ struct dentry * cl_dentry; /* inode */ + struct rpc_clnt * cl_parent; /* Points to parent of clones */ + struct rpc_rtt cl_rtt_default; + struct rpc_portmap cl_pmap_default; + char cl_inline_name[32]; }; #define cl_timeout cl_xprt->timeout -#define cl_prog cl_pmap.pm_prog -#define cl_vers cl_pmap.pm_vers -#define cl_port cl_pmap.pm_port -#define cl_prot cl_pmap.pm_prot +#define cl_prog cl_pmap->pm_prog +#define cl_vers cl_pmap->pm_vers +#define cl_port cl_pmap->pm_port +#define cl_prot cl_pmap->pm_prot /* * General RPC program info @@ -108,6 +112,7 @@ struct rpc_procinfo { struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname, struct rpc_program *info, u32 version, rpc_authflavor_t authflavor); +struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); int rpc_shutdown_client(struct rpc_clnt *); int rpc_destroy_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index a89b30cadff3..35900eb52b61 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -61,6 +61,7 @@ rpcauth_unregister(struct rpc_authops *ops) struct rpc_auth * rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) { + struct rpc_auth *auth; struct rpc_authops *ops; u32 flavor = pseudoflavor_to_flavor(pseudoflavor); @@ -68,13 +69,21 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) return NULL; if (!try_module_get(ops->owner)) return NULL; - clnt->cl_auth = ops->create(clnt, pseudoflavor); - return clnt->cl_auth; + auth = ops->create(clnt, pseudoflavor); + if (!auth) + return NULL; + atomic_set(&auth->au_count, 1); + if (clnt->cl_auth) + rpcauth_destroy(clnt->cl_auth); + clnt->cl_auth = auth; + return auth; } void rpcauth_destroy(struct rpc_auth *auth) { + if (!atomic_dec_and_test(&auth->au_count)) + return; auth->au_ops->destroy(auth); module_put(auth->au_ops->owner); kfree(auth); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index cc4bfb201807..6c6a8310000a 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -102,6 +102,7 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, { struct rpc_version *version; struct rpc_clnt *clnt = NULL; + int len; dprintk("RPC: creating %s client for %s (xprt %p)\n", program->name, servname, xprt); @@ -116,23 +117,37 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, goto out_no_clnt; memset(clnt, 0, sizeof(*clnt)); atomic_set(&clnt->cl_users, 0); + atomic_set(&clnt->cl_count, 1); + clnt->cl_parent = clnt; + + clnt->cl_server = clnt->cl_inline_name; + len = strlen(servname) + 1; + if (len > sizeof(clnt->cl_inline_name)) { + char *buf = kmalloc(len, GFP_KERNEL); + if (buf != 0) + clnt->cl_server = buf; + else + len = sizeof(clnt->cl_inline_name); + } + strlcpy(clnt->cl_server, servname, len); clnt->cl_xprt = xprt; clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; - clnt->cl_server = servname; clnt->cl_protname = program->name; + clnt->cl_pmap = &clnt->cl_pmap_default; clnt->cl_port = xprt->addr.sin_port; clnt->cl_prog = program->number; clnt->cl_vers = version->number; clnt->cl_prot = xprt->prot; clnt->cl_stats = program->stats; - INIT_RPC_WAITQ(&clnt->cl_bindwait, "bindwait"); + INIT_RPC_WAITQ(&clnt->cl_pmap_default.pm_bindwait, "bindwait"); if (!clnt->cl_port) clnt->cl_autobind = 1; - rpc_init_rtt(&clnt->cl_rtt, xprt->timeout.to_initval); + clnt->cl_rtt = &clnt->cl_rtt_default; + rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval); if (rpc_setup_pipedir(clnt, program->pipe_dir_name) < 0) goto out_no_path; @@ -157,11 +172,39 @@ out_no_clnt: out_no_auth: rpc_rmdir(clnt->cl_pathname); out_no_path: + if (clnt->cl_server != clnt->cl_inline_name) + kfree(clnt->cl_server); kfree(clnt); clnt = NULL; goto out; } +/* + * This function clones the RPC client structure. It allows us to share the + * same transport while varying parameters such as the authentication + * flavour. + */ +struct rpc_clnt * +rpc_clone_client(struct rpc_clnt *clnt) +{ + struct rpc_clnt *new; + + new = (struct rpc_clnt *)kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) + goto out_no_clnt; + memcpy(new, clnt, sizeof(*new)); + atomic_set(&new->cl_count, 1); + atomic_set(&new->cl_users, 0); + atomic_inc(&new->cl_parent->cl_count); + if (new->cl_auth) + atomic_inc(&new->cl_auth->au_count); +out: + return new; +out_no_clnt: + printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__); + goto out; +} + /* * Properly shut down an RPC client, terminating all outstanding * requests. Note that we must be certain that cl_oneshot and @@ -201,19 +244,29 @@ rpc_shutdown_client(struct rpc_clnt *clnt) int rpc_destroy_client(struct rpc_clnt *clnt) { + if (!atomic_dec_and_test(&clnt->cl_count)) + return 1; + BUG_ON(atomic_read(&clnt->cl_users) != 0); + dprintk("RPC: destroying %s client for %s\n", clnt->cl_protname, clnt->cl_server); - if (clnt->cl_auth) { rpcauth_destroy(clnt->cl_auth); clnt->cl_auth = NULL; } + if (clnt->cl_parent != clnt) { + rpc_destroy_client(clnt->cl_parent); + goto out_free; + } if (clnt->cl_pathname[0]) rpc_rmdir(clnt->cl_pathname); if (clnt->cl_xprt) { xprt_destroy(clnt->cl_xprt); clnt->cl_xprt = NULL; } + if (clnt->cl_server != clnt->cl_inline_name) + kfree(clnt->cl_server); +out_free: kfree(clnt); return 0; } diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index ed627ea885e8..4b619e002123 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -41,7 +41,7 @@ static spinlock_t pmap_lock = SPIN_LOCK_UNLOCKED; void rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) { - struct rpc_portmap *map = &clnt->cl_pmap; + struct rpc_portmap *map = clnt->cl_pmap; struct sockaddr_in *sap = &clnt->cl_xprt->addr; struct rpc_message msg = { .rpc_proc = &pmap_procedures[PMAP_GETPORT], @@ -57,12 +57,12 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) map->pm_prog, map->pm_vers, map->pm_prot); spin_lock(&pmap_lock); - if (clnt->cl_binding) { - rpc_sleep_on(&clnt->cl_bindwait, task, NULL, 0); + if (map->pm_binding) { + rpc_sleep_on(&map->pm_bindwait, task, NULL, 0); spin_unlock(&pmap_lock); return; } - clnt->cl_binding = 1; + map->pm_binding = 1; spin_unlock(&pmap_lock); task->tk_status = -EACCES; /* why set this? returns -EIO below */ @@ -85,8 +85,8 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) bailout: spin_lock(&pmap_lock); - clnt->cl_binding = 0; - rpc_wake_up(&clnt->cl_bindwait); + map->pm_binding = 0; + rpc_wake_up(&map->pm_bindwait); spin_unlock(&pmap_lock); task->tk_status = -EIO; task->tk_action = NULL; @@ -129,6 +129,7 @@ static void pmap_getport_done(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; + struct rpc_portmap *map = clnt->cl_pmap; dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n", task->tk_pid, task->tk_status, clnt->cl_port); @@ -145,8 +146,8 @@ pmap_getport_done(struct rpc_task *task) clnt->cl_xprt->addr.sin_port = clnt->cl_port; } spin_lock(&pmap_lock); - clnt->cl_binding = 0; - rpc_wake_up(&clnt->cl_bindwait); + map->pm_binding = 0; + rpc_wake_up(&map->pm_bindwait); spin_unlock(&pmap_lock); } diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index f6bde71024e5..dd8409baa09e 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -41,6 +41,7 @@ EXPORT_SYMBOL(rpc_release_task); /* RPC client functions */ EXPORT_SYMBOL(rpc_create_client); +EXPORT_SYMBOL(rpc_clone_client); EXPORT_SYMBOL(rpc_destroy_client); EXPORT_SYMBOL(rpc_shutdown_client); EXPORT_SYMBOL(rpc_release_client); @@ -66,6 +67,7 @@ EXPORT_SYMBOL(xprt_set_timeout); /* Client credential cache */ EXPORT_SYMBOL(rpcauth_register); EXPORT_SYMBOL(rpcauth_unregister); +EXPORT_SYMBOL(rpcauth_create); EXPORT_SYMBOL(rpcauth_lookupcred); EXPORT_SYMBOL(rpcauth_lookup_credcache); EXPORT_SYMBOL(rpcauth_free_credcache); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index ef616fe9c561..e6c5f7ab7968 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -584,9 +584,9 @@ xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied) __xprt_put_cong(xprt, req); if (timer) { if (req->rq_ntrans == 1) - rpc_update_rtt(&clnt->cl_rtt, timer, + rpc_update_rtt(clnt->cl_rtt, timer, (long)jiffies - req->rq_xtime); - rpc_set_timeo(&clnt->cl_rtt, timer, req->rq_ntrans - 1); + rpc_set_timeo(clnt->cl_rtt, timer, req->rq_ntrans - 1); } } @@ -1224,8 +1224,8 @@ xprt_transmit(struct rpc_task *task) spin_lock_bh(&xprt->sock_lock); if (!xprt->nocong) { int timer = task->tk_msg.rpc_proc->p_timer; - task->tk_timeout = rpc_calc_rto(&clnt->cl_rtt, timer); - task->tk_timeout <<= rpc_ntimeo(&clnt->cl_rtt, timer); + task->tk_timeout = rpc_calc_rto(clnt->cl_rtt, timer); + task->tk_timeout <<= rpc_ntimeo(clnt->cl_rtt, timer); task->tk_timeout <<= clnt->cl_timeout.to_retries - req->rq_timeout.to_retries; if (task->tk_timeout > req->rq_timeout.to_maxval) -- cgit v1.2.3 From c2f2ea78e058ca74fe2e674fd8cdfb71966b248c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:50:51 +0100 Subject: NFSv4/RPCSEC_GSS: Make Frank's server->client_sys feature use RPC cloning in order to avoid duplicating sockets etc. Make NFSv4 share a single socket for all communication to the same server. --- fs/nfs/inode.c | 67 +++++++++++++++++++++++++++++++++++++++----------- fs/nfs/nfs4proc.c | 6 +---- fs/nfs/nfs4state.c | 4 +++ include/linux/nfs_fs.h | 15 +++++------ 4 files changed, 65 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index af022760107a..5cd28cb5076a 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -493,10 +493,17 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) server->client = nfs_create_client(server, data); if (server->client == NULL) goto out_fail; - data->pseudoflavor = RPC_AUTH_UNIX; /* RFC 2623, sec 2.3.2 */ - server->client_sys = nfs_create_client(server, data); - if (server->client_sys == NULL) - goto out_shutdown; + /* RFC 2623, sec 2.3.2 */ + if (authflavor != RPC_AUTH_UNIX) { + server->client_sys = rpc_clone_client(server->client); + if (server->client_sys == NULL) + goto out_shutdown; + if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys)) + goto out_shutdown; + } else { + atomic_inc(&server->client->cl_count); + server->client_sys = server->client; + } /* Fire up rpciod if not yet running */ if (rpciod_up() != 0) { @@ -1349,6 +1356,7 @@ static struct file_system_type nfs_fs_type = { static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) { struct nfs_server *server; + struct nfs4_client *clp = NULL; struct rpc_xprt *xprt = NULL; struct rpc_clnt *clnt = NULL; struct rpc_timeout timeparms; @@ -1398,13 +1406,13 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, return -EINVAL; } - /* Now create transport and client */ - xprt = xprt_create_proto(proto, &server->addr, &timeparms); - if (xprt == NULL) { - printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + clp = nfs4_get_client(&server->addr.sin_addr); + if (!clp) { + printk(KERN_WARNING "NFS: failed to create NFS4 client.\n"); goto out_fail; } + /* Now create transport and client */ authflavour = RPC_AUTH_UNIX; if (data->auth_flavourlen != 0) { if (data->auth_flavourlen > 1) @@ -1414,34 +1422,61 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, goto out_fail; } } - clnt = rpc_create_client(xprt, server->hostname, &nfs_program, - server->rpc_ops->version, authflavour); + + down_write(&clp->cl_sem); + if (clp->cl_rpcclient == NULL) { + xprt = xprt_create_proto(proto, &server->addr, &timeparms); + if (xprt == NULL) { + up_write(&clp->cl_sem); + printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + goto out_fail; + } + clnt = rpc_create_client(xprt, server->hostname, &nfs_program, + server->rpc_ops->version, authflavour); + if (clnt == NULL) { + up_write(&clp->cl_sem); + printk(KERN_WARNING "NFS: cannot create RPC client.\n"); + xprt_destroy(xprt); + goto out_fail; + } + clnt->cl_chatty = 1; + clp->cl_rpcclient = clnt; + clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); + memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); + } + clnt = rpc_clone_client(clp->cl_rpcclient); + server->nfs4_state = clp; + up_write(&clp->cl_sem); + if (clnt == NULL) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); - xprt_destroy(xprt); goto out_fail; } clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0; - clnt->cl_chatty = 1; server->client = clnt; + if (clnt->cl_auth->au_flavor != authflavour) { + if (rpcauth_create(authflavour, clnt) == NULL) { + printk(KERN_WARNING "NFS: couldn't create credcache!\n"); + goto out_shutdown; + } + } + /* Fire up rpciod if not yet running */ if (rpciod_up() != 0) { printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); goto out_shutdown; } - if (create_nfsv4_state(server, data)) - goto out_shutdown; - if ((server->idmap = nfs_idmap_new(server)) == NULL) printk(KERN_WARNING "NFS: couldn't start IDmap\n"); err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; + clp = NULL; rpciod_down(); destroy_nfsv4_state(server); if (server->idmap != NULL) @@ -1449,6 +1484,8 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, out_shutdown: rpc_shutdown_client(server->client); out_fail: + if (clp) + nfs4_put_client(clp); return err; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f447a7a05449..2d6850d65dd3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -762,9 +762,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct qstr q; int status; - clp = server->nfs4_state = nfs4_get_client(&server->addr.sin_addr); - if (!clp) - return -ENOMEM; + clp = server->nfs4_state; down_write(&clp->cl_sem); /* Has the clientid already been initialized? */ @@ -850,8 +848,6 @@ no_setclientid: return status; out_unlock: up_write(&clp->cl_sem); - nfs4_put_client(clp); - server->nfs4_state = NULL; return status; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 42a2344a2893..960899db68e2 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -93,6 +93,10 @@ nfs4_free_client(struct nfs4_client *clp) kfree(sp); } BUG_ON(!list_empty(&clp->cl_state_owners)); + if (clp->cl_cred) + put_rpccred(clp->cl_cred); + if (clp->cl_rpcclient) + rpc_shutdown_client(clp->cl_rpcclient); kfree(clp); } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 5ae592b26d63..1ba074302796 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -489,6 +489,14 @@ struct nfs4_client { int cl_nunused; spinlock_t cl_lock; atomic_t cl_count; + + struct rpc_clnt * cl_rpcclient; + struct rpc_cred * cl_cred; + + /* Our own IP address, as a null-terminated string. + * This is used to generate the clientid, and the callback address. + */ + char cl_ipaddr[16]; }; /* @@ -558,13 +566,6 @@ extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); struct nfs4_mount_data; -static inline int -create_nfsv4_state(struct nfs_server *server, struct nfs4_mount_data *data) -{ - server->nfs4_state = NULL; - return 0; -} - static inline void destroy_nfsv4_state(struct nfs_server *server) { -- cgit v1.2.3 From acac57debb7af23d6178e319c1017b24ec847ded Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:52:03 +0100 Subject: NFSv4: Convert the RENEW operation from using nfs4_compound, to being a standalone RPC call in preparation for the renew daemon overhaul. --- fs/nfs/nfs4proc.c | 59 +++++++++----------------------------------------- fs/nfs/nfs4renewd.c | 2 +- fs/nfs/nfs4xdr.c | 46 ++++++++++++++++++++++++++++++++++----- include/linux/nfs4.h | 1 + include/linux/nfs_fs.h | 2 +- 5 files changed, 53 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 2d6850d65dd3..e7327e3bf484 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -428,18 +428,6 @@ nfs4_setup_rename(struct nfs4_compound *cp, struct qstr *old, struct qstr *new, cp->req_nops++; } -static void -nfs4_setup_renew(struct nfs4_compound *cp) -{ - struct nfs4_client **client_state = GET_OP(cp, renew); - - *client_state = cp->server->nfs4_state; - - OPNUM(cp) = OP_RENEW; - cp->req_nops++; - cp->renew_index = cp->req_nops; -} - static void nfs4_setup_restorefh(struct nfs4_compound *cp) { @@ -1648,55 +1636,28 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) } /* - * nfs4_proc_renew(): This is not one of the nfs_rpc_ops; it is a special + * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special * standalone procedure for queueing an asynchronous RENEW. */ -struct renew_desc { - struct rpc_task task; - struct nfs4_compound compound; - struct nfs4_op ops[1]; -}; - static void renew_done(struct rpc_task *task) { - struct nfs4_compound *cp = (struct nfs4_compound *) task->tk_msg.rpc_argp; - process_lease(cp); -} - -static void -renew_release(struct rpc_task *task) -{ - kfree(task->tk_calldata); + struct nfs_server *server = (struct nfs_server *)task->tk_msg.rpc_resp; + unsigned long timestamp = (unsigned long)task->tk_calldata; + renew_lease(server, timestamp); } int -nfs4_proc_renew(struct nfs_server *server) +nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *cred) { - struct renew_desc *rp; - struct rpc_task *task; - struct nfs4_compound *cp; struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND], + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], + .rpc_argp = server->nfs4_state, + .rpc_resp = server, + .rpc_cred = cred, }; - rp = (struct renew_desc *) kmalloc(sizeof(*rp), GFP_KERNEL); - if (!rp) - return -ENOMEM; - cp = &rp->compound; - task = &rp->task; - - nfs4_setup_compound(cp, rp->ops, server, "renew"); - nfs4_setup_renew(cp); - - msg.rpc_argp = cp; - msg.rpc_resp = cp; - rpc_init_task(task, server->client, renew_done, RPC_TASK_ASYNC); - rpc_call_setup(task, &msg, 0); - task->tk_calldata = rp; - task->tk_release = renew_release; - - return rpc_execute(task); + return rpc_call_async(server->client, &msg, 0, renew_done, (void *)jiffies); } /* diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index 4ba871885dbc..92176b0b0a9b 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -70,7 +70,7 @@ renewd(struct rpc_task *task) timeout = (2 * lease) / 3 + last - jiffies; else { /* Queue an asynchronous RENEW. */ - nfs4_proc_renew(server); + nfs4_proc_async_renew(server, NULL); timeout = (2 * lease) / 3; } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 35a28e9e7d21..add338c9be56 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -90,6 +90,8 @@ extern int nfs_stat_to_errno(int); #define decode_pre_write_getattr_maxsz op_decode_hdr_maxsz + 5 #define encode_post_write_getattr_maxsz op_encode_hdr_maxsz + 2 #define decode_post_write_getattr_maxsz op_decode_hdr_maxsz + 13 +#define encode_renew_maxsz op_encode_hdr_maxsz + 3 +#define decode_renew_maxsz op_decode_hdr_maxsz #define NFS4_enc_compound_sz 1024 /* XXX: large enough? */ #define NFS4_dec_compound_sz 1024 /* XXX: large enough? */ @@ -159,6 +161,10 @@ extern int nfs_stat_to_errno(int); #define NFS4_dec_setattr_sz compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 3 +#define NFS4_enc_renew_sz compound_encode_hdr_maxsz + \ + encode_renew_maxsz +#define NFS4_dec_renew_sz compound_decode_hdr_maxsz + \ + decode_renew_maxsz static struct { @@ -889,9 +895,6 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs case OP_RENAME: status = encode_rename(xdr, &cp->ops[i].u.rename); break; - case OP_RENEW: - status = encode_renew(xdr, cp->ops[i].u.renew); - break; case OP_RESTOREFH: status = encode_restorefh(xdr); break; @@ -1131,6 +1134,22 @@ out: return status; } +/* + * a RENEW request + */ +static int +nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 1, + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + return encode_renew(&xdr, clp); +} + /* * START OF "GENERIC" DECODE ROUTINES. * These may look a little ugly since they are imported from a "generic" @@ -2137,9 +2156,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs case OP_RENAME: status = decode_rename(xdr, &op->u.rename); break; - case OP_RENEW: - status = decode_renew(xdr); - break; case OP_SAVEFH: status = decode_savefh(xdr); break; @@ -2387,6 +2403,23 @@ out: return status; } +/* + * Decode RENEW response + */ +static int +nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, uint32_t *p, void *dummy) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_renew(&xdr); + return status; +} + uint32_t * nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) { @@ -2443,6 +2476,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), + PROC(RENEW, enc_renew, dec_renew), }; struct rpc_version nfs_version4 = { diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 8bb512eb2b43..1598d1b3c739 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -221,6 +221,7 @@ enum { NFSPROC4_CLNT_OPEN_CONFIRM, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, + NFSPROC4_CLNT_RENEW, }; #endif diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 1ba074302796..a8dc94ea12d3 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -545,7 +545,7 @@ struct nfs4_state { /* nfs4proc.c */ -extern int nfs4_proc_renew(struct nfs_server *server); +extern int nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); /* nfs4renewd.c */ -- cgit v1.2.3 From 7cf3d8b799b5a018b89bc7ce553d66c12aa50ade Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:53:15 +0100 Subject: NFSv4: Convert the lease renewal daemon from being per-mountpoint to being per-server. Instead of running it on top of rpciod, convert it to use keventd. This mean we can use the struct nfs4_client semaphores for ordering purposes. --- fs/nfs/inode.c | 17 ++++++-- fs/nfs/nfs4proc.c | 41 ++++++++++-------- fs/nfs/nfs4renewd.c | 108 +++++++++++++++++++++++++++++++--------------- fs/nfs/nfs4state.c | 26 +++++++++++ include/linux/nfs_fs.h | 35 +++++++-------- include/linux/nfs_fs_sb.h | 5 ++- 6 files changed, 154 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5cd28cb5076a..2c1df6550feb 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -163,6 +163,8 @@ nfs_put_super(struct super_block *sb) nfs_idmap_delete(server); #endif /* CONFIG_NFS_V4 */ + nfs4_renewd_prepare_shutdown(server); + if (server->client != NULL) rpc_shutdown_client(server->client); if (server->client_sys != NULL) @@ -1281,6 +1283,8 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, if (!server) return ERR_PTR(-ENOMEM); memset(server, 0, sizeof(struct nfs_server)); + /* Zero out the NFS state stuff */ + init_nfsv4_state(server); root = &server->fh; memcpy(root, &data->root, sizeof(*root)); @@ -1444,13 +1448,15 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); } + list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); clnt = rpc_clone_client(clp->cl_rpcclient); server->nfs4_state = clp; up_write(&clp->cl_sem); + clp = NULL; if (clnt == NULL) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); - goto out_fail; + goto out_remove_list; } clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; @@ -1476,13 +1482,16 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; - clp = NULL; rpciod_down(); - destroy_nfsv4_state(server); if (server->idmap != NULL) nfs_idmap_delete(server); out_shutdown: rpc_shutdown_client(server->client); +out_remove_list: + down_write(&server->nfs4_state->cl_sem); + list_del_init(&server->nfs4_siblings); + up_write(&server->nfs4_state->cl_sem); + destroy_nfsv4_state(server); out_fail: if (clp) nfs4_put_client(clp); @@ -1542,6 +1551,8 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, if (!server) return ERR_PTR(-ENOMEM); memset(server, 0, sizeof(struct nfs_server)); + /* Zero out the NFS state stuff */ + init_nfsv4_state(server); if (data->version != NFS4_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e7327e3bf484..a3ac8370ba3c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -56,8 +56,6 @@ extern struct rpc_procinfo nfs4_procedures[]; extern nfs4_stateid zero_stateid; -static spinlock_t renew_lock = SPIN_LOCK_UNLOCKED; - static void nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, struct nfs_server *server, char *tag) @@ -480,10 +478,11 @@ nfs4_setup_setclientid_confirm(struct nfs4_compound *cp) static void renew_lease(struct nfs_server *server, unsigned long timestamp) { - spin_lock(&renew_lock); - if (time_before(server->last_renewal,timestamp)) - server->last_renewal = timestamp; - spin_unlock(&renew_lock); + struct nfs4_client *clp = server->nfs4_state; + spin_lock(&clp->cl_lock); + if (time_before(clp->cl_last_renewal,timestamp)) + clp->cl_last_renewal = timestamp; + spin_unlock(&clp->cl_lock); } static inline void @@ -748,6 +747,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo fsinfo; unsigned char * p; struct qstr q; + unsigned long last_renewed; int status; clp = server->nfs4_state; @@ -773,6 +773,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, */ nfs4_setup_compound(&compound, ops, server, "setclientid"); nfs4_setup_setclientid(&compound, 0, 0); + last_renewed = jiffies; if ((status = nfs4_call_compound(&compound, NULL, 0))) goto out_unlock; @@ -786,20 +787,22 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, nfs4_setup_putrootfh(&compound); nfs4_setup_getrootattr(&compound, fattr, &fsinfo); nfs4_setup_getfh(&compound, fhandle); + last_renewed = jiffies; if ((status = nfs4_call_compound(&compound, NULL, 0))) goto out_unlock; - clp->cl_state = NFS4CLNT_OK; -no_setclientid: /* * Now that we have instantiated the clientid and determined * the lease time, we can initialize the renew daemon for this * server. * FIXME: we only need one renewd daemon per server. */ - server->lease_time = fsinfo.lease_time * HZ; - if ((status = nfs4_init_renewd(server))) - goto out_unlock; + clp->cl_lease_time = fsinfo.lease_time * HZ; + clp->cl_last_renewal = last_renewed; + nfs4_schedule_state_renewal(clp); + clp->cl_state = NFS4CLNT_OK; + +no_setclientid: up_write(&clp->cl_sem); /* @@ -1642,22 +1645,24 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) static void renew_done(struct rpc_task *task) { - struct nfs_server *server = (struct nfs_server *)task->tk_msg.rpc_resp; + struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp; unsigned long timestamp = (unsigned long)task->tk_calldata; - renew_lease(server, timestamp); + spin_lock(&clp->cl_lock); + if (time_before(clp->cl_last_renewal,timestamp)) + clp->cl_last_renewal = timestamp; + spin_unlock(&clp->cl_lock); } int -nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *cred) +nfs4_proc_async_renew(struct nfs4_client *clp) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], - .rpc_argp = server->nfs4_state, - .rpc_resp = server, - .rpc_cred = cred, + .rpc_argp = clp, + .rpc_cred = clp->cl_cred, }; - return rpc_call_async(server->client, &msg, 0, renew_done, (void *)jiffies); + return rpc_call_async(clp->cl_rpcclient, &msg, 0, renew_done, (void *)jiffies); } /* diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index 92176b0b0a9b..667e06f1c647 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -54,53 +54,91 @@ #include #include -static RPC_WAITQ(nfs4_renewd_queue, "nfs4_renewd_queue"); +#define NFSDBG_FACILITY NFSDBG_PROC -static void -renewd(struct rpc_task *task) +void +nfs4_renew_state(void *data) { - struct nfs_server *server = (struct nfs_server *)task->tk_calldata; - unsigned long lease = server->lease_time; - unsigned long last = server->last_renewal; - unsigned long timeout; + struct nfs4_client *clp = (struct nfs4_client *)data; + long lease, timeout; + unsigned long last, now; - if (!server->nfs4_state) - timeout = (2 * lease) / 3; - else if (jiffies < last + lease/3) - timeout = (2 * lease) / 3 + last - jiffies; - else { + down_read(&clp->cl_sem); + dprintk("%s: start\n", __FUNCTION__); + /* Are there any active superblocks? */ + if (list_empty(&clp->cl_superblocks)) + goto out; + spin_lock(&clp->cl_lock); + lease = clp->cl_lease_time; + last = clp->cl_last_renewal; + now = jiffies; + timeout = (2 * lease) / 3 + (long)last - (long)now; + /* Are we close to a lease timeout? */ + if (time_after(now, last + lease/3)) { + spin_unlock(&clp->cl_lock); /* Queue an asynchronous RENEW. */ - nfs4_proc_async_renew(server, NULL); + nfs4_proc_async_renew(clp); timeout = (2 * lease) / 3; - } - + spin_lock(&clp->cl_lock); + } else + dprintk("%s: failed to call renewd. Reason: lease not expired \n", + __FUNCTION__); if (timeout < 5 * HZ) /* safeguard */ timeout = 5 * HZ; - task->tk_timeout = timeout; - task->tk_action = renewd; - task->tk_exit = NULL; - rpc_sleep_on(&nfs4_renewd_queue, task, NULL, NULL); - return; + dprintk("%s: requeueing work. Lease period = %ld\n", + __FUNCTION__, (timeout + HZ - 1) / HZ); + cancel_delayed_work(&clp->cl_renewd); + schedule_delayed_work(&clp->cl_renewd, timeout); + spin_unlock(&clp->cl_lock); +out: + up_read(&clp->cl_sem); + dprintk("%s: done\n", __FUNCTION__); } -int -nfs4_init_renewd(struct nfs_server *server) +/* Must be called with clp->cl_sem locked for writes */ +void +nfs4_schedule_state_renewal(struct nfs4_client *clp) { - struct rpc_task *task; - int status; + long timeout; - lock_kernel(); - status = -ENOMEM; - task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC); - if (!task) - goto out; - task->tk_calldata = server; - task->tk_action = renewd; - status = rpc_execute(task); + spin_lock(&clp->cl_lock); + timeout = (2 * clp->cl_lease_time) / 3 + (long)clp->cl_last_renewal + - (long)jiffies; + if (timeout < 5 * HZ) + timeout = 5 * HZ; + dprintk("%s: requeueing work. Lease period = %ld\n", + __FUNCTION__, (timeout + HZ - 1) / HZ); + cancel_delayed_work(&clp->cl_renewd); + schedule_delayed_work(&clp->cl_renewd, timeout); + spin_unlock(&clp->cl_lock); +} -out: - unlock_kernel(); - return status; +void +nfs4_renewd_prepare_shutdown(struct nfs_server *server) +{ + struct nfs4_client *clp = server->nfs4_state; + + if (!clp) + return; + flush_scheduled_work(); + down_write(&clp->cl_sem); + if (!list_empty(&server->nfs4_siblings)) + list_del_init(&server->nfs4_siblings); + up_write(&clp->cl_sem); +} + +/* Must be called with clp->cl_sem locked for writes */ +void +nfs4_kill_renewd(struct nfs4_client *clp) +{ + down_read(&clp->cl_sem); + if (!list_empty(&clp->cl_superblocks)) { + up_read(&clp->cl_sem); + return; + } + cancel_delayed_work(&clp->cl_renewd); + up_read(&clp->cl_sem); + flush_scheduled_work(); } /* diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 960899db68e2..bded88b8d3eb 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -41,6 +41,7 @@ #include #include #include +#include #define OPENOWNER_POOL_SIZE 8 @@ -55,6 +56,28 @@ nfs4_stateid one_stateid = static LIST_HEAD(nfs4_clientid_list); +extern void nfs4_renew_state(void *); + +void +init_nfsv4_state(struct nfs_server *server) +{ + server->nfs4_state = NULL; + INIT_LIST_HEAD(&server->nfs4_siblings); +} + +void +destroy_nfsv4_state(struct nfs_server *server) +{ + if (server->mnt_path) { + kfree(server->mnt_path); + server->mnt_path = NULL; + } + if (server->nfs4_state) { + nfs4_put_client(server->nfs4_state); + server->nfs4_state = NULL; + } +} + /* * nfs4_get_client(): returns an empty client structure * nfs4_put_client(): drops reference to client structure @@ -75,6 +98,8 @@ nfs4_alloc_client(struct in_addr *addr) INIT_LIST_HEAD(&clp->cl_unused); spin_lock_init(&clp->cl_lock); atomic_set(&clp->cl_count, 1); + INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); + INIT_LIST_HEAD(&clp->cl_superblocks); clp->cl_state = NFS4CLNT_NEW; } return clp; @@ -130,6 +155,7 @@ nfs4_put_client(struct nfs4_client *clp) return; list_del(&clp->cl_servers); spin_unlock(&state_spinlock); + nfs4_kill_renewd(clp); nfs4_free_client(clp); } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a8dc94ea12d3..4996221041a4 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -28,6 +28,7 @@ #include #include #include +#include /* * Enable debugging support for nfs client. @@ -493,6 +494,12 @@ struct nfs4_client { struct rpc_clnt * cl_rpcclient; struct rpc_cred * cl_cred; + struct list_head cl_superblocks; /* List of nfs_server structs */ + + unsigned long cl_lease_time; + unsigned long cl_last_renewal; + struct work_struct cl_renewd; + /* Our own IP address, as a null-terminated string. * This is used to generate the clientid, and the callback address. */ @@ -545,13 +552,17 @@ struct nfs4_state { /* nfs4proc.c */ -extern int nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *); +extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); /* nfs4renewd.c */ -extern int nfs4_init_renewd(struct nfs_server *server); +extern void nfs4_schedule_state_renewal(struct nfs4_client *); +extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); +extern void nfs4_kill_renewd(struct nfs4_client *); /* nfs4state.c */ +extern void init_nfsv4_state(struct nfs_server *); +extern void destroy_nfsv4_state(struct nfs_server *); extern struct nfs4_client *nfs4_get_client(struct in_addr *); extern void nfs4_put_client(struct nfs4_client *clp); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); @@ -560,29 +571,13 @@ extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); - - - - - struct nfs4_mount_data; -static inline void -destroy_nfsv4_state(struct nfs_server *server) -{ - if (server->mnt_path) { - kfree(server->mnt_path); - server->mnt_path = NULL; - } - if (server->nfs4_state) { - nfs4_put_client(server->nfs4_state); - server->nfs4_state = NULL; - } -} #else -#define create_nfsv4_state(server, data) 0 +#define init_nfsv4_state(server) do { } while (0) #define destroy_nfsv4_state(server) do { } while (0) #define nfs4_put_state_owner(inode, owner) do { } while (0) #define nfs4_put_open_state(state) do { } while (0) +#define nfs4_renewd_prepare_shutdown(server) do { } while (0) #endif #endif /* __KERNEL__ */ diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 20ceb626cb3b..5f0b0ce3aa2c 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -35,8 +35,9 @@ struct nfs_server { char ip_addr[16]; char * mnt_path; struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */ - unsigned long lease_time; /* in jiffies */ - unsigned long last_renewal; /* in jiffies */ + struct list_head nfs4_siblings; /* List of other nfs_server structs + * that share the same clientid + */ void *idmap; #endif }; -- cgit v1.2.3 From f414757e25c8122dcf6ac1b546f67b6317e161b3 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:54:20 +0100 Subject: NFSv4: Split out the code for retrieving static server information out of the GETATTR compound. --- fs/nfs/nfs4proc.c | 73 +++++----------------- fs/nfs/nfs4xdr.c | 160 ++++++++++++++++++++++++++++++++++++++---------- include/linux/nfs4.h | 1 + include/linux/nfs_xdr.h | 1 - 4 files changed, 144 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a3ac8370ba3c..264ef919baee 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -56,6 +56,9 @@ extern struct rpc_procinfo nfs4_procedures[]; extern nfs4_stateid zero_stateid; +static int nfs4_proc_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); + + static void nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, struct nfs_server *server, char *tag) @@ -177,44 +180,16 @@ u32 nfs4_statfs_bitmap[2] = { | FATTR4_WORD1_SPACE_TOTAL }; -u32 nfs4_fsinfo_bitmap[2] = { - FATTR4_WORD0_MAXFILESIZE - | FATTR4_WORD0_MAXREAD - | FATTR4_WORD0_MAXWRITE - | FATTR4_WORD0_LEASE_TIME, - 0 -}; - u32 nfs4_pathconf_bitmap[2] = { FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME, 0 }; -/* mount bitmap: fattr bitmap + lease time */ -u32 nfs4_mount_bitmap[2] = { - FATTR4_WORD0_TYPE - | FATTR4_WORD0_CHANGE - | FATTR4_WORD0_SIZE - | FATTR4_WORD0_FSID - | FATTR4_WORD0_FILEID - | FATTR4_WORD0_LEASE_TIME, - FATTR4_WORD1_MODE - | FATTR4_WORD1_NUMLINKS - | FATTR4_WORD1_OWNER - | FATTR4_WORD1_OWNER_GROUP - | FATTR4_WORD1_RAWDEV - | FATTR4_WORD1_SPACE_USED - | FATTR4_WORD1_TIME_ACCESS - | FATTR4_WORD1_TIME_METADATA - | FATTR4_WORD1_TIME_MODIFY -}; - static inline void __nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap, struct nfs_fattr *fattr, struct nfs_fsstat *fsstat, - struct nfs_fsinfo *fsinfo, struct nfs_pathconf *pathconf) { struct nfs4_getattr *getattr = GET_OP(cp, getattr); @@ -222,7 +197,6 @@ __nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap, getattr->gt_bmval = bitmap; getattr->gt_attrs = fattr; getattr->gt_fsstat = fsstat; - getattr->gt_fsinfo = fsinfo; getattr->gt_pathconf = pathconf; OPNUM(cp) = OP_GETATTR; @@ -234,16 +208,7 @@ nfs4_setup_getattr(struct nfs4_compound *cp, struct nfs_fattr *fattr) { __nfs4_setup_getattr(cp, nfs4_fattr_bitmap, fattr, - NULL, NULL, NULL); -} - -static void -nfs4_setup_getrootattr(struct nfs4_compound *cp, - struct nfs_fattr *fattr, - struct nfs_fsinfo *fsinfo) -{ - __nfs4_setup_getattr(cp, nfs4_mount_bitmap, - fattr, NULL, fsinfo, NULL); + NULL, NULL); } static void @@ -251,15 +216,7 @@ nfs4_setup_statfs(struct nfs4_compound *cp, struct nfs_fsstat *fsstat) { __nfs4_setup_getattr(cp, nfs4_statfs_bitmap, - NULL, fsstat, NULL, NULL); -} - -static void -nfs4_setup_fsinfo(struct nfs4_compound *cp, - struct nfs_fsinfo *fsinfo) -{ - __nfs4_setup_getattr(cp, nfs4_fsinfo_bitmap, - NULL, NULL, fsinfo, NULL); + NULL, fsstat, NULL); } static void @@ -267,7 +224,7 @@ nfs4_setup_pathconf(struct nfs4_compound *cp, struct nfs_pathconf *pathconf) { __nfs4_setup_getattr(cp, nfs4_pathconf_bitmap, - NULL, NULL, NULL, pathconf); + NULL, NULL, pathconf); } static void @@ -759,7 +716,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, fattr->valid = 0; nfs4_setup_compound(&compound, ops, server, "getrootfh"); nfs4_setup_putrootfh(&compound); - nfs4_setup_getrootattr(&compound, fattr, &fsinfo); + nfs4_setup_getattr(&compound, fattr); nfs4_setup_getfh(&compound, fhandle); if ((status = nfs4_call_compound(&compound, NULL, 0))) goto out_unlock; @@ -785,7 +742,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, nfs4_setup_compound(&compound, ops, server, "setclientid_confirm"); nfs4_setup_setclientid_confirm(&compound); nfs4_setup_putrootfh(&compound); - nfs4_setup_getrootattr(&compound, fattr, &fsinfo); + nfs4_setup_getattr(&compound, fattr); nfs4_setup_getfh(&compound, fhandle); last_renewed = jiffies; if ((status = nfs4_call_compound(&compound, NULL, 0))) @@ -797,6 +754,8 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, * server. * FIXME: we only need one renewd daemon per server. */ + if ((status = nfs4_proc_fsinfo(server, fhandle, &fsinfo))) + goto out_unlock; clp->cl_lease_time = fsinfo.lease_time * HZ; clp->cl_last_renewal = last_renewed; nfs4_schedule_state_renewal(clp); @@ -1434,14 +1393,14 @@ static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) { - struct nfs4_compound compound; - struct nfs4_op ops[2]; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO], + .rpc_argp = fhandle, + .rpc_resp = fsinfo, + }; memset(fsinfo, 0, sizeof(*fsinfo)); - nfs4_setup_compound(&compound, ops, server, "statfs"); - nfs4_setup_putfh(&compound, fhandle); - nfs4_setup_fsinfo(&compound, fsinfo); - return nfs4_call_compound(&compound, NULL, 0); + return rpc_call_sync(server->client, &msg, 0); } static int diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index add338c9be56..ed79661601dd 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -90,6 +90,8 @@ extern int nfs_stat_to_errno(int); #define decode_pre_write_getattr_maxsz op_decode_hdr_maxsz + 5 #define encode_post_write_getattr_maxsz op_encode_hdr_maxsz + 2 #define decode_post_write_getattr_maxsz op_decode_hdr_maxsz + 13 +#define encode_fsinfo_maxsz op_encode_hdr_maxsz + 2 +#define decode_fsinfo_maxsz op_decode_hdr_maxsz + 11 #define encode_renew_maxsz op_encode_hdr_maxsz + 3 #define decode_renew_maxsz op_decode_hdr_maxsz @@ -161,6 +163,12 @@ extern int nfs_stat_to_errno(int); #define NFS4_dec_setattr_sz compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 3 +#define NFS4_enc_fsinfo_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_fsinfo_maxsz +#define NFS4_dec_fsinfo_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_fsinfo_maxsz #define NFS4_enc_renew_sz compound_encode_hdr_maxsz + \ encode_renew_maxsz #define NFS4_dec_renew_sz compound_decode_hdr_maxsz + \ @@ -506,6 +514,15 @@ encode_post_write_getattr(struct xdr_stream *xdr) FATTR4_WORD1_TIME_MODIFY); } +static int +encode_fsinfo(struct xdr_stream *xdr) +{ + return encode_getattr_one(xdr, FATTR4_WORD0_MAXFILESIZE + | FATTR4_WORD0_MAXREAD + | FATTR4_WORD0_MAXWRITE + | FATTR4_WORD0_LEASE_TIME); +} + static int encode_getfh(struct xdr_stream *xdr) { @@ -1134,6 +1151,26 @@ out: return status; } +/* + * FSINFO request + */ +static int +nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, uint32_t *p, void *fhandle) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, fhandle); + if (!status) + status = encode_fsinfo(&xdr); + return status; +} + /* * a RENEW request */ @@ -1312,7 +1349,6 @@ decode_create(struct xdr_stream *xdr, struct nfs4_create *create) } extern uint32_t nfs4_fattr_bitmap[2]; -extern uint32_t nfs4_fsinfo_bitmap[2]; extern uint32_t nfs4_fsstat_bitmap[2]; extern uint32_t nfs4_pathconf_bitmap[2]; @@ -1322,7 +1358,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, { struct nfs_fattr *nfp = getattr->gt_attrs; struct nfs_fsstat *fsstat = getattr->gt_fsstat; - struct nfs_fsinfo *fsinfo = getattr->gt_fsinfo; struct nfs_pathconf *pathconf = getattr->gt_pathconf; uint32_t attrlen, dummy32, bmlen, bmval0 = 0, @@ -1368,11 +1403,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, nfp->nlink = 1; nfp->timestamp = jiffies; } - if (fsinfo) { - fsinfo->rtmult = fsinfo->wtmult = 512; /* ??? */ - fsinfo->lease_time = 60; - } - if (bmval0 & FATTR4_WORD0_TYPE) { READ_BUF(4); len += 4; @@ -1406,12 +1436,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, (long long)nfp->fsid_u.nfs4.major, (long long)nfp->fsid_u.nfs4.minor); } - if (bmval0 & FATTR4_WORD0_LEASE_TIME) { - READ_BUF(4); - len += 4; - READ32(fsinfo->lease_time); - dprintk("read_attrs: lease_time=%d\n", fsinfo->lease_time); - } if (bmval0 & FATTR4_WORD0_FILEID) { READ_BUF(8); len += 8; @@ -1436,12 +1460,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, READ64(fsstat->tfiles); dprintk("read_attrs: files_tot=0x%Lx\n", (long long) fsstat->tfiles); } - if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { - READ_BUF(8); - len += 8; - READ64(fsinfo->maxfilesize); - dprintk("read_attrs: maxfilesize=0x%Lx\n", (long long) fsinfo->maxfilesize); - } if (bmval0 & FATTR4_WORD0_MAXLINK) { READ_BUF(4); len += 4; @@ -1454,20 +1472,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, READ32(pathconf->max_namelen); dprintk("read_attrs: maxname=%d\n", pathconf->max_namelen); } - if (bmval0 & FATTR4_WORD0_MAXREAD) { - READ_BUF(8); - len += 8; - READ64(fsinfo->rtmax); - fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax; - dprintk("read_attrs: maxread=%d\n", fsinfo->rtmax); - } - if (bmval0 & FATTR4_WORD0_MAXWRITE) { - READ_BUF(8); - len += 8; - READ64(fsinfo->wtmax); - fsinfo->wtpref = fsinfo->wtmax; - dprintk("read_attrs: maxwrite=%d\n", fsinfo->wtmax); - } if (bmval1 & FATTR4_WORD1_MODE) { READ_BUF(4); @@ -1709,6 +1713,74 @@ out_bad_bitmap: } +static int +decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) +{ + uint32_t *p; + uint32_t len, attrlen, bmlen, bmval0 = 0, bmval1 = 0; + int status; + + status = decode_op_hdr(xdr, OP_GETATTR); + if (status) + return status; + READ_BUF(4); + READ32(bmlen); + if (bmlen < 1) + return -EIO; + READ_BUF(bmlen << 2); + READ32(bmval0); + if (bmval0 & ~(FATTR4_WORD0_MAXFILESIZE|FATTR4_WORD0_MAXREAD| + FATTR4_WORD0_MAXWRITE|FATTR4_WORD0_LEASE_TIME)) + goto out_bad_bitmap; + if (bmlen > 1) { + READ32(bmval1); + if (bmval1 != 0 || bmlen > 2) + goto out_bad_bitmap; + } + READ_BUF(4); + READ32(attrlen); + READ_BUF(attrlen); + fsinfo->rtmult = fsinfo->wtmult = 512; /* ??? */ + fsinfo->lease_time = 60; + len = attrlen; + + if (bmval0 & FATTR4_WORD0_LEASE_TIME) { + len -= 4; + READ32(fsinfo->lease_time); + dprintk("read_attrs: lease_time=%d\n", fsinfo->lease_time); + } + if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { + len -= 8; + READ64(fsinfo->maxfilesize); + dprintk("read_attrs: maxfilesize=0x%Lx\n", (long long) fsinfo->maxfilesize); + } + if (bmval0 & FATTR4_WORD0_MAXREAD) { + len -= 8; + READ64(fsinfo->rtmax); + fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax; + dprintk("read_attrs: maxread=%d\n", fsinfo->rtmax); + } + if (bmval0 & FATTR4_WORD0_MAXWRITE) { + len -= 8; + READ64(fsinfo->wtmax); + fsinfo->wtpref = fsinfo->wtmax; + dprintk("read_attrs: maxwrite=%d\n", fsinfo->wtmax); + } + if (len != 0) + goto out_bad_attrlen; + return 0; +out_bad_attrlen: + printk(KERN_NOTICE "%s: server attribute length %u does not match bitmap 0x%x/0x%x\n", + __FUNCTION__, (unsigned int)attrlen, + (unsigned int) bmval0, (unsigned int)bmval1); + return -EIO; +out_bad_bitmap: + printk(KERN_NOTICE "%s: server returned bad attribute bitmap 0x%x/0x%x\n", + __FUNCTION__, + (unsigned int)bmval0, (unsigned int)bmval1); + return -EIO; +} + static int decode_getfh(struct xdr_stream *xdr, struct nfs4_getfh *getfh) { @@ -2403,6 +2475,27 @@ out: return status; } +/* + * FSINFO request + */ +static int +nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_putfh(&xdr); + if (!status) + status = decode_fsinfo(&xdr, fsinfo); + if (!status) + status = -nfs_stat_to_errno(hdr.status); + return status; +} + /* * Decode RENEW response */ @@ -2476,6 +2569,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), + PROC(FSINFO, enc_fsinfo, dec_fsinfo), PROC(RENEW, enc_renew, dec_renew), }; diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 1598d1b3c739..d535a46f7d4f 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -221,6 +221,7 @@ enum { NFSPROC4_CLNT_OPEN_CONFIRM, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, + NFSPROC4_CLNT_FSINFO, NFSPROC4_CLNT_RENEW, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 5bc59e4db5e3..09c2dd2216d0 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -449,7 +449,6 @@ struct nfs4_getattr { u32 * gt_bmval; /* request */ struct nfs_fattr * gt_attrs; /* response */ struct nfs_fsstat * gt_fsstat; /* response */ - struct nfs_fsinfo * gt_fsinfo; /* response */ struct nfs_pathconf * gt_pathconf; /* response */ }; -- cgit v1.2.3 From 88e4b0f2c019ff2f4cee0045812bab0f8146f4ae Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:56:12 +0100 Subject: NFSv4: Convert SETCLIENTID and SETCLIENTID_CONFIRM to be standalone operations. Ensure that SETCLIENTID_CONFIRM always returns the lease timeout length. --- fs/nfs/nfs4proc.c | 127 ++++++++++++++++++++++---------------------- fs/nfs/nfs4xdr.c | 136 ++++++++++++++++++++++++++++++++++++++++++------ include/linux/nfs4.h | 2 + include/linux/nfs_fs.h | 2 + include/linux/nfs_xdr.h | 2 - 5 files changed, 186 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 264ef919baee..8e8ac9cc08f6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -56,9 +56,6 @@ extern struct rpc_procinfo nfs4_procedures[]; extern nfs4_stateid zero_stateid; -static int nfs4_proc_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); - - static void nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, struct nfs_server *server, char *tag) @@ -397,41 +394,6 @@ nfs4_setup_savefh(struct nfs4_compound *cp) cp->req_nops++; } -static void -nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short port) -{ - struct nfs4_setclientid *setclientid = GET_OP(cp, setclientid); - struct nfs_server *server = cp->server; - struct timespec tv; - u32 *p; - - tv = CURRENT_TIME; - p = (u32 *)setclientid->sc_verifier.data; - *p++ = tv.tv_sec; - *p++ = tv.tv_nsec; - setclientid->sc_name = server->ip_addr; - sprintf(setclientid->sc_netid, "udp"); - sprintf(setclientid->sc_uaddr, "%s.%d.%d", server->ip_addr, port >> 8, port & 255); - setclientid->sc_prog = program; - setclientid->sc_cb_ident = 0; - setclientid->sc_state = server->nfs4_state; - - OPNUM(cp) = OP_SETCLIENTID; - cp->req_nops++; -} - -static void -nfs4_setup_setclientid_confirm(struct nfs4_compound *cp) -{ - struct nfs4_client **client_state = GET_OP(cp, setclientid_confirm); - - *client_state = cp->server->nfs4_state; - - OPNUM(cp) = OP_SETCLIENTID_CONFIRM; - cp->req_nops++; - cp->renew_index = cp->req_nops; -} - static void renew_lease(struct nfs_server *server, unsigned long timestamp) { @@ -701,51 +663,31 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs4_client *clp; struct nfs4_compound compound; struct nfs4_op ops[4]; - struct nfs_fsinfo fsinfo; unsigned char * p; struct qstr q; - unsigned long last_renewed; int status; clp = server->nfs4_state; down_write(&clp->cl_sem); /* Has the clientid already been initialized? */ - if (clp->cl_state != NFS4CLNT_NEW) { + if (clp->cl_state != NFS4CLNT_NEW) /* Yep, so just read the root attributes and the lease time. */ - fattr->valid = 0; - nfs4_setup_compound(&compound, ops, server, "getrootfh"); - nfs4_setup_putrootfh(&compound); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - if ((status = nfs4_call_compound(&compound, NULL, 0))) - goto out_unlock; goto no_setclientid; - } /* * SETCLIENTID. * Until delegations are imported, we don't bother setting the program * number and port to anything meaningful. */ - nfs4_setup_compound(&compound, ops, server, "setclientid"); - nfs4_setup_setclientid(&compound, 0, 0); - last_renewed = jiffies; - if ((status = nfs4_call_compound(&compound, NULL, 0))) + if ((status = nfs4_proc_setclientid(clp, 0, 0))) goto out_unlock; /* * SETCLIENTID_CONFIRM, plus root filehandle. * We also get the lease time here. */ - fattr->valid = 0; - nfs4_setup_compound(&compound, ops, server, "setclientid_confirm"); - nfs4_setup_setclientid_confirm(&compound); - nfs4_setup_putrootfh(&compound); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - last_renewed = jiffies; - if ((status = nfs4_call_compound(&compound, NULL, 0))) + if ((status = nfs4_proc_setclientid_confirm(clp))) goto out_unlock; /* @@ -754,10 +696,6 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, * server. * FIXME: we only need one renewd daemon per server. */ - if ((status = nfs4_proc_fsinfo(server, fhandle, &fsinfo))) - goto out_unlock; - clp->cl_lease_time = fsinfo.lease_time * HZ; - clp->cl_last_renewal = last_renewed; nfs4_schedule_state_renewal(clp); clp->cl_state = NFS4CLNT_OK; @@ -770,6 +708,13 @@ no_setclientid: * catch an ERR_WRONGSEC if it occurs along the way... */ p = server->mnt_path; + fattr->valid = 0; + nfs4_setup_compound(&compound, ops, server, "getrootfh"); + nfs4_setup_putrootfh(&compound); + nfs4_setup_getattr(&compound, fattr); + nfs4_setup_getfh(&compound, fhandle); + if ((status = nfs4_call_compound(&compound, NULL, 0))) + goto out; for (;;) { while (*p == '/') p++; @@ -798,6 +743,7 @@ no_setclientid: return status; out_unlock: up_write(&clp->cl_sem); +out: return status; } @@ -1724,6 +1670,57 @@ nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa return 1; } +int +nfs4_proc_setclientid(struct nfs4_client *clp, + u32 program, unsigned short port) +{ + u32 *p; + struct nfs4_setclientid setclientid; + struct timespec tv; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID], + .rpc_argp = &setclientid, + .rpc_resp = clp, + .rpc_cred = clp->cl_cred, + }; + + tv = CURRENT_TIME; + p = (u32*)setclientid.sc_verifier.data; + *p++ = (u32)tv.tv_sec; + *p = (u32)tv.tv_nsec; + setclientid.sc_name = clp->cl_ipaddr; + sprintf(setclientid.sc_netid, "tcp"); + sprintf(setclientid.sc_uaddr, "%s.%d.%d", clp->cl_ipaddr, port >> 8, port & 255); + setclientid.sc_prog = htonl(program); + setclientid.sc_cb_ident = 0; + + return rpc_call_sync(clp->cl_rpcclient, &msg, 0); +} + +int +nfs4_proc_setclientid_confirm(struct nfs4_client *clp) +{ + struct nfs_fsinfo fsinfo; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM], + .rpc_argp = clp, + .rpc_resp = &fsinfo, + .rpc_cred = clp->cl_cred, + }; + unsigned long now; + int status; + + now = jiffies; + status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); + if (status == 0) { + spin_lock(&clp->cl_lock); + clp->cl_lease_time = fsinfo.lease_time * HZ; + clp->cl_last_renewal = now; + spin_unlock(&clp->cl_lock); + } + return status; +} + struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ .getroot = nfs4_proc_get_root, diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index ed79661601dd..cb96666b3831 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -73,6 +73,8 @@ extern int nfs_stat_to_errno(int); #define encode_putfh_maxsz op_encode_hdr_maxsz + 1 + \ (NFS4_FHSIZE >> 2) #define decode_putfh_maxsz op_decode_hdr_maxsz +#define encode_putrootfh_maxsz op_encode_hdr_maxsz +#define decode_putrootfh_maxsz op_decode_hdr_maxsz #define encode_getfh_maxsz op_encode_hdr_maxsz #define decode_getfh_maxsz op_decode_hdr_maxsz + 1 + \ (NFS4_FHSIZE >> 2) @@ -94,6 +96,21 @@ extern int nfs_stat_to_errno(int); #define decode_fsinfo_maxsz op_decode_hdr_maxsz + 11 #define encode_renew_maxsz op_encode_hdr_maxsz + 3 #define decode_renew_maxsz op_decode_hdr_maxsz +#define encode_setclientid_maxsz \ + op_encode_hdr_maxsz + \ + 4 /*server->ip_addr*/ + \ + 1 /*Netid*/ + \ + 6 /*uaddr*/ + \ + 6 + (NFS4_VERIFIER_SIZE >> 2) +#define decode_setclientid_maxsz \ + op_decode_hdr_maxsz + \ + 2 + \ + 1024 /* large value for CLID_INUSE */ +#define encode_setclientid_confirm_maxsz \ + op_encode_hdr_maxsz + \ + 3 + (NFS4_VERIFIER_SIZE >> 2) +#define decode_setclientid_confirm_maxsz \ + op_decode_hdr_maxsz #define NFS4_enc_compound_sz 1024 /* XXX: large enough? */ #define NFS4_dec_compound_sz 1024 /* XXX: large enough? */ @@ -173,6 +190,20 @@ extern int nfs_stat_to_errno(int); encode_renew_maxsz #define NFS4_dec_renew_sz compound_decode_hdr_maxsz + \ decode_renew_maxsz +#define NFS4_enc_setclientid_sz compound_encode_hdr_maxsz + \ + encode_setclientid_maxsz +#define NFS4_dec_setclientid_sz compound_decode_hdr_maxsz + \ + decode_setclientid_maxsz +#define NFS4_enc_setclientid_confirm_sz \ + compound_encode_hdr_maxsz + \ + encode_setclientid_confirm_maxsz + \ + encode_putrootfh_maxsz + \ + encode_fsinfo_maxsz +#define NFS4_dec_setclientid_confirm_sz \ + compound_decode_hdr_maxsz + \ + decode_setclientid_confirm_maxsz + \ + decode_putrootfh_maxsz + \ + decode_fsinfo_maxsz static struct { @@ -918,12 +949,6 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs case OP_SAVEFH: status = encode_savefh(xdr); break; - case OP_SETCLIENTID: - status = encode_setclientid(xdr, &cp->ops[i].u.setclientid); - break; - case OP_SETCLIENTID_CONFIRM: - status = encode_setclientid_confirm(xdr, cp->ops[i].u.setclientid_confirm); - break; default: BUG(); } @@ -1187,6 +1212,46 @@ nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) return encode_renew(&xdr, clp); } +/* + * a SETCLIENTID request + */ +static int +nfs4_xdr_enc_setclientid(struct rpc_rqst *req, uint32_t *p, + struct nfs4_setclientid *sc) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 1, + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + return encode_setclientid(&xdr, sc); +} + +/* + * a SETCLIENTID_CONFIRM request + */ +static int +nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, + struct nfs4_client *clp) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 3, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_setclientid_confirm(&xdr, clp); + if (!status) + status = encode_putrootfh(&xdr); + if (!status) + status = encode_fsinfo(&xdr); + return status; +} + /* * START OF "GENERIC" DECODE ROUTINES. * These may look a little ugly since they are imported from a "generic" @@ -2098,7 +2163,7 @@ decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res) } static int -decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid *setclientid) +decode_setclientid(struct xdr_stream *xdr, struct nfs4_client *clp) { uint32_t *p; uint32_t opnum; @@ -2114,9 +2179,9 @@ decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid *setclientid) } READ32(nfserr); if (nfserr == NFS_OK) { - READ_BUF(8 + sizeof(setclientid->sc_state->cl_confirm.data)); - READ64(setclientid->sc_state->cl_clientid); - COPYMEM(setclientid->sc_state->cl_confirm.data, sizeof(setclientid->sc_state->cl_confirm.data)); + READ_BUF(8 + sizeof(clp->cl_confirm.data)); + READ64(clp->cl_clientid); + COPYMEM(clp->cl_confirm.data, sizeof(clp->cl_confirm.data)); } else if (nfserr == NFSERR_CLID_INUSE) { uint32_t len; @@ -2231,12 +2296,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs case OP_SAVEFH: status = decode_savefh(xdr); break; - case OP_SETCLIENTID: - status = decode_setclientid(xdr, &op->u.setclientid); - break; - case OP_SETCLIENTID_CONFIRM: - status = decode_setclientid_confirm(xdr); - break; default: BUG(); return -EIO; @@ -2513,6 +2572,49 @@ nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, uint32_t *p, void *dummy) return status; } +/* + * a SETCLIENTID request + */ +static int +nfs4_xdr_dec_setclientid(struct rpc_rqst *req, uint32_t *p, + struct nfs4_client *clp) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_setclientid(&xdr, clp); + if (!status) + status = -nfs_stat_to_errno(hdr.status); + return status; +} + +/* + * a SETCLIENTID_CONFIRM request + */ +static int +nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_setclientid_confirm(&xdr); + if (!status) + status = decode_putrootfh(&xdr); + if (!status) + status = decode_fsinfo(&xdr, fsinfo); + if (!status) + status = -nfs_stat_to_errno(hdr.status); + return status; +} + uint32_t * nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) { @@ -2571,6 +2673,8 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(SETATTR, enc_setattr, dec_setattr), PROC(FSINFO, enc_fsinfo, dec_fsinfo), PROC(RENEW, enc_renew, dec_renew), + PROC(SETCLIENTID, enc_setclientid, dec_setclientid), + PROC(SETCLIENTID_CONFIRM, enc_setclientid_confirm, dec_setclientid_confirm), }; struct rpc_version nfs_version4 = { diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index d535a46f7d4f..a601ae91de21 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -223,6 +223,8 @@ enum { NFSPROC4_CLNT_SETATTR, NFSPROC4_CLNT_FSINFO, NFSPROC4_CLNT_RENEW, + NFSPROC4_CLNT_SETCLIENTID, + NFSPROC4_CLNT_SETCLIENTID_CONFIRM, }; #endif diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4996221041a4..fcaf3322cadf 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -552,6 +552,8 @@ struct nfs4_state { /* nfs4proc.c */ +extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); +extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 09c2dd2216d0..244a430f83ef 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -555,8 +555,6 @@ struct nfs4_op { struct nfs4_rename rename; struct nfs4_client * renew; struct nfs4_setattr setattr; - struct nfs4_setclientid setclientid; - struct nfs4_client * setclientid_confirm; } u; }; -- cgit v1.2.3 From 2d5e9ebc702452d30ca0a9c50b9e3f0d8a56a4e9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:57:13 +0100 Subject: NFSv4: Don't translate those NFSv4 errors that are needed by the kernel itself into EIO. Fix a signed/unsigned bug in nfs4_increment_seqid. --- fs/nfs/nfs4state.c | 4 +-- fs/nfs/nfs4xdr.c | 63 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/nfs4.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs_fs.h | 2 +- 4 files changed, 135 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index bded88b8d3eb..3bdb3a016e25 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -376,9 +376,9 @@ nfs4_put_open_state(struct nfs4_state *state) * see comments nfs_fs.h:seqid_mutating_error() */ void -nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp) +nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) { - if (status == NFS_OK || seqid_mutating_err(status)) + if (status == NFS_OK || seqid_mutating_err(-status)) sp->so_seqid++; } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index cb96666b3831..67a8e7cf403b 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -57,7 +57,7 @@ /* Mapping from NFS error code to "errno" error code. */ #define errno_NFSERR_IO EIO -extern int nfs_stat_to_errno(int); +static int nfs_stat_to_errno(int); /* NFSv4 COMPOUND tags are only wanted for debugging purposes */ #ifdef DEBUG @@ -2650,6 +2650,67 @@ nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) return p; } +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + */ +static struct { + int stat; + int errno; +} nfs_errtbl[] = { + { NFS4_OK, 0 }, + { NFS4ERR_PERM, EPERM }, + { NFS4ERR_NOENT, ENOENT }, + { NFS4ERR_IO, errno_NFSERR_IO }, + { NFS4ERR_NXIO, ENXIO }, + { NFS4ERR_ACCESS, EACCES }, + { NFS4ERR_EXIST, EEXIST }, + { NFS4ERR_XDEV, EXDEV }, + { NFS4ERR_NOTDIR, ENOTDIR }, + { NFS4ERR_ISDIR, EISDIR }, + { NFS4ERR_INVAL, EINVAL }, + { NFS4ERR_FBIG, EFBIG }, + { NFS4ERR_NOSPC, ENOSPC }, + { NFS4ERR_ROFS, EROFS }, + { NFS4ERR_MLINK, EMLINK }, + { NFS4ERR_NAMETOOLONG, ENAMETOOLONG }, + { NFS4ERR_NOTEMPTY, ENOTEMPTY }, + { NFS4ERR_DQUOT, EDQUOT }, + { NFS4ERR_STALE, ESTALE }, + { NFS4ERR_BADHANDLE, EBADHANDLE }, + { NFS4ERR_BAD_COOKIE, EBADCOOKIE }, + { NFS4ERR_NOTSUPP, ENOTSUPP }, + { NFS4ERR_TOOSMALL, ETOOSMALL }, + { NFS4ERR_SERVERFAULT, ESERVERFAULT }, + { NFS4ERR_BADTYPE, EBADTYPE }, + { NFS4ERR_LOCKED, EAGAIN }, + { NFS4ERR_RESOURCE, EREMOTEIO }, + { NFS4ERR_SYMLINK, ELOOP }, + { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP }, + { NFS4ERR_DEADLOCK, EDEADLK }, + { -1, EIO } +}; + +/* + * Convert an NFS error code to a local one. + * This one is used jointly by NFSv2 and NFSv3. + */ +static int +nfs_stat_to_errno(int stat) +{ + int i; + for (i = 0; nfs_errtbl[i].stat != -1; i++) { + if (nfs_errtbl[i].stat == stat) + return nfs_errtbl[i].errno; + } + /* If we cannot translate the error, the recovery routines should + * handle it. + * Note: remaining NFSv4 error codes have values > 10000, so should + * not conflict with native Linux error codes. + */ + return stat; +} + #ifndef MAX # define MAX(a, b) (((a) > (b))? (a) : (b)) #endif diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index a601ae91de21..f56601a5825d 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -88,6 +88,76 @@ enum nfs_opnum4 { OP_WRITE = 38, }; +enum nfsstat4 { + NFS4_OK = 0, + NFS4ERR_PERM = 1, + NFS4ERR_NOENT = 2, + NFS4ERR_IO = 5, + NFS4ERR_NXIO = 6, + NFS4ERR_ACCESS = 13, + NFS4ERR_EXIST = 17, + NFS4ERR_XDEV = 18, + /* Unused/reserved 19 */ + NFS4ERR_NOTDIR = 20, + NFS4ERR_ISDIR = 21, + NFS4ERR_INVAL = 22, + NFS4ERR_FBIG = 27, + NFS4ERR_NOSPC = 28, + NFS4ERR_ROFS = 30, + NFS4ERR_MLINK = 31, + NFS4ERR_NAMETOOLONG = 63, + NFS4ERR_NOTEMPTY = 66, + NFS4ERR_DQUOT = 69, + NFS4ERR_STALE = 70, + NFS4ERR_BADHANDLE = 10001, + NFS4ERR_BAD_COOKIE = 10003, + NFS4ERR_NOTSUPP = 10004, + NFS4ERR_TOOSMALL = 10005, + NFS4ERR_SERVERFAULT = 10006, + NFS4ERR_BADTYPE = 10007, + NFS4ERR_DELAY = 10008, + NFS4ERR_SAME = 10009, + NFS4ERR_DENIED = 10010, + NFS4ERR_EXPIRED = 10011, + NFS4ERR_LOCKED = 10012, + NFS4ERR_GRACE = 10013, + NFS4ERR_FHEXPIRED = 10014, + NFS4ERR_SHARE_DENIED = 10015, + NFS4ERR_WRONGSEC = 10016, + NFS4ERR_CLID_INUSE = 10017, + NFS4ERR_RESOURCE = 10018, + NFS4ERR_MOVED = 10019, + NFS4ERR_NOFILEHANDLE = 10020, + NFS4ERR_MINOR_VERS_MISMATCH = 10021, + NFS4ERR_STALE_CLIENTID = 10022, + NFS4ERR_STALE_STATEID = 10023, + NFS4ERR_OLD_STATEID = 10024, + NFS4ERR_BAD_STATEID = 10025, + NFS4ERR_BAD_SEQID = 10026, + NFS4ERR_NOT_SAME = 10027, + NFS4ERR_LOCK_RANGE = 10028, + NFS4ERR_SYMLINK = 10029, + NFS4ERR_RESTOREFH = 10030, + NFS4ERR_LEASE_MOVED = 10031, + NFS4ERR_ATTRNOTSUPP = 10032, + NFS4ERR_NO_GRACE = 10033, + NFS4ERR_RECLAIM_BAD = 10034, + NFS4ERR_RECLAIM_CONFLICT = 10035, + NFS4ERR_BADXDR = 10036, + NFS4ERR_LOCKS_HELD = 10037, + NFS4ERR_OPENMODE = 10038, + NFS4ERR_BADOWNER = 10039, + NFS4ERR_BADCHAR = 10040, + NFS4ERR_BADNAME = 10041, + NFS4ERR_BAD_RANGE = 10042, + NFS4ERR_LOCK_NOTSUPP = 10043, + NFS4ERR_OP_ILLEGAL = 10044, + NFS4ERR_DEADLOCK = 10045, + NFS4ERR_FILE_OPEN = 10046, + NFS4ERR_ADMIN_REVOKED = 10047, + NFS4ERR_CB_PATH_DOWN = 10048 +}; + /* * Note: NF4BAD is not actually part of the protocol; it is just used * internally by nfsd. diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index fcaf3322cadf..a005c999ce43 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -571,7 +571,7 @@ extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struc extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); -extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); +extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); struct nfs4_mount_data; #else -- cgit v1.2.3 From e85c40cde9d47156479a28710becc39becf6fe24 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:57:57 +0100 Subject: NFSv4: Preparation for the server reboot recovery code. --- fs/nfs/nfs4proc.c | 55 ++++++++++++++++++++++++++-- fs/nfs/nfs4state.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4xdr.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 1 + include/linux/nfs_fs.h | 2 + include/linux/nfs_xdr.h | 13 +++++++ 6 files changed, 253 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 8e8ac9cc08f6..5e52e7d218f8 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -458,6 +458,54 @@ process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr) } } +/* + * OPEN_RECLAIM: + * reclaim state on the server after a reboot. + * Assumes caller is holding the sp->so_sem + */ +int +nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr = { + .valid = 0, + }; + struct nfs4_change_info d_cinfo; + struct nfs4_getattr f_getattr = { + .gt_bmval = nfs4_fattr_bitmap, + .gt_attrs = &fattr, + }; + + struct nfs_open_reclaimargs o_arg = { + .fh = NFS_FH(inode), + .seqid = sp->so_seqid, + .id = sp->so_id, + .share_access = state->state, + .clientid = server->nfs4_state->cl_clientid, + .claim = NFS4_OPEN_CLAIM_PREVIOUS, + .f_getattr = &f_getattr, + }; + struct nfs_openres o_res = { + .cinfo = &d_cinfo, + .f_getattr = &f_getattr, + .server = server, /* Grrr */ + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_RECLAIM], + .rpc_argp = &o_arg, + .rpc_resp = &o_res, + .rpc_cred = sp->so_cred, + }; + int status; + + status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_seqid(status, sp); + /* Update the inode attributes */ + nfs_refresh_inode(inode, &fattr); + return status; +} + struct nfs4_state * nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred) { @@ -523,10 +571,9 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt o_arg.id = sp->so_id; status = rpc_call_sync(server->client, &msg, 0); - if (status) { - goto out_up; - } nfs4_increment_seqid(status, sp); + if (status) + goto out_up; process_cinfo(&d_cinfo, &d_attr); nfs_refresh_inode(dir, &d_attr); @@ -555,9 +602,9 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt memcpy(&oc_arg.stateid, &o_res.stateid, sizeof(oc_arg.stateid)); status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_seqid(status, sp); if (status) goto out_up; - nfs4_increment_seqid(status, sp); memcpy(&state->stateid, &oc_res.stateid, sizeof(state->stateid)); } else memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 3bdb3a016e25..8738414bd8ea 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -382,6 +382,95 @@ nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) sp->so_seqid++; } +static int reclaimer(void *); +struct reclaimer_args { + struct nfs4_client *clp; + struct completion complete; +}; + +/* + * State recovery routine + */ +void +nfs4_recover_state(struct nfs4_client *clp) +{ + struct reclaimer_args args = { + .clp = clp, + }; + init_completion(&args.complete); + + down_read(&clp->cl_sem); + if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) + goto out_failed; + wait_for_completion(&args.complete); + return; +out_failed: + up_read(&clp->cl_sem); +} + +static void +nfs4_reclaim_open_state(struct nfs4_state_owner *sp) +{ + struct nfs4_state *state; + int status; + + list_for_each_entry(state, &sp->so_states, open_states) { + status = nfs4_open_reclaim(sp, state); + if (status) { + /* + * Open state on this file cannot be recovered + * All we can do is revert to using the zero stateid. + */ + memset(state->stateid.data, 0, + sizeof(state->stateid.data)); + /* Mark the file as being 'closed' */ + state->state = 0; + } + } +} + +static int +reclaimer(void *ptr) +{ + struct reclaimer_args *args = (struct reclaimer_args *)ptr; + struct nfs4_client *clp = args->clp; + struct nfs4_state_owner *sp; + int status; + + daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); + allow_signal(SIGKILL); + + complete(&args->complete); + + /* Are there any NFS mounts out there? */ + if (list_empty(&clp->cl_superblocks)) + goto out; + status = nfs4_proc_setclientid(clp, 0, 0); + if (status) + goto out_error; + status = nfs4_proc_setclientid_confirm(clp); + if (status) + goto out_error; + spin_lock(&clp->cl_lock); + list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + atomic_inc(&sp->so_count); + spin_unlock(&clp->cl_lock); + down(&sp->so_sema); + nfs4_reclaim_open_state(sp); + up(&sp->so_sema); + nfs4_put_state_owner(sp); + spin_lock(&clp->cl_lock); + } + spin_unlock(&clp->cl_lock); +out: + up_read(&clp->cl_sem); + return 0; +out_error: + printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u\n", + NIPQUAD(clp->cl_addr.s_addr)); + goto out; +} + /* * Local variables: * c-basic-offset: 8 diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 67a8e7cf403b..16296618a231 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -166,6 +166,16 @@ static int nfs_stat_to_errno(int); #define NFS4_dec_open_confirm_sz compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 4 +#define NFS4_enc_open_reclaim_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + op_encode_hdr_maxsz + \ + 11 + \ + encode_getattr_maxsz +#define NFS4_dec_open_reclaim_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + \ + 4 + 5 + 2 + 3 + \ + decode_getattr_maxsz #define NFS4_enc_close_sz compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 5 @@ -666,6 +676,41 @@ encode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmargs *arg) } +static int +encode_open_reclaim(struct xdr_stream *xdr, struct nfs_open_reclaimargs *arg) +{ + uint32_t *p; + + /* + * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4, + * owner 4, opentype 4, claim 4, delegation_type 4 = 44 + */ + RESERVE_SPACE(44); + WRITE32(OP_OPEN); + WRITE32(arg->seqid); + switch (arg->share_access) { + case FMODE_READ: + WRITE32(NFS4_SHARE_ACCESS_READ); + break; + case FMODE_WRITE: + WRITE32(NFS4_SHARE_ACCESS_WRITE); + break; + case FMODE_READ|FMODE_WRITE: + WRITE32(NFS4_SHARE_ACCESS_BOTH); + break; + default: + BUG(); + } + WRITE32(0); /* for linux, share_deny = 0 always */ + WRITE64(arg->clientid); + WRITE32(4); + WRITE32(arg->id); + WRITE32(NFS4_OPEN_NOCREATE); + WRITE32(NFS4_OPEN_CLAIM_PREVIOUS); + WRITE32(NFS4_OPEN_DELEGATE_NONE); + return 0; +} + static int encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh) { @@ -1058,6 +1103,32 @@ out: return status; } +/* + * Encode an OPEN request + */ +static int +nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, + struct nfs_open_reclaimargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 3, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if (status) + goto out; + status = encode_open_reclaim(&xdr, args); + if (status) + goto out; + status = encode_getattr(&xdr, args->f_getattr); +out: + return status; +} + /* * Encode a READ request @@ -2417,6 +2488,31 @@ out: return status; } +/* + * Decode OPEN_RECLAIM response + */ +static int +nfs4_xdr_dec_open_reclaim(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_open(&xdr, res); + if (status) + goto out; + status = decode_getattr(&xdr, res->f_getattr, res->server); +out: + return status; +} + /* * Decode SETATTR response */ @@ -2730,6 +2826,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(COMMIT, enc_commit, dec_commit), PROC(OPEN, enc_open, dec_open), PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), + PROC(OPEN_RECLAIM, enc_open_reclaim, dec_open_reclaim), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), PROC(FSINFO, enc_fsinfo, dec_fsinfo), diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index f56601a5825d..a6f2d563b605 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -289,6 +289,7 @@ enum { NFSPROC4_CLNT_COMMIT, NFSPROC4_CLNT_OPEN, NFSPROC4_CLNT_OPEN_CONFIRM, + NFSPROC4_CLNT_OPEN_RECLAIM, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, NFSPROC4_CLNT_FSINFO, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a005c999ce43..b43412088372 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -554,6 +554,7 @@ struct nfs4_state { /* nfs4proc.c */ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); +extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); @@ -572,6 +573,7 @@ extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); +extern void nfs4_recover_state(struct nfs4_client *); struct nfs4_mount_data; #else diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 244a430f83ef..242b50436d92 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -133,6 +133,19 @@ struct nfs_open_confirmres { nfs4_stateid stateid; }; +/* + * Arguments to the open_reclaim call. + */ +struct nfs_open_reclaimargs { + struct nfs_fh * fh; + __u64 clientid; + __u32 seqid; + __u32 id; + __u32 share_access; + __u32 claim; + struct nfs4_getattr * f_getattr; +}; + /* * Arguments to the close call. */ -- cgit v1.2.3 From dce9f3bf3f0af4a22eb06822a74e2de84f98cd57 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:58:38 +0100 Subject: NFSv4: Basic code for recovering file OPEN state after a server reboot. --- fs/nfs/inode.c | 2 + fs/nfs/nfs4proc.c | 235 ++++++++++++++++++++++++++++++++++++++++--------- fs/nfs/nfs4state.c | 129 +++++++++++++++++++++++---- include/linux/nfs_fs.h | 14 ++- 4 files changed, 317 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 2c1df6550feb..767cd32e7a36 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1448,6 +1448,8 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); } + if (list_empty(&clp->cl_superblocks)) + clear_bit(NFS4CLNT_OK, &clp->cl_state); list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); clnt = rpc_clone_client(clp->cl_rpcclient); server->nfs4_state = clp; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5e52e7d218f8..841934fafaee 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -48,9 +48,12 @@ #define NFSDBG_FACILITY NFSDBG_PROC +#define NFS4_POLL_RETRY_TIME (15*HZ) + #define GET_OP(cp,name) &cp->ops[cp->req_nops].u.name #define OPNUM(cp) cp->ops[cp->req_nops].opnum +static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *); extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); extern struct rpc_procinfo nfs4_procedures[]; @@ -532,7 +535,6 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt struct nfs_openargs o_arg = { .fh = NFS_FH(dir), .share_access = flags & (FMODE_READ|FMODE_WRITE), - .clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid, .opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE, .createmode = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED, .name = name, @@ -553,6 +555,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt .rpc_cred = cred, }; +retry: status = -ENOMEM; if (!(sp = nfs4_get_state_owner(NFS_SERVER(dir), cred))) { dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n"); @@ -569,6 +572,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt down(&sp->so_sema); o_arg.seqid = sp->so_seqid; o_arg.id = sp->so_id; + o_arg.clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid, status = rpc_call_sync(server->client, &msg, 0); nfs4_increment_seqid(status, sp); @@ -623,6 +627,9 @@ out_up: nfs4_put_open_state(state); if (inode) iput(inode); + status = nfs4_handle_error(server, status); + if (!status) + goto retry; out: return ERR_PTR(status); } @@ -651,7 +658,9 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, .rpc_argp = &arg, .rpc_resp = &res, }; + int status; +retry: fattr->valid = 0; if (state) @@ -659,7 +668,13 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, else memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); - return(rpc_call_sync(server->client, &msg, 0)); + status = rpc_call_sync(server->client, &msg, 0); + if (status) { + status = nfs4_handle_error(server, status); + if (!status) + goto retry; + } + return status; } /* @@ -707,48 +722,12 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { - struct nfs4_client *clp; struct nfs4_compound compound; struct nfs4_op ops[4]; unsigned char * p; struct qstr q; int status; - clp = server->nfs4_state; - - down_write(&clp->cl_sem); - /* Has the clientid already been initialized? */ - if (clp->cl_state != NFS4CLNT_NEW) - /* Yep, so just read the root attributes and the lease time. */ - goto no_setclientid; - - /* - * SETCLIENTID. - * Until delegations are imported, we don't bother setting the program - * number and port to anything meaningful. - */ - if ((status = nfs4_proc_setclientid(clp, 0, 0))) - goto out_unlock; - - /* - * SETCLIENTID_CONFIRM, plus root filehandle. - * We also get the lease time here. - */ - if ((status = nfs4_proc_setclientid_confirm(clp))) - goto out_unlock; - - /* - * Now that we have instantiated the clientid and determined - * the lease time, we can initialize the renew daemon for this - * server. - * FIXME: we only need one renewd daemon per server. - */ - nfs4_schedule_state_renewal(clp); - clp->cl_state = NFS4CLNT_OK; - -no_setclientid: - up_write(&clp->cl_sem); - /* * Now we do a separate LOOKUP for each component of the mount path. * The LOOKUPs are done separately so that we can conveniently @@ -787,9 +766,6 @@ no_setclientid: } break; } - return status; -out_unlock: - up_write(&clp->cl_sem); out: return status; } @@ -1410,6 +1386,20 @@ nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, return nfs4_call_compound(&compound, NULL, 0); } +static void +nfs4_restart_read(struct rpc_task *task) +{ + struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; + struct nfs_page *req; + + rpc_restart_call(task); + req = nfs_list_entry(data->pages.next); + if (req->wb_state) + memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + else + memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); +} + static void nfs4_read_done(struct rpc_task *task) { @@ -1417,6 +1407,10 @@ nfs4_read_done(struct rpc_task *task) struct inode *inode = data->inode; struct nfs_fattr *fattr = data->res.fattr; + if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { + task->tk_action = nfs4_restart_read; + return; + } if (task->tk_status > 0) renew_lease(NFS_SERVER(inode), data->timestamp); /* Check cache consistency */ @@ -1484,12 +1478,30 @@ nfs4_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_mtime = fattr->mtime; } +static void +nfs4_restart_write(struct rpc_task *task) +{ + struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata; + struct nfs_page *req; + + rpc_restart_call(task); + req = nfs_list_entry(data->pages.next); + if (req->wb_state) + memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + else + memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); +} + static void nfs4_write_done(struct rpc_task *task) { struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct inode *inode = data->inode; + if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { + task->tk_action = nfs4_restart_write; + return; + } if (task->tk_status >= 0) renew_lease(NFS_SERVER(inode), data->timestamp); nfs4_write_refresh_inode(inode, data->res.fattr); @@ -1552,8 +1564,13 @@ static void nfs4_commit_done(struct rpc_task *task) { struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct inode *inode = data->inode; - nfs4_write_refresh_inode(data->inode, data->res.fattr); + if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { + task->tk_action = nfs4_restart_write; + return; + } + nfs4_write_refresh_inode(inode, data->res.fattr); /* Call back common NFS writeback processing */ nfs_commit_done(task); } @@ -1599,6 +1616,14 @@ renew_done(struct rpc_task *task) { struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp; unsigned long timestamp = (unsigned long)task->tk_calldata; + + if (task->tk_status < 0) { + switch (task->tk_status) { + case -NFS4ERR_STALE_CLIENTID: + nfs4_schedule_state_recovery(clp); + return; + } + } spin_lock(&clp->cl_lock); if (time_before(clp->cl_last_renewal,timestamp)) clp->cl_last_renewal = timestamp; @@ -1617,6 +1642,25 @@ nfs4_proc_async_renew(struct nfs4_client *clp) return rpc_call_async(clp->cl_rpcclient, &msg, 0, renew_done, (void *)jiffies); } +int +nfs4_proc_renew(struct nfs4_client *clp) +{ + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], + .rpc_argp = clp, + .rpc_cred = clp->cl_cred, + }; + unsigned long now = jiffies; + int status; + + status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); + spin_lock(&clp->cl_lock); + if (time_before(clp->cl_last_renewal,now)) + clp->cl_last_renewal = now; + spin_unlock(&clp->cl_lock); + return status; +} + /* * We will need to arrange for the VFS layer to provide an atomic open. * Until then, this open method is prone to inefficiency and race conditions @@ -1697,6 +1741,113 @@ nfs4_request_init(struct nfs_page *req, struct file *filp) req->wb_cred = get_rpccred(state->owner->so_cred); } +static int +nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) +{ + struct nfs4_client *clp = server->nfs4_state; + + if (!clp) + return 0; + switch(task->tk_status) { + case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_EXPIRED: + rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL, NULL); + nfs4_schedule_state_recovery(clp); + task->tk_status = 0; + return -EAGAIN; + case -NFS4ERR_GRACE: + case -NFS4ERR_DELAY: + rpc_delay(task, NFS4_POLL_RETRY_TIME); + task->tk_status = 0; + return -EAGAIN; + } + return 0; +} + +int +nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) +{ + DEFINE_WAIT(wait); + sigset_t oldset; + int interruptible, res; + + might_sleep(); + + rpc_clnt_sigmask(clnt, &oldset); + interruptible = TASK_UNINTERRUPTIBLE; + if (clnt->cl_intr) + interruptible = TASK_INTERRUPTIBLE; + do { + res = 0; + prepare_to_wait(&clp->cl_waitq, &wait, interruptible); + nfs4_schedule_state_recovery(clp); + if (test_bit(NFS4CLNT_OK, &clp->cl_state) && + !test_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state)) + break; + if (clnt->cl_intr && signalled()) { + res = -ERESTARTSYS; + break; + } + schedule(); + } while(!test_bit(NFS4CLNT_OK, &clp->cl_state)); + finish_wait(&clp->cl_waitq, &wait); + rpc_clnt_sigunmask(clnt, &oldset); + return res; +} + +static int +nfs4_delay(struct rpc_clnt *clnt) +{ + sigset_t oldset; + int res = 0; + + might_sleep(); + + rpc_clnt_sigmask(clnt, &oldset); + if (clnt->cl_intr) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(NFS4_POLL_RETRY_TIME); + if (signalled()) + res = -ERESTARTSYS; + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(NFS4_POLL_RETRY_TIME); + } + rpc_clnt_sigunmask(clnt, &oldset); + return res; +} + +/* This is the error handling routine for processes that are allowed + * to sleep. + */ +int +nfs4_handle_error(struct nfs_server *server, int errorcode) +{ + struct nfs4_client *clp = server->nfs4_state; + int ret = errorcode; + + switch(errorcode) { + case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_EXPIRED: + ret = nfs4_wait_clnt_recover(server->client, clp); + break; + case -NFS4ERR_GRACE: + case -NFS4ERR_DELAY: + ret = nfs4_delay(server->client); + break; + default: + if (errorcode <= -1000) { + printk(KERN_WARNING "%s could not handle NFSv4 error %d\n", + __FUNCTION__, -errorcode); + ret = -EIO; + } + } + /* We failed to handle the error */ + return ret; +} + static int nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *page) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 8738414bd8ea..36f3f7e4c0ef 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -56,6 +56,7 @@ nfs4_stateid one_stateid = static LIST_HEAD(nfs4_clientid_list); +static void nfs4_recover_state(void *); extern void nfs4_renew_state(void *); void @@ -98,9 +99,12 @@ nfs4_alloc_client(struct in_addr *addr) INIT_LIST_HEAD(&clp->cl_unused); spin_lock_init(&clp->cl_lock); atomic_set(&clp->cl_count, 1); + INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp); INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); INIT_LIST_HEAD(&clp->cl_superblocks); - clp->cl_state = NFS4CLNT_NEW; + init_waitqueue_head(&clp->cl_waitq); + INIT_RPC_WAITQ(&clp->cl_rpcwaitq, "NFS4 client"); + clp->cl_state = 1 << NFS4CLNT_NEW; } return clp; } @@ -155,6 +159,9 @@ nfs4_put_client(struct nfs4_client *clp) return; list_del(&clp->cl_servers); spin_unlock(&state_spinlock); + BUG_ON(!list_empty(&clp->cl_superblocks)); + wake_up_all(&clp->cl_waitq); + rpc_wake_up(&clp->cl_rpcwaitq); nfs4_kill_renewd(clp); nfs4_free_client(clp); } @@ -175,6 +182,7 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) atomic_inc(&sp->so_count); sp->so_cred = cred; list_move(&sp->so_list, &clp->cl_state_owners); + sp->so_generation = clp->cl_generation; clp->cl_nunused--; } return sp; @@ -215,13 +223,17 @@ nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) new->so_client = clp; new->so_id = nfs4_alloc_lockowner_id(clp); new->so_cred = cred; + new->so_generation = clp->cl_generation; sp = new; new = NULL; } spin_unlock(&clp->cl_lock); if (new) kfree(new); - if (!sp) + if (sp) { + if (!test_bit(NFS4CLNT_OK, &clp->cl_state)) + nfs4_wait_clnt_recover(server->client, clp); + } else put_rpccred(cred); return sp; } @@ -353,6 +365,7 @@ nfs4_put_open_state(struct nfs4_state *state) { struct inode *inode = state->inode; struct nfs4_state_owner *owner = state->owner; + int status = 0; if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) return; @@ -360,8 +373,16 @@ nfs4_put_open_state(struct nfs4_state *state) spin_unlock(&inode->i_lock); down(&owner->so_sema); list_del(&state->open_states); - if (state->state != 0) - nfs4_do_close(inode, state); + if (state->state != 0) { + do { + status = nfs4_do_close(inode, state); + if (!status) + break; + up(&owner->so_sema); + status = nfs4_handle_error(NFS_SERVER(inode), status); + down(&owner->so_sema); + } while (!status); + } up(&owner->so_sema); iput(inode); nfs4_free_open_state(state); @@ -392,41 +413,81 @@ struct reclaimer_args { * State recovery routine */ void -nfs4_recover_state(struct nfs4_client *clp) +nfs4_recover_state(void *data) { + struct nfs4_client *clp = (struct nfs4_client *)data; struct reclaimer_args args = { .clp = clp, }; + might_sleep(); + init_completion(&args.complete); down_read(&clp->cl_sem); - if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) + if (test_and_set_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state)) goto out_failed; + if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) + goto out_failed_clear; wait_for_completion(&args.complete); return; +out_failed_clear: + smp_mb__before_clear_bit(); + clear_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state); + smp_mb__after_clear_bit(); + wake_up_all(&clp->cl_waitq); + rpc_wake_up(&clp->cl_rpcwaitq); out_failed: up_read(&clp->cl_sem); } -static void +/* + * Schedule a state recovery attempt + */ +void +nfs4_schedule_state_recovery(struct nfs4_client *clp) +{ + if (!clp) + return; + smp_mb__before_clear_bit(); + clear_bit(NFS4CLNT_OK, &clp->cl_state); + smp_mb__after_clear_bit(); + schedule_work(&clp->cl_recoverd); +} + +static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp) { struct nfs4_state *state; - int status; + int status = 0; list_for_each_entry(state, &sp->so_states, open_states) { status = nfs4_open_reclaim(sp, state); - if (status) { - /* - * Open state on this file cannot be recovered - * All we can do is revert to using the zero stateid. - */ - memset(state->stateid.data, 0, + if (status >= 0) + continue; + switch (status) { + default: + printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", + __FUNCTION__, status); + case -NFS4ERR_EXPIRED: + case -NFS4ERR_NO_GRACE: + case -NFS4ERR_RECLAIM_BAD: + case -NFS4ERR_RECLAIM_CONFLICT: + /* + * Open state on this file cannot be recovered + * All we can do is revert to using the zero stateid. + */ + memset(state->stateid.data, 0, sizeof(state->stateid.data)); - /* Mark the file as being 'closed' */ - state->state = 0; + /* Mark the file as being 'closed' */ + state->state = 0; + break; + case -NFS4ERR_STALE_CLIENTID: + goto out_err; } } + return 0; +out_err: + return status; } static int @@ -435,6 +496,7 @@ reclaimer(void *ptr) struct reclaimer_args *args = (struct reclaimer_args *)ptr; struct nfs4_client *clp = args->clp; struct nfs4_state_owner *sp; + int generation; int status; daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); @@ -445,29 +507,58 @@ reclaimer(void *ptr) /* Are there any NFS mounts out there? */ if (list_empty(&clp->cl_superblocks)) goto out; + if (!test_bit(NFS4CLNT_NEW, &clp->cl_state)) { + status = nfs4_proc_renew(clp); + if (status == 0) { + set_bit(NFS4CLNT_OK, &clp->cl_state); + goto out; + } + } status = nfs4_proc_setclientid(clp, 0, 0); if (status) goto out_error; status = nfs4_proc_setclientid_confirm(clp); if (status) goto out_error; + generation = ++(clp->cl_generation); + clear_bit(NFS4CLNT_NEW, &clp->cl_state); + set_bit(NFS4CLNT_OK, &clp->cl_state); + up_read(&clp->cl_sem); + nfs4_schedule_state_renewal(clp); +restart_loop: spin_lock(&clp->cl_lock); list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + if (sp->so_generation - generation <= 0) + continue; atomic_inc(&sp->so_count); spin_unlock(&clp->cl_lock); down(&sp->so_sema); - nfs4_reclaim_open_state(sp); + if (sp->so_generation - generation < 0) { + smp_rmb(); + sp->so_generation = clp->cl_generation; + status = nfs4_reclaim_open_state(sp); + } up(&sp->so_sema); nfs4_put_state_owner(sp); - spin_lock(&clp->cl_lock); + if (status < 0) { + if (status == -NFS4ERR_STALE_CLIENTID) + nfs4_schedule_state_recovery(clp); + goto out; + } + goto restart_loop; } spin_unlock(&clp->cl_lock); out: - up_read(&clp->cl_sem); + smp_mb__before_clear_bit(); + clear_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state); + smp_mb__after_clear_bit(); + wake_up_all(&clp->cl_waitq); + rpc_wake_up(&clp->cl_rpcwaitq); return 0; out_error: printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u\n", NIPQUAD(clp->cl_addr.s_addr)); + up_read(&clp->cl_sem); goto out; } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index b43412088372..3efc91b200e6 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -465,6 +465,7 @@ extern void * nfs_root_data(void); enum nfs4_client_state { NFS4CLNT_OK = 0, NFS4CLNT_NEW, + NFS4CLNT_SETUP_STATE, }; /* @@ -475,7 +476,8 @@ struct nfs4_client { struct in_addr cl_addr; /* Server identifier */ u64 cl_clientid; /* constant */ nfs4_verifier cl_confirm; - enum nfs4_client_state cl_state; + unsigned long cl_state; + long cl_generation; u32 cl_lockowner_id; @@ -499,6 +501,10 @@ struct nfs4_client { unsigned long cl_lease_time; unsigned long cl_last_renewal; struct work_struct cl_renewd; + struct work_struct cl_recoverd; + + wait_queue_head_t cl_waitq; + struct rpc_wait_queue cl_rpcwaitq; /* Our own IP address, as a null-terminated string. * This is used to generate the clientid, and the callback address. @@ -523,6 +529,7 @@ struct nfs4_state_owner { u32 so_seqid; /* protected by so_sema */ unsigned int so_flags; /* protected by so_sema */ atomic_t so_count; + long so_generation; struct rpc_cred *so_cred; /* Associated cred */ struct list_head so_states; @@ -556,7 +563,9 @@ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); +extern int nfs4_proc_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); +extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs4_client *); @@ -573,7 +582,8 @@ extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); -extern void nfs4_recover_state(struct nfs4_client *); +extern int nfs4_handle_error(struct nfs_server *, int); +extern void nfs4_schedule_state_recovery(struct nfs4_client *); struct nfs4_mount_data; #else -- cgit v1.2.3 From 2642498f5cd2b90e55736e8cfd312c05f63301e3 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:59:20 +0100 Subject: RPC/NFSv4: Allow lease RENEW calls to be soft (i.e. to time out) despite the mount being hard. --- fs/nfs/nfs4proc.c | 3 ++- include/linux/sunrpc/sched.h | 2 ++ net/sunrpc/clnt.c | 4 ++-- net/sunrpc/sched.c | 5 ++++- net/sunrpc/xprt.c | 6 +++--- 5 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 841934fafaee..f617d0bf5313 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1639,7 +1639,8 @@ nfs4_proc_async_renew(struct nfs4_client *clp) .rpc_cred = clp->cl_cred, }; - return rpc_call_async(clp->cl_rpcclient, &msg, 0, renew_done, (void *)jiffies); + return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT, + renew_done, (void *)jiffies); } int diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 1113d7f3df13..6b8e3eb91513 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -108,6 +108,7 @@ typedef void (*rpc_action)(struct rpc_task *); #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */ #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */ #define RPC_TASK_KILLED 0x0100 /* task was killed */ +#define RPC_TASK_SOFT 0x0200 /* Use soft timeouts */ #define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC) #define RPC_IS_SETUID(t) ((t)->tk_flags & RPC_TASK_SETUID) @@ -117,6 +118,7 @@ typedef void (*rpc_action)(struct rpc_task *); #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) #define RPC_IS_ACTIVATED(t) ((t)->tk_active) #define RPC_DO_CALLBACK(t) ((t)->tk_callback != NULL) +#define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT) #define RPC_TASK_SLEEPING 0 #define RPC_TASK_RUNNING 1 diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 6c6a8310000a..b41434a36009 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -798,7 +798,7 @@ call_timeout(struct rpc_task *task) to->to_retries = clnt->cl_timeout.to_retries; dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid); - if (clnt->cl_softrtry) { + if (RPC_IS_SOFT(task)) { if (clnt->cl_chatty) printk(KERN_NOTICE "%s: server %s not responding, timed out\n", clnt->cl_protname, clnt->cl_server); @@ -841,7 +841,7 @@ call_decode(struct rpc_task *task) } if (task->tk_status < 12) { - if (!clnt->cl_softrtry) { + if (!RPC_IS_SOFT(task)) { task->tk_action = call_bind; clnt->cl_stats->rpcretrans++; goto out_retry; diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 79ebf10e03d9..ad56eb17e80f 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -731,8 +731,11 @@ rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, list_add(&task->tk_task, &all_tasks); spin_unlock(&rpc_sched_lock); - if (clnt) + if (clnt) { atomic_inc(&clnt->cl_users); + if (clnt->cl_softrtry) + task->tk_flags |= RPC_TASK_SOFT; + } #ifdef RPC_DEBUG task->tk_magic = 0xf00baa; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index e6c5f7ab7968..aa93b824433d 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -488,7 +488,7 @@ xprt_connect(struct rpc_task *task) case -ECONNREFUSED: case -ECONNRESET: case -ENOTCONN: - if (!task->tk_client->cl_softrtry) { + if (!RPC_IS_SOFT(task)) { rpc_delay(task, RPC_REESTABLISH_TIMEOUT); task->tk_status = -ENOTCONN; break; @@ -496,7 +496,7 @@ xprt_connect(struct rpc_task *task) default: /* Report myriad other possible returns. If this file * system is soft mounted, just error out, like Solaris. */ - if (task->tk_client->cl_softrtry) { + if (RPC_IS_SOFT(task)) { printk(KERN_WARNING "RPC: error %d connecting to server %s, exiting\n", -status, task->tk_client->cl_server); @@ -530,7 +530,7 @@ xprt_connect_status(struct rpc_task *task) } /* if soft mounted, just cause this RPC to fail */ - if (task->tk_client->cl_softrtry) + if (RPC_IS_SOFT(task)) task->tk_status = -EIO; switch (task->tk_status) { -- cgit v1.2.3 From ab91d13dcba1d0b69394fa1b0a3b034db71316ca Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:00:03 +0100 Subject: RPC: Ensure that we disconnect TCP sockets if there has been no NFS traffic for the last 5 minutes. This code also affects NFSv2/v3. --- include/linux/sunrpc/xprt.h | 7 +++ net/sunrpc/xprt.c | 106 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 90 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 8472b1c5ad2e..393e6dc6a268 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -163,6 +163,12 @@ struct rpc_xprt { tcp_offset; /* fragment offset */ unsigned long tcp_copied, /* copied to request */ tcp_flags; + /* + * Disconnection of idle sockets + */ + struct work_struct task_cleanup; + struct timer_list timer; + unsigned long last_used; /* * Send stuff @@ -202,6 +208,7 @@ int xprt_clear_backlog(struct rpc_xprt *); void xprt_sock_setbufsize(struct rpc_xprt *); #define XPRT_CONNECT 0 +#define XPRT_LOCKED 1 #define xprt_connected(xp) (test_bit(XPRT_CONNECT, &(xp)->sockstate)) #define xprt_set_connected(xp) (set_bit(XPRT_CONNECT, &(xp)->sockstate)) diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index aa93b824433d..84ccc9d8e05b 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,7 @@ #endif #define XPRT_MAX_BACKOFF (8) +#define XPRT_IDLE_TIMEOUT (5*60*HZ) /* * Local functions @@ -139,25 +141,33 @@ __xprt_lock_write(struct rpc_xprt *xprt, struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; - if (!xprt->snd_task) { - if (xprt->nocong || __xprt_get_cong(xprt, task)) { - xprt->snd_task = task; - if (req) { - req->rq_bytes_sent = 0; - req->rq_ntrans++; - } - } + if (test_and_set_bit(XPRT_LOCKED, &xprt->sockstate)) { + if (task == xprt->snd_task) + return 1; + if (task == NULL) + return 0; + goto out_sleep; } - if (xprt->snd_task != task) { - dprintk("RPC: %4d TCP write queue full\n", task->tk_pid); - task->tk_timeout = 0; - task->tk_status = -EAGAIN; - if (req && req->rq_ntrans) - rpc_sleep_on(&xprt->resend, task, NULL, NULL); - else - rpc_sleep_on(&xprt->sending, task, NULL, NULL); + if (xprt->nocong || __xprt_get_cong(xprt, task)) { + xprt->snd_task = task; + if (req) { + req->rq_bytes_sent = 0; + req->rq_ntrans++; + } + return 1; } - return xprt->snd_task == task; + smp_mb__before_clear_bit(); + clear_bit(XPRT_LOCKED, &xprt->sockstate); + smp_mb__after_clear_bit(); +out_sleep: + dprintk("RPC: %4d failed to lock socket %p\n", task->tk_pid, xprt); + task->tk_timeout = 0; + task->tk_status = -EAGAIN; + if (req && req->rq_ntrans) + rpc_sleep_on(&xprt->resend, task, NULL, NULL); + else + rpc_sleep_on(&xprt->sending, task, NULL, NULL); + return 0; } static inline int @@ -177,15 +187,15 @@ __xprt_lock_write_next(struct rpc_xprt *xprt) { struct rpc_task *task; - if (xprt->snd_task) + if (test_and_set_bit(XPRT_LOCKED, &xprt->sockstate)) return; + if (!xprt->nocong && RPCXPRT_CONGESTED(xprt)) + goto out_unlock; task = rpc_wake_up_next(&xprt->resend); if (!task) { - if (!xprt->nocong && RPCXPRT_CONGESTED(xprt)) - return; task = rpc_wake_up_next(&xprt->sending); if (!task) - return; + goto out_unlock; } if (xprt->nocong || __xprt_get_cong(xprt, task)) { struct rpc_rqst *req = task->tk_rqstp; @@ -194,7 +204,12 @@ __xprt_lock_write_next(struct rpc_xprt *xprt) req->rq_bytes_sent = 0; req->rq_ntrans++; } + return; } +out_unlock: + smp_mb__before_clear_bit(); + clear_bit(XPRT_LOCKED, &xprt->sockstate); + smp_mb__after_clear_bit(); } /* @@ -203,9 +218,13 @@ __xprt_lock_write_next(struct rpc_xprt *xprt) static void __xprt_release_write(struct rpc_xprt *xprt, struct rpc_task *task) { - if (xprt->snd_task == task) + if (xprt->snd_task == task) { xprt->snd_task = NULL; - __xprt_lock_write_next(xprt); + smp_mb__before_clear_bit(); + clear_bit(XPRT_LOCKED, &xprt->sockstate); + smp_mb__after_clear_bit(); + __xprt_lock_write_next(xprt); + } } static inline void @@ -393,6 +412,15 @@ xprt_close(struct rpc_xprt *xprt) sock_release(sock); } +static void +xprt_socket_autoclose(void *args) +{ + struct rpc_xprt *xprt = (struct rpc_xprt *)args; + + xprt_close(xprt); + xprt_release_write(xprt, NULL); +} + /* * Mark a transport as disconnected */ @@ -406,6 +434,27 @@ xprt_disconnect(struct rpc_xprt *xprt) spin_unlock_bh(&xprt->sock_lock); } +/* + * Used to allow disconnection when we've been idle + */ +static void +xprt_init_autodisconnect(unsigned long data) +{ + struct rpc_xprt *xprt = (struct rpc_xprt *)data; + + spin_lock(&xprt->sock_lock); + if (!list_empty(&xprt->recv) || xprt->shutdown) + goto out_abort; + if (test_and_set_bit(XPRT_LOCKED, &xprt->sockstate)) + goto out_abort; + spin_unlock(&xprt->sock_lock); + /* Let keventd close the socket */ + schedule_work(&xprt->task_cleanup); + return; +out_abort: + spin_unlock(&xprt->sock_lock); +} + /* * Attempt to connect a TCP socket. * @@ -1254,6 +1303,8 @@ xprt_reserve(struct rpc_task *task) spin_lock(&xprt->xprt_lock); do_xprt_reserve(task); spin_unlock(&xprt->xprt_lock); + if (task->tk_rqstp) + del_timer_sync(&xprt->timer); } } @@ -1333,6 +1384,9 @@ xprt_release(struct rpc_task *task) __xprt_put_cong(xprt, req); if (!list_empty(&req->rq_list)) list_del(&req->rq_list); + xprt->last_used = jiffies; + if (list_empty(&xprt->recv) && !xprt->shutdown) + mod_timer(&xprt->timer, xprt->last_used + XPRT_IDLE_TIMEOUT); spin_unlock_bh(&xprt->sock_lock); task->tk_rqstp = NULL; memset(req, 0, sizeof(*req)); /* mark unused */ @@ -1403,6 +1457,11 @@ xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) init_waitqueue_head(&xprt->cong_wait); INIT_LIST_HEAD(&xprt->recv); + INIT_WORK(&xprt->task_cleanup, xprt_socket_autoclose, xprt); + init_timer(&xprt->timer); + xprt->timer.function = xprt_init_autodisconnect; + xprt->timer.data = (unsigned long) xprt; + xprt->last_used = jiffies; /* Set timeout parameters */ if (to) { @@ -1583,6 +1642,7 @@ xprt_shutdown(struct rpc_xprt *xprt) rpc_wake_up(&xprt->backlog); if (waitqueue_active(&xprt->cong_wait)) wake_up(&xprt->cong_wait); + del_timer_sync(&xprt->timer); } /* -- cgit v1.2.3 From 5bb0bc7c89ec7d694cd77487fe26f004ee0d0bb7 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:00:45 +0100 Subject: NFSv4: Atomic open(). Fixes races w.r.t. opening files. --- fs/nfs/dir.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++- fs/nfs/inode.c | 54 +++++++++++++++++-- fs/nfs/nfs3proc.c | 2 + fs/nfs/nfs4proc.c | 134 ++++++++++++++++++++++++++++++++-------------- fs/nfs/nfs4state.c | 2 - fs/nfs/proc.c | 2 + include/linux/nfs_fs.h | 6 +++ include/linux/nfs_xdr.h | 2 + 8 files changed, 295 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 0796bd6a3ac0..1adc5bfea4f6 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -72,6 +72,26 @@ struct inode_operations nfs_dir_inode_operations = { .setattr = nfs_setattr, }; +#ifdef CONFIG_NFS_V4 + +static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); +struct inode_operations nfs4_dir_inode_operations = { + .create = nfs_create, + .lookup = nfs_atomic_lookup, + .link = nfs_link, + .unlink = nfs_unlink, + .symlink = nfs_symlink, + .mkdir = nfs_mkdir, + .rmdir = nfs_rmdir, + .mknod = nfs_mknod, + .rename = nfs_rename, + .permission = nfs_permission, + .getattr = nfs_getattr, + .setattr = nfs_setattr, +}; + +#endif /* CONFIG_NFS_V4 */ + /* * Open file */ @@ -670,7 +690,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru goto out; error = -ENOMEM; - dentry->d_op = &nfs_dentry_operations; + dentry->d_op = NFS_PROTO(dir)->dentry_ops; lock_kernel(); @@ -702,6 +722,119 @@ out: return ERR_PTR(error); } +#ifdef CONFIG_NFS_V4 +static int nfs_open_revalidate(struct dentry *, struct nameidata *); + +struct dentry_operations nfs4_dentry_operations = { + .d_revalidate = nfs_open_revalidate, + .d_delete = nfs_dentry_delete, + .d_iput = nfs_dentry_iput, +}; + +static int is_atomic_open(struct inode *dir, struct nameidata *nd) +{ + if (!nd) + return 0; + /* Check that we are indeed trying to open this file */ + if ((nd->flags & LOOKUP_CONTINUE) || !(nd->flags & LOOKUP_OPEN)) + return 0; + /* NFS does not (yet) have a stateful open for directories */ + if (nd->flags & LOOKUP_DIRECTORY) + return 0; + /* Are we trying to write to a read only partition? */ + if (IS_RDONLY(dir) && (nd->intent.open.flags & (O_CREAT|O_TRUNC|FMODE_WRITE))) + return 0; + return 1; +} + +static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct inode *inode = NULL; + int error = 0; + + /* Check that we are indeed trying to open this file */ + if (!is_atomic_open(dir, nd)) + goto no_open; + + if (dentry->d_name.len > NFS_SERVER(dir)->namelen) { + error = -ENAMETOOLONG; + goto out; + } + dentry->d_op = NFS_PROTO(dir)->dentry_ops; + + /* Let vfs_create() deal with O_EXCL */ + if (nd->intent.open.flags & O_EXCL) + goto no_entry; + + /* Open the file on the server */ + lock_kernel(); + inode = nfs4_atomic_open(dir, dentry, nd); + unlock_kernel(); + if (IS_ERR(inode)) { + error = PTR_ERR(inode); + switch (error) { + /* Make a negative dentry */ + case -ENOENT: + inode = NULL; + break; + /* This turned out not to be a regular file */ + case -ELOOP: + if (!(nd->intent.open.flags & O_NOFOLLOW)) + goto no_open; + /* case -EISDIR: */ + /* case -EINVAL: */ + default: + goto out; + } + } +no_entry: + d_add(dentry, inode); + nfs_renew_times(dentry); +out: + BUG_ON(error > 0); + return ERR_PTR(error); +no_open: + return nfs_lookup(dir, dentry, nd); +} + +static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + struct dentry *parent = NULL; + struct inode *inode = dentry->d_inode; + int openflags, ret = 0; + + /* NFS only supports OPEN for regular files */ + if (inode && !S_ISREG(inode->i_mode)) + goto no_open; + parent = dget_parent(dentry); + if (!is_atomic_open(parent->d_inode, nd)) + goto no_open; + openflags = nd->intent.open.flags; + if (openflags & O_CREAT) { + /* If this is a negative dentry, just drop it */ + if (!inode) + goto out; + /* If this is exclusive open, just revalidate */ + if (openflags & O_EXCL) + goto no_open; + } + /* We can't create new files, or truncate existing ones here */ + openflags &= ~(O_CREAT|O_TRUNC); + + lock_kernel(); + ret = nfs4_open_revalidate(parent->d_inode, dentry, openflags); + unlock_kernel(); +out: + dput(parent); + if (!ret) + d_drop(dentry); + return ret; +no_open: + dput(parent); + return nfs_lookup_revalidate(dentry, nd); +} +#endif /* CONFIG_NFSV4 */ + static inline int find_dirent_name(nfs_readdir_descriptor_t *desc, struct page *page, struct dentry *dentry) { @@ -1306,6 +1439,9 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd) /* We only need to check permissions on file open() and access() */ if (!nd || !(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS))) return 0; + /* NFSv4 has atomic_open... */ + if (NFS_PROTO(inode)->version > 3 && (nd->flags & LOOKUP_OPEN)) + return 0; } lock_kernel(); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 767cd32e7a36..276f3a10298b 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -303,7 +303,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) server = NFS_SB(sb); sb->s_magic = NFS_SUPER_MAGIC; - sb->s_op = &nfs_sops; /* Did getting the root inode fail? */ if (nfs_get_root(&root_inode, authflavor, sb, &server->fh) < 0) @@ -312,7 +311,7 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) if (!sb->s_root) goto out_no_root; - sb->s_root->d_op = &nfs_dentry_operations; + sb->s_root->d_op = server->rpc_ops->dentry_ops; /* Get some general file system info */ if (server->rpc_ops->fsinfo(server, &server->fh, &fsinfo) < 0) { @@ -513,6 +512,7 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) goto out_shutdown; } + sb->s_op = &nfs_sops; err = nfs_sb_init(sb, authflavor); if (err != 0) goto out_noinit; @@ -745,7 +745,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) inode->i_data.a_ops = &nfs_file_aops; inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info; } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &nfs_dir_inode_operations; + inode->i_op = NFS_SB(sb)->rpc_ops->dir_inode_ops; inode->i_fop = &nfs_dir_operations; if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS) && fattr->size <= NFS_LIMIT_READDIRPLUS) @@ -837,7 +837,12 @@ printk("nfs_setattr: revalidate failed, error=%d\n", error); filemap_fdatawait(inode->i_mapping); if (error) goto out; + /* Optimize away unnecessary truncates */ + if ((attr->ia_valid & ATTR_SIZE) && i_size_read(inode) == attr->ia_size) + attr->ia_valid &= ~ATTR_SIZE; } + if (!attr->ia_valid) + goto out; error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr); if (error) @@ -1357,6 +1362,48 @@ static struct file_system_type nfs_fs_type = { #ifdef CONFIG_NFS_V4 +static void nfs4_clear_inode(struct inode *); + +static struct super_operations nfs4_sops = { + .alloc_inode = nfs_alloc_inode, + .destroy_inode = nfs_destroy_inode, + .write_inode = nfs_write_inode, + .delete_inode = nfs_delete_inode, + .put_super = nfs_put_super, + .statfs = nfs_statfs, + .clear_inode = nfs4_clear_inode, + .umount_begin = nfs_umount_begin, + .show_options = nfs_show_options, +}; + +/* + * Clean out any remaining NFSv4 state that might be left over due + * to open() calls that passed nfs_atomic_lookup, but failed to call + * nfs_open(). + */ +static void nfs4_clear_inode(struct inode *inode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + while (!list_empty(&nfsi->open_states)) { + struct nfs4_state *state; + + state = list_entry(nfsi->open_states.next, + struct nfs4_state, + inode_states); + dprintk("%s(%s/%Ld): found unclaimed NFSv4 state %p\n", + __FUNCTION__, + inode->i_sb->s_id, + (long long)NFS_FILEID(inode), + state); + list_del(&state->inode_states); + nfs4_put_open_state(state); + } + /* Now call standard NFS clear_inode() code */ + nfs_clear_inode(inode); +} + + static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) { struct nfs_server *server; @@ -1481,6 +1528,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, if ((server->idmap = nfs_idmap_new(server)) == NULL) printk(KERN_WARNING "NFS: couldn't start IDmap\n"); + sb->s_op = &nfs4_sops; err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 0ca8bbc17e13..e5b2ad2f8623 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -898,6 +898,8 @@ nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ + .dentry_ops = &nfs_dentry_operations, + .dir_inode_ops = &nfs_dir_inode_operations, .getroot = nfs3_proc_get_root, .getattr = nfs3_proc_getattr, .setattr = nfs3_proc_setattr, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f617d0bf5313..a6ac022add44 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -45,6 +45,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_PROC @@ -509,6 +510,9 @@ nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) return status; } +/* + * Returns an nfs4_state + an referenced inode + */ struct nfs4_state * nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred) { @@ -617,19 +621,23 @@ retry: up(&sp->so_sema); nfs4_put_state_owner(sp); - iput(inode); return state; out_up: up(&sp->so_sema); nfs4_put_state_owner(sp); - if (state) + if (state) { nfs4_put_open_state(state); - if (inode) + state = NULL; + } + if (inode) { iput(inode); + inode = NULL; + } status = nfs4_handle_error(server, status); if (!status) goto retry; + BUG_ON(status < -1000 || status > 0); out: return ERR_PTR(status); } @@ -718,6 +726,56 @@ nfs4_do_close(struct inode *inode, struct nfs4_state *state) return status; } +struct inode * +nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct iattr attr; + struct rpc_cred *cred; + struct nfs4_state *state; + + if (nd->flags & LOOKUP_CREATE) { + attr.ia_mode = nd->intent.open.create_mode; + attr.ia_valid = ATTR_MODE; + if (!IS_POSIXACL(dir)) + attr.ia_mode &= ~current->fs->umask; + } else { + attr.ia_valid = 0; + BUG_ON(nd->intent.open.flags & O_CREAT); + } + + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + state = nfs4_do_open(dir, &dentry->d_name, nd->intent.open.flags, &attr, cred); + put_rpccred(cred); + if (IS_ERR(state)) + return (struct inode *)state; + return state->inode; +} + +int +nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags) +{ + struct rpc_cred *cred; + struct nfs4_state *state; + struct inode *inode; + + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred); + put_rpccred(cred); + if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0) + return 1; + if (IS_ERR(state)) + return 0; + inode = state->inode; + if (inode == dentry->d_inode) { + iput(inode); + return 1; + } + d_drop(dentry); + nfs4_put_open_state(state); + iput(inode); + return 0; +} + static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) @@ -808,28 +866,39 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, struct inode * inode = dentry->d_inode; int size_change = sattr->ia_valid & ATTR_SIZE; struct nfs4_state *state = NULL; - int status; + int need_iput = 0; + int status; fattr->valid = 0; if (size_change) { - struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); - state = nfs4_do_open(dentry->d_parent->d_inode, + state = nfs4_find_state_bypid(inode, current->pid); + + if (!state) { + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_do_open(dentry->d_parent->d_inode, &dentry->d_name, FMODE_WRITE, NULL, cred); - put_rpccred(cred); + put_rpccred(cred); + need_iput = 1; + } if (IS_ERR(state)) return PTR_ERR(state); if (state->inode != inode) { - printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n"); - nfs4_put_open_state(state); - return -EIO; + printk(KERN_WARNING "nfs: raced in setattr (%p != %p), returning -EIO\n", inode, state->inode); + status = -EIO; + goto out; } } status = nfs4_do_setattr(NFS_SERVER(inode), fattr, NFS_FH(inode), sattr, state); - if (state) +out: + if (state) { + inode = state->inode; nfs4_put_open_state(state); + if (need_iput) + iput(inode); + } return status; } @@ -1085,18 +1154,18 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, state = nfs4_do_open(dir, name, flags, sattr, cred); put_rpccred(cred); if (!IS_ERR(state)) { - inode = igrab(state->inode); + inode = state->inode; if (flags & O_EXCL) { struct nfs_fattr fattr; int status; status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, NFS_FH(inode), sattr, state); if (status != 0) { + nfs4_put_open_state(state); iput(inode); inode = ERR_PTR(status); } } - nfs4_put_open_state(state); } else inode = (struct inode *)state; return inode; @@ -1672,43 +1741,28 @@ static int nfs4_proc_file_open(struct inode *inode, struct file *filp) { struct dentry *dentry = filp->f_dentry; - struct inode *dir = dentry->d_parent->d_inode; - struct rpc_cred *cred; struct nfs4_state *state; - int flags = filp->f_flags; - int status = 0; dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n", (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int)dentry->d_name.len, dentry->d_name.name); - if ((flags + 1) & O_ACCMODE) - flags++; - - lock_kernel(); -/* -* We have already opened the file "O_EXCL" in nfs4_proc_create!! -* This ugliness will go away with lookup-intent... -*/ - cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); - state = nfs4_do_open(dir, &dentry->d_name, flags, NULL, cred); - if (IS_ERR(state)) { - status = PTR_ERR(state); - state = NULL; - } else if (filp->f_mode & FMODE_WRITE) - nfs_set_mmcred(inode, cred); - if (inode != filp->f_dentry->d_inode) { + /* Find our open stateid */ + state = nfs4_find_state_bypid(inode, current->pid); + if (state == NULL) { printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__); - status = -EIO; /* ERACE actually */ - nfs4_put_open_state(state); - state = NULL; + return -EIO; /* ERACE actually */ + } + nfs4_put_open_state(state); + if (filp->f_mode & FMODE_WRITE) { + lock_kernel(); + nfs_set_mmcred(inode, state->owner->so_cred); + unlock_kernel(); } filp->private_data = state; - put_rpccred(cred); - unlock_kernel(); - return status; + return 0; } /* @@ -1922,6 +1976,8 @@ nfs4_proc_setclientid_confirm(struct nfs4_client *clp) struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ + .dentry_ops = &nfs4_dentry_operations, + .dir_inode_ops = &nfs4_dir_inode_operations, .getroot = nfs4_proc_get_root, .getattr = nfs4_proc_getattr, .setattr = nfs4_proc_setattr, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 36f3f7e4c0ef..333daf5f48e7 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -349,7 +349,6 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner) atomic_inc(&owner->so_count); list_add(&state->inode_states, &nfsi->open_states); state->inode = inode; - atomic_inc(&inode->i_count); spin_unlock(&inode->i_lock); } else { spin_unlock(&inode->i_lock); @@ -384,7 +383,6 @@ nfs4_put_open_state(struct nfs4_state *state) } while (!status); } up(&owner->so_sema); - iput(inode); nfs4_free_open_state(state); nfs4_put_state_owner(owner); } diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 6570e719ee54..79f18e4cfb81 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -656,6 +656,8 @@ nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *pag struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ + .dentry_ops = &nfs_dentry_operations, + .dir_inode_ops = &nfs_dir_inode_operations, .getroot = nfs_proc_get_root, .getattr = nfs_proc_getattr, .setattr = nfs_proc_setattr, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 3efc91b200e6..0605e9c63026 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -558,6 +558,9 @@ struct nfs4_state { }; +extern struct dentry_operations nfs4_dentry_operations; +extern struct inode_operations nfs4_dir_inode_operations; + /* nfs4proc.c */ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); @@ -566,6 +569,8 @@ extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_proc_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); +extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); +extern int nfs4_open_revalidate(struct inode *, struct dentry *, int); /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs4_client *); @@ -581,6 +586,7 @@ extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struc extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); +extern struct nfs4_state *nfs4_find_state_bypid(struct inode *, pid_t); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); extern int nfs4_handle_error(struct nfs_server *, int); extern void nfs4_schedule_state_recovery(struct nfs4_client *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 242b50436d92..4393ae7c305d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -637,6 +637,8 @@ struct nfs_page; */ struct nfs_rpc_ops { int version; /* Protocol version */ + struct dentry_operations *dentry_ops; + struct inode_operations *dir_inode_ops; int (*getroot) (struct nfs_server *, struct nfs_fh *, struct nfs_fattr *); -- cgit v1.2.3 From c04e88dbd89ec891a252ffe09378310dd1d18546 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:01:32 +0100 Subject: NFSv4: Share open_owner structs between several different processes. Reduces the load on the server. --- fs/nfs/nfs4proc.c | 80 +++++++++++++++++++++++++++----- fs/nfs/nfs4state.c | 118 ++++++++++++++++++++++++++++++++++++++++++------ fs/nfs/nfs4xdr.c | 82 +++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 1 + include/linux/nfs_fs.h | 7 ++- include/linux/nfs_xdr.h | 1 + 6 files changed, 263 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a6ac022add44..10f67569de73 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -616,8 +616,13 @@ retry: memcpy(&state->stateid, &oc_res.stateid, sizeof(state->stateid)); } else memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); + spin_lock(&inode->i_lock); + if (flags & FMODE_READ) + state->nreaders++; + if (flags & FMODE_WRITE) + state->nwriters++; state->state |= flags & (FMODE_READ|FMODE_WRITE); - state->pid = current->pid; + spin_unlock(&inode->i_lock); up(&sp->so_sema); nfs4_put_state_owner(sp); @@ -634,6 +639,21 @@ out_up: iput(inode); inode = NULL; } + /* NOTE: BAD_SEQID means the server and client disagree about the + * book-keeping w.r.t. state-changing operations + * (OPEN/CLOSE/LOCK/LOCKU...) + * It is actually a sign of a bug on the client or on the server. + * + * If we receive a BAD_SEQID error in the particular case of + * doing an OPEN, we assume that nfs4_increment_seqid() will + * have unhashed the old state_owner for us, and that we can + * therefore safely retry using a new one. We should still warn + * the user though... + */ + if (status == -NFS4ERR_BAD_SEQID) { + printk(KERN_WARNING "NFS: v4 server returned a bad sequence-id error!\n"); + goto retry; + } status = nfs4_handle_error(server, status); if (!status) goto retry; @@ -722,6 +742,36 @@ nfs4_do_close(struct inode *inode, struct nfs4_state *state) * the state_owner. we keep this around to process errors */ nfs4_increment_seqid(status, sp); + if (!status) + memcpy(&state->stateid, &res.stateid, sizeof(state->stateid)); + + return status; +} + +int +nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) +{ + struct nfs4_state_owner *sp = state->owner; + int status = 0; + struct nfs_closeargs arg = { + .fh = NFS_FH(inode), + .seqid = sp->so_seqid, + .share_access = mode, + }; + struct nfs_closeres res = { + .status = 0, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE], + .rpc_argp = &arg, + .rpc_resp = &res, + }; + + memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid)); + status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0); + nfs4_increment_seqid(status, sp); + if (!status) + memcpy(&state->stateid, &res.stateid, sizeof(state->stateid)); return status; } @@ -771,7 +821,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags) return 1; } d_drop(dentry); - nfs4_put_open_state(state); + nfs4_close_state(state, openflags); iput(inode); return 0; } @@ -872,15 +922,14 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, fattr->valid = 0; if (size_change) { - state = nfs4_find_state_bypid(inode, current->pid); - + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_find_state(inode, cred, FMODE_WRITE); if (!state) { - struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); state = nfs4_do_open(dentry->d_parent->d_inode, &dentry->d_name, FMODE_WRITE, NULL, cred); - put_rpccred(cred); need_iput = 1; } + put_rpccred(cred); if (IS_ERR(state)) return PTR_ERR(state); @@ -895,7 +944,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, out: if (state) { inode = state->inode; - nfs4_put_open_state(state); + nfs4_close_state(state, FMODE_WRITE); if (need_iput) iput(inode); } @@ -1161,7 +1210,7 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, NFS_FH(inode), sattr, state); if (status != 0) { - nfs4_put_open_state(state); + nfs4_close_state(state, flags); iput(inode); inode = ERR_PTR(status); } @@ -1742,6 +1791,7 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) { struct dentry *dentry = filp->f_dentry; struct nfs4_state *state; + struct rpc_cred *cred; dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n", (int)dentry->d_parent->d_name.len, @@ -1750,12 +1800,14 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) /* Find our open stateid */ - state = nfs4_find_state_bypid(inode, current->pid); + cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_find_state(inode, cred, filp->f_mode); + put_rpccred(cred); if (state == NULL) { printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__); return -EIO; /* ERACE actually */ } - nfs4_put_open_state(state); + nfs4_close_state(state, filp->f_mode); if (filp->f_mode & FMODE_WRITE) { lock_kernel(); nfs_set_mmcred(inode, state->owner->so_cred); @@ -1774,7 +1826,7 @@ nfs4_proc_file_release(struct inode *inode, struct file *filp) struct nfs4_state *state = (struct nfs4_state *)filp->private_data; if (state) - nfs4_put_open_state(state); + nfs4_close_state(state, filp->f_mode); return 0; } @@ -1816,6 +1868,9 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) rpc_delay(task, NFS4_POLL_RETRY_TIME); task->tk_status = 0; return -EAGAIN; + case -NFS4ERR_OLD_STATEID: + task->tk_status = 0; + return -EAGAIN; } return 0; } @@ -1892,6 +1947,9 @@ nfs4_handle_error(struct nfs_server *server, int errorcode) case -NFS4ERR_DELAY: ret = nfs4_delay(server->client); break; + case -NFS4ERR_OLD_STATEID: + ret = 0; + break; default: if (errorcode <= -1000) { printk(KERN_WARNING "%s could not handle NFSv4 error %d\n", diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 333daf5f48e7..7a078a42eec3 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -188,6 +188,23 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) return sp; } +static struct nfs4_state_owner * +nfs4_find_state_owner(struct nfs4_client *clp, struct rpc_cred *cred) +{ + struct nfs4_state_owner *sp, *res = NULL; + + list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + if (sp->so_cred != cred) + continue; + atomic_inc(&sp->so_count); + /* Move to the head of the list */ + list_move(&sp->so_list, &clp->cl_state_owners); + res = sp; + break; + } + return res; +} + /* * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to * create a new state_owner. @@ -208,6 +225,15 @@ nfs4_alloc_state_owner(void) return sp; } +static void +nfs4_unhash_state_owner(struct nfs4_state_owner *sp) +{ + struct nfs4_client *clp = sp->so_client; + spin_lock(&clp->cl_lock); + list_del_init(&sp->so_list); + spin_unlock(&clp->cl_lock); +} + struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) { @@ -217,7 +243,9 @@ nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) get_rpccred(cred); new = nfs4_alloc_state_owner(); spin_lock(&clp->cl_lock); - sp = nfs4_client_grab_unused(clp, cred); + sp = nfs4_find_state_owner(clp, cred); + if (sp == NULL) + sp = nfs4_client_grab_unused(clp, cred); if (sp == NULL && new != NULL) { list_add(&new->so_list, &clp->cl_state_owners); new->so_client = clp; @@ -248,6 +276,8 @@ nfs4_put_state_owner(struct nfs4_state_owner *sp) return; if (clp->cl_nunused >= OPENOWNER_POOL_SIZE) goto out_free; + if (list_empty(&sp->so_list)) + goto out_free; list_move(&sp->so_list, &clp->cl_unused); clp->cl_nunused++; spin_unlock(&clp->cl_lock); @@ -269,24 +299,38 @@ nfs4_alloc_open_state(void) state = kmalloc(sizeof(*state), GFP_KERNEL); if (!state) return NULL; - state->pid = current->pid; state->state = 0; + state->nreaders = 0; + state->nwriters = 0; memset(state->stateid.data, 0, sizeof(state->stateid.data)); atomic_set(&state->count, 1); return state; } static struct nfs4_state * -__nfs4_find_state_bypid(struct inode *inode, pid_t pid) +__nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs4_state *state; + mode &= (FMODE_READ|FMODE_WRITE); list_for_each_entry(state, &nfsi->open_states, inode_states) { - if (state->pid == pid) { - atomic_inc(&state->count); - return state; - } + if (state->owner->so_cred != cred) + continue; + if ((mode & FMODE_READ) != 0 && state->nreaders == 0) + continue; + if ((mode & FMODE_WRITE) != 0 && state->nwriters == 0) + continue; + if ((state->state & mode) != mode) + continue; + /* Add the state to the head of the inode's list */ + list_move(&state->inode_states, &nfsi->open_states); + atomic_inc(&state->count); + if (mode & FMODE_READ) + state->nreaders++; + if (mode & FMODE_WRITE) + state->nwriters++; + return state; } return NULL; } @@ -298,7 +342,12 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) struct nfs4_state *state; list_for_each_entry(state, &nfsi->open_states, inode_states) { + /* Is this in the process of being freed? */ + if (state->nreaders == 0 && state->nwriters == 0) + continue; if (state->owner == owner) { + /* Add the state to the head of the inode's list */ + list_move(&state->inode_states, &nfsi->open_states); atomic_inc(&state->count); return state; } @@ -307,16 +356,12 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) } struct nfs4_state * -nfs4_find_state_bypid(struct inode *inode, pid_t pid) +nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) { - struct nfs_inode *nfsi = NFS_I(inode); struct nfs4_state *state; spin_lock(&inode->i_lock); - state = __nfs4_find_state_bypid(inode, pid); - /* Add the state to the tail of the inode's list */ - if (state) - list_move_tail(&state->inode_states, &nfsi->open_states); + state = __nfs4_find_state(inode, cred, mode); spin_unlock(&inode->i_lock); return state; } @@ -387,6 +432,50 @@ nfs4_put_open_state(struct nfs4_state *state) nfs4_put_state_owner(owner); } +void +nfs4_close_state(struct nfs4_state *state, mode_t mode) +{ + struct inode *inode = state->inode; + struct nfs4_state_owner *owner = state->owner; + int newstate; + int status = 0; + + down(&owner->so_sema); + /* Protect against nfs4_find_state() */ + spin_lock(&inode->i_lock); + if (mode & FMODE_READ) + state->nreaders--; + if (mode & FMODE_WRITE) + state->nwriters--; + if (state->nwriters == 0 && state->nreaders == 0) + list_del_init(&state->inode_states); + spin_unlock(&inode->i_lock); + do { + newstate = 0; + if (state->state == 0) + break; + if (state->nreaders) + newstate |= FMODE_READ; + if (state->nwriters) + newstate |= FMODE_WRITE; + if (state->state == newstate) + break; + if (newstate != 0) + status = nfs4_do_downgrade(inode, state, newstate); + else + status = nfs4_do_close(inode, state); + if (!status) { + state->state = newstate; + break; + } + up(&owner->so_sema); + status = nfs4_handle_error(NFS_SERVER(inode), status); + down(&owner->so_sema); + } while (!status); + up(&owner->so_sema); + nfs4_put_open_state(state); +} + /* * Called with sp->so_sema held. * @@ -399,6 +488,9 @@ nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) { if (status == NFS_OK || seqid_mutating_err(-status)) sp->so_seqid++; + /* If the server returns BAD_SEQID, unhash state_owner here */ + if (status == -NFS4ERR_BAD_SEQID) + nfs4_unhash_state_owner(sp); } static int reclaimer(void *); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 16296618a231..7a2d241e50e8 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -176,6 +176,14 @@ static int nfs_stat_to_errno(int); op_decode_hdr_maxsz + \ 4 + 5 + 2 + 3 + \ decode_getattr_maxsz +#define NFS4_enc_open_downgrade_sz \ + compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + op_encode_hdr_maxsz + 7 +#define NFS4_dec_open_downgrade_sz \ + compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + 4 #define NFS4_enc_close_sz compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 5 @@ -711,6 +719,22 @@ encode_open_reclaim(struct xdr_stream *xdr, struct nfs_open_reclaimargs *arg) return 0; } +static int +encode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeargs *arg) +{ + uint32_t *p; + + RESERVE_SPACE(16+sizeof(arg->stateid.data)); + WRITE32(OP_OPEN_DOWNGRADE); + WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data)); + WRITE32(arg->seqid); + WRITE32(arg->share_access); + /* No deny modes */ + WRITE32(0); + + return 0; +} + static int encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh) { @@ -1129,6 +1153,27 @@ out: return status; } +/* + * Encode an OPEN_DOWNGRADE request + */ +static int +nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, uint32_t *p, struct nfs_closeargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if (status) + goto out; + status = encode_open_downgrade(&xdr, args); +out: + return status; +} /* * Encode a READ request @@ -2001,6 +2046,19 @@ decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res) return 0; } +static int +decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res) +{ + uint32_t *p; + int status; + + status = decode_op_hdr(xdr, OP_OPEN_DOWNGRADE); + if (status) + return status; + READ_BUF(sizeof(res->stateid.data)); + COPYMEM(res->stateid.data, sizeof(res->stateid.data)); + return 0; +} static int decode_putfh(struct xdr_stream *xdr) @@ -2377,6 +2435,29 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs DECODE_TAIL; } + +/* + * Decode OPEN_DOWNGRADE response + */ +static int +nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_open_downgrade(&xdr, res); +out: + return status; +} + /* * END OF "GENERIC" DECODE ROUTINES. */ @@ -2827,6 +2908,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(OPEN, enc_open, dec_open), PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), PROC(OPEN_RECLAIM, enc_open_reclaim, dec_open_reclaim), + PROC(OPEN_DOWNGRADE, enc_open_downgrade, dec_open_downgrade), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), PROC(FSINFO, enc_fsinfo, dec_fsinfo), diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index a6f2d563b605..4a61a4682718 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -290,6 +290,7 @@ enum { NFSPROC4_CLNT_OPEN, NFSPROC4_CLNT_OPEN_CONFIRM, NFSPROC4_CLNT_OPEN_RECLAIM, + NFSPROC4_CLNT_OPEN_DOWNGRADE, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, NFSPROC4_CLNT_FSINFO, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 0605e9c63026..cfbb7ff1aa89 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -549,10 +549,11 @@ struct nfs4_state { struct nfs4_state_owner *owner; /* Pointer to the open owner */ struct inode *inode; /* Pointer to the inode */ - pid_t pid; /* Thread that called OPEN */ nfs4_stateid stateid; + unsigned int nreaders; + unsigned int nwriters; int state; /* State on the server (R,W, or RW) */ atomic_t count; }; @@ -568,6 +569,7 @@ extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_proc_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); +int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode); extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int); @@ -586,7 +588,8 @@ extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struc extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); -extern struct nfs4_state *nfs4_find_state_bypid(struct inode *, pid_t); +extern void nfs4_close_state(struct nfs4_state *, mode_t); +extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); extern int nfs4_handle_error(struct nfs_server *, int); extern void nfs4_schedule_state_recovery(struct nfs4_client *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4393ae7c305d..21827ad1a71e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -153,6 +153,7 @@ struct nfs_closeargs { struct nfs_fh * fh; nfs4_stateid stateid; __u32 seqid; + __u32 share_access; }; struct nfs_closeres { -- cgit v1.2.3 From 1f37cd43d9e866e99f81ffe6141b49cc6f83f619 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:02:21 +0100 Subject: NFSv4: Fix a bug which was causing Oopses if the client was mounting more than one partition from the same server. --- fs/nfs/idmap.c | 44 ++++++++++++++++++++++---------------------- fs/nfs/inode.c | 15 +++++---------- fs/nfs/nfs4state.c | 2 ++ fs/nfs/nfs4xdr.c | 8 ++++---- include/linux/nfs_fs.h | 5 +++++ include/linux/nfs_fs_sb.h | 1 - include/linux/nfs_idmap.h | 12 ++++++------ 7 files changed, 44 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index bd1d1335561c..1d5d8a9dba1e 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -88,23 +88,27 @@ static struct rpc_pipe_ops idmap_upcall_ops = { .destroy_msg = idmap_pipe_destroy_msg, }; -void * -nfs_idmap_new(struct nfs_server *server) +void +nfs_idmap_new(struct nfs4_client *clp) { struct idmap *idmap; + if (clp->cl_idmap != NULL) + return; if ((idmap = kmalloc(sizeof(*idmap), GFP_KERNEL)) == NULL) - return (NULL); + return; memset(idmap, 0, sizeof(*idmap)); snprintf(idmap->idmap_path, sizeof(idmap->idmap_path), - "%s/idmap", server->client->cl_pathname); + "%s/idmap", clp->cl_rpcclient->cl_pathname); idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path, idmap, &idmap_upcall_ops, 0); - if (IS_ERR(idmap->idmap_dentry)) - goto err_free; + if (IS_ERR(idmap->idmap_dentry)) { + kfree(idmap); + return; + } init_MUTEX(&idmap->idmap_lock); init_MUTEX(&idmap->idmap_im_lock); @@ -112,22 +116,18 @@ nfs_idmap_new(struct nfs_server *server) idmap->idmap_user_hash.h_type = IDMAP_TYPE_USER; idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP; - return (idmap); - - err_free: - kfree(idmap); - return (NULL); + clp->cl_idmap = idmap; } void -nfs_idmap_delete(struct nfs_server *server) +nfs_idmap_delete(struct nfs4_client *clp) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; if (!idmap) return; rpc_unlink(idmap->idmap_path); - server->idmap = NULL; + clp->cl_idmap = NULL; kfree(idmap); } @@ -468,29 +468,29 @@ static unsigned int fnvhash32(const void *buf, size_t buflen) return (hash); } -int nfs_map_name_to_uid(struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +int nfs_map_name_to_uid(struct nfs4_client *clp, const char *name, size_t namelen, __u32 *uid) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); } -int nfs_map_group_to_gid(struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +int nfs_map_group_to_gid(struct nfs4_client *clp, const char *name, size_t namelen, __u32 *uid) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); } -int nfs_map_uid_to_name(struct nfs_server *server, __u32 uid, char *buf) +int nfs_map_uid_to_name(struct nfs4_client *clp, __u32 uid, char *buf) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); } -int nfs_map_gid_to_group(struct nfs_server *server, __u32 uid, char *buf) +int nfs_map_gid_to_group(struct nfs4_client *clp, __u32 uid, char *buf) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 276f3a10298b..f23707e5ecb2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -158,11 +158,6 @@ nfs_put_super(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); -#ifdef CONFIG_NFS_V4 - if (server->idmap != NULL) - nfs_idmap_delete(server); -#endif /* CONFIG_NFS_V4 */ - nfs4_renewd_prepare_shutdown(server); if (server->client != NULL) @@ -1494,6 +1489,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, clp->cl_rpcclient = clnt; clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); + nfs_idmap_new(clp); } if (list_empty(&clp->cl_superblocks)) clear_bit(NFS4CLNT_OK, &clp->cl_state); @@ -1507,6 +1503,10 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, printk(KERN_WARNING "NFS: cannot create RPC client.\n"); goto out_remove_list; } + if (server->nfs4_state->cl_idmap == NULL) { + printk(KERN_WARNING "NFS: failed to create idmapper.\n"); + goto out_shutdown; + } clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0; @@ -1525,16 +1525,11 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, goto out_shutdown; } - if ((server->idmap = nfs_idmap_new(server)) == NULL) - printk(KERN_WARNING "NFS: couldn't start IDmap\n"); - sb->s_op = &nfs4_sops; err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; rpciod_down(); - if (server->idmap != NULL) - nfs_idmap_delete(server); out_shutdown: rpc_shutdown_client(server->client); out_remove_list: diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 7a078a42eec3..0b391c7f4ce4 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #define OPENOWNER_POOL_SIZE 8 @@ -124,6 +125,7 @@ nfs4_free_client(struct nfs4_client *clp) BUG_ON(!list_empty(&clp->cl_state_owners)); if (clp->cl_cred) put_rpccred(clp->cl_cred); + nfs_idmap_delete(clp); if (clp->cl_rpcclient) rpc_shutdown_client(clp->cl_rpcclient); kfree(clp); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 7a2d241e50e8..3a372266bb03 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -328,7 +328,7 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap, if (iap->ia_valid & ATTR_MODE) len += 4; if (iap->ia_valid & ATTR_UID) { - owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name); + owner_namelen = nfs_map_uid_to_name(server->nfs4_state, iap->ia_uid, owner_name); if (owner_namelen < 0) { printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n", iap->ia_uid); @@ -340,7 +340,7 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap, len += 4 + (XDR_QUADLEN(owner_namelen) << 2); } if (iap->ia_valid & ATTR_GID) { - owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group); + owner_grouplen = nfs_map_gid_to_group(server->nfs4_state, iap->ia_gid, owner_group); if (owner_grouplen < 0) { printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n", iap->ia_gid); @@ -1677,7 +1677,7 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, } READ_BUF(dummy32); len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_map_name_to_uid(server, (char *)p, dummy32, + if ((status = nfs_map_name_to_uid(server->nfs4_state, (char *)p, dummy32, &nfp->uid)) < 0) { dprintk("read_attrs: name-to-uid mapping failed!\n"); nfp->uid = -2; @@ -1694,7 +1694,7 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, } READ_BUF(dummy32); len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_map_group_to_gid(server, (char *)p, dummy32, + if ((status = nfs_map_group_to_gid(server->nfs4_state, (char *)p, dummy32, &nfp->gid)) < 0) { dprintk("read_attrs: group-to-gid mapping failed!\n"); nfp->gid = -2; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index cfbb7ff1aa89..512d9203905f 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -438,6 +438,8 @@ extern void * nfs_root_data(void); #ifdef CONFIG_NFS_V4 +struct idmap; + /* * In a seqid-mutating op, this macro controls which error return * values trigger incrementation of the seqid. @@ -506,6 +508,9 @@ struct nfs4_client { wait_queue_head_t cl_waitq; struct rpc_wait_queue cl_rpcwaitq; + /* idmapper */ + struct idmap * cl_idmap; + /* Our own IP address, as a null-terminated string. * This is used to generate the clientid, and the callback address. */ diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 5f0b0ce3aa2c..1b5f7e130502 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -38,7 +38,6 @@ struct nfs_server { struct list_head nfs4_siblings; /* List of other nfs_server structs * that share the same clientid */ - void *idmap; #endif }; diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index c95076e5941b..50df56b5a01e 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -60,13 +60,13 @@ struct idmap_msg { }; #ifdef __KERNEL__ -void *nfs_idmap_new(struct nfs_server *); -void nfs_idmap_delete(struct nfs_server *); +void nfs_idmap_new(struct nfs4_client *); +void nfs_idmap_delete(struct nfs4_client *); -int nfs_map_name_to_uid(struct nfs_server *, const char *, size_t, __u32 *); -int nfs_map_group_to_gid(struct nfs_server *, const char *, size_t, __u32 *); -int nfs_map_uid_to_name(struct nfs_server *, __u32, char *); -int nfs_map_gid_to_group(struct nfs_server *, __u32, char *); +int nfs_map_name_to_uid(struct nfs4_client *, const char *, size_t, __u32 *); +int nfs_map_group_to_gid(struct nfs4_client *, const char *, size_t, __u32 *); +int nfs_map_uid_to_name(struct nfs4_client *, __u32, char *); +int nfs_map_gid_to_group(struct nfs4_client *, __u32, char *); #endif /* __KERNEL__ */ #endif /* NFS_IDMAP_H */ -- cgit v1.2.3 From 3f1990d30fe3d6e62d41d0224c27855b59517b8b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:03:03 +0100 Subject: NFSv4: Add support for POSIX file locking. --- fs/nfs/file.c | 23 ++-- fs/nfs/nfs3proc.c | 8 ++ fs/nfs/nfs4proc.c | 290 +++++++++++++++++++++++++++++++++++++++++--- fs/nfs/nfs4state.c | 174 +++++++++++++++++++++++++- fs/nfs/nfs4xdr.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++- fs/nfs/proc.c | 8 ++ include/linux/nfs4.h | 3 + include/linux/nfs_fs.h | 37 +++++- include/linux/nfs_page.h | 1 + include/linux/nfs_xdr.h | 65 +++++++++- 10 files changed, 878 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/fs/nfs/file.c b/fs/nfs/file.c index b000db0f1b23..c32e7a2575d3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -278,21 +277,17 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) if (!inode) return -EINVAL; - /* This will be in a forthcoming patch. */ - if (NFS_PROTO(inode)->version == 4) { - printk(KERN_INFO "NFS: file locking over NFSv4 is not yet supported\n"); - return -EIO; - } - /* No mandatory locks over NFS */ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) return -ENOLCK; - /* Fake OK code if mounted without NLM support */ - if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { - if (IS_GETLK(cmd)) - status = LOCK_USE_CLNT; - goto out_ok; + if (NFS_PROTO(inode)->version != 4) { + /* Fake OK code if mounted without NLM support */ + if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { + if (IS_GETLK(cmd)) + status = LOCK_USE_CLNT; + goto out_ok; + } } /* @@ -302,7 +297,7 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) * Not sure whether that would be unique, though, or whether * that would break in other places. */ - if (!fl->fl_owner || (fl->fl_flags & FL_POSIX) != FL_POSIX) + if (!fl->fl_owner || !(fl->fl_flags & FL_POSIX)) return -ENOLCK; /* @@ -322,7 +317,7 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) return status; lock_kernel(); - status = nlmclnt_proc(inode, cmd, fl); + status = NFS_PROTO(inode)->lock(filp, cmd, fl); unlock_kernel(); if (status < 0) return status; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index e5b2ad2f8623..a27b48f411cb 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #define NFSDBG_FACILITY NFSDBG_PROC @@ -896,6 +897,12 @@ nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa return 1; } +static int +nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) +{ + return nlmclnt_proc(filp->f_dentry->d_inode, cmd, fl); +} + struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -931,4 +938,5 @@ struct nfs_rpc_ops nfs_v3_clientops = { .file_release = nfs_release, .request_init = nfs3_request_init, .request_compatible = nfs3_request_compatible, + .lock = nfs3_proc_lock, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 10f67569de73..3d509bd94b0e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -598,9 +598,7 @@ retry: .fh = &o_res.fh, .seqid = sp->so_seqid, }; - struct nfs_open_confirmres oc_res = { - .status = 0, - }; + struct nfs_open_confirmres oc_res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM], .rpc_argp = &oc_arg, @@ -692,7 +690,7 @@ retry: fattr->valid = 0; if (state) - memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid)); + nfs4_copy_stateid(&arg.stateid, state, 0); else memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); @@ -724,9 +722,7 @@ nfs4_do_close(struct inode *inode, struct nfs4_state *state) struct nfs_closeargs arg = { .fh = NFS_FH(inode), }; - struct nfs_closeres res = { - .status = 0, - }; + struct nfs_closeres res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE], .rpc_argp = &arg, @@ -758,9 +754,7 @@ nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) .seqid = sp->so_seqid, .share_access = mode, }; - struct nfs_closeres res = { - .status = 0, - }; + struct nfs_closeres res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE], .rpc_argp = &arg, @@ -1085,7 +1079,7 @@ nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) if (filp) { struct nfs4_state *state; state = (struct nfs4_state *)filp->private_data; - memcpy(&rdata->args.stateid, &state->stateid, sizeof(rdata->args.stateid)); + nfs4_copy_stateid(&rdata->args.stateid, state, rdata->lockowner); msg.rpc_cred = state->owner->so_cred; } else { memcpy(&rdata->args.stateid, &zero_stateid, sizeof(rdata->args.stateid)); @@ -1127,7 +1121,7 @@ nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp) if (filp) { struct nfs4_state *state; state = (struct nfs4_state *)filp->private_data; - memcpy(&wdata->args.stateid, &state->stateid, sizeof(wdata->args.stateid)); + nfs4_copy_stateid(&wdata->args.stateid, state, wdata->lockowner); msg.rpc_cred = state->owner->so_cred; } else { memcpy(&wdata->args.stateid, &zero_stateid, sizeof(wdata->args.stateid)); @@ -1163,7 +1157,7 @@ nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp) if (filp) { struct nfs4_state *state; state = (struct nfs4_state *)filp->private_data; - memcpy(&cdata->args.stateid, &state->stateid, sizeof(cdata->args.stateid)); + nfs4_copy_stateid(&cdata->args.stateid, state, cdata->lockowner); msg.rpc_cred = state->owner->so_cred; } else { memcpy(&cdata->args.stateid, &zero_stateid, sizeof(cdata->args.stateid)); @@ -1513,7 +1507,7 @@ nfs4_restart_read(struct rpc_task *task) rpc_restart_call(task); req = nfs_list_entry(data->pages.next); if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); } @@ -1564,8 +1558,9 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) data->res.eof = 0; data->timestamp = jiffies; + data->lockowner = req->wb_lockowner; if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); @@ -1605,7 +1600,7 @@ nfs4_restart_write(struct rpc_task *task) rpc_restart_call(task); req = nfs_list_entry(data->pages.next); if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); } @@ -1661,8 +1656,9 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) data->res.verf = &data->verf; data->timestamp = jiffies; + data->lockowner = req->wb_lockowner; if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); @@ -1846,6 +1842,7 @@ nfs4_request_init(struct nfs_page *req, struct file *filp) state = (struct nfs4_state *)filp->private_data; req->wb_state = state; req->wb_cred = get_rpccred(state->owner->so_cred); + req->wb_lockowner = current->files; } static int @@ -1975,6 +1972,8 @@ nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa state = (struct nfs4_state *)filp->private_data; if (req->wb_state != state) return 0; + if (req->wb_lockowner != current->files) + return 0; cred = state->owner->so_cred; if (req->wb_cred != cred) return 0; @@ -2032,6 +2031,262 @@ nfs4_proc_setclientid_confirm(struct nfs4_client *clp) return status; } +#define NFS4_LOCK_MINTIMEOUT (1 * HZ) +#define NFS4_LOCK_MAXTIMEOUT (30 * HZ) + +/* + * sleep, with exponential backoff, and retry the LOCK operation. + */ +static unsigned long +nfs4_set_lock_task_retry(unsigned long timeout) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(timeout); + timeout <<= 1; + if (timeout > NFS4_LOCK_MAXTIMEOUT) + return NFS4_LOCK_MAXTIMEOUT; + return timeout; +} + +static inline int +nfs4_lck_type(int cmd, struct file_lock *request) +{ + /* set lock type */ + switch (request->fl_type) { + case F_RDLCK: + return IS_SETLKW(cmd) ? NFS4_READW_LT : NFS4_READ_LT; + case F_WRLCK: + return IS_SETLKW(cmd) ? NFS4_WRITEW_LT : NFS4_WRITE_LT; + case F_UNLCK: + return NFS4_WRITE_LT; + } + BUG(); +} + +static inline uint64_t +nfs4_lck_length(struct file_lock *request) +{ + if (request->fl_end == OFFSET_MAX) + return ~(uint64_t)0; + return request->fl_end - request->fl_start + 1; +} + +int +nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs4_client *clp = server->nfs4_state; + struct nfs_lockargs arg = { + .fh = NFS_FH(inode), + .type = nfs4_lck_type(cmd, request), + .offset = request->fl_start, + .length = nfs4_lck_length(request), + }; + struct nfs_lockres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKT], + .rpc_argp = &arg, + .rpc_resp = &res, + .rpc_cred = state->owner->so_cred, + }; + struct nfs_lowner nlo; + struct nfs4_lock_state *lsp; + int status; + + nlo.clientid = clp->cl_clientid; + down(&state->lock_sema); + lsp = nfs4_find_lock_state(state, request->fl_owner); + if (lsp) + nlo.id = lsp->ls_id; + else { + spin_lock(&clp->cl_lock); + nlo.id = nfs4_alloc_lockowner_id(clp); + spin_unlock(&clp->cl_lock); + } + arg.u.lockt = &nlo; + status = rpc_call_sync(server->client, &msg, 0); + if (!status) { + request->fl_type = F_UNLCK; + } else if (status == -NFS4ERR_DENIED) { + int64_t len, start, end; + start = res.u.denied.offset; + len = res.u.denied.length; + end = start + len - 1; + if (end < 0 || len == 0) + request->fl_end = OFFSET_MAX; + else + request->fl_end = (loff_t)end; + request->fl_start = (loff_t)start; + request->fl_type = F_WRLCK; + if (res.u.denied.type & 1) + request->fl_type = F_RDLCK; + request->fl_pid = 0; + status = 0; + } + if (lsp) + nfs4_put_lock_state(lsp); + up(&state->lock_sema); + return status; +} + +int +nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_lockargs arg = { + .fh = NFS_FH(inode), + .type = nfs4_lck_type(cmd, request), + .offset = request->fl_start, + .length = nfs4_lck_length(request), + }; + struct nfs_lockres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU], + .rpc_argp = &arg, + .rpc_resp = &res, + .rpc_cred = state->owner->so_cred, + }; + struct nfs4_lock_state *lsp; + struct nfs_locku_opargs luargs; + int status = 0; + + down(&state->lock_sema); + lsp = nfs4_find_lock_state(state, request->fl_owner); + if (!lsp) + goto out; + luargs.seqid = lsp->ls_seqid; + memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid)); + arg.u.locku = &luargs; + status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_lock_seqid(status, lsp); + + if (status == 0) { + memcpy(&lsp->ls_stateid, &res.u.stateid, + sizeof(lsp->ls_stateid)); + nfs4_notify_unlck(inode, request, lsp); + } + nfs4_put_lock_state(lsp); +out: + up(&state->lock_sema); + return status; +} + +static int +nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs4_lock_state *lsp; + struct nfs_lockargs arg = { + .fh = NFS_FH(inode), + .type = nfs4_lck_type(cmd, request), + .offset = request->fl_start, + .length = nfs4_lck_length(request), + }; + struct nfs_lockres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK], + .rpc_argp = &arg, + .rpc_resp = &res, + .rpc_cred = state->owner->so_cred, + }; + struct nfs_lock_opargs largs = { + .new_lock_owner = 0, + }; + int status; + + down(&state->lock_sema); + lsp = nfs4_find_lock_state(state, request->fl_owner); + if (lsp == NULL) { + struct nfs4_state_owner *owner = state->owner; + struct nfs_open_to_lock otl = { + .lock_owner.clientid = server->nfs4_state->cl_clientid, + }; + status = -ENOMEM; + lsp = nfs4_alloc_lock_state(state, request->fl_owner); + if (!lsp) + goto out; + otl.lock_seqid = lsp->ls_seqid; + otl.lock_owner.id = lsp->ls_id; + memcpy(&otl.open_stateid, &state->stateid, sizeof(otl.open_stateid)); + largs.u.open_lock = &otl; + largs.new_lock_owner = 1; + arg.u.lock = &largs; + down(&owner->so_sema); + otl.open_seqid = owner->so_seqid; + status = rpc_call_sync(server->client, &msg, 0); + /* increment open_owner seqid on success, and + * seqid mutating errors */ + nfs4_increment_seqid(status, owner); + up(&owner->so_sema); + } else { + struct nfs_exist_lock el = { + .seqid = lsp->ls_seqid, + }; + memcpy(&el.stateid, &lsp->ls_stateid, sizeof(el.stateid)); + largs.u.exist_lock = ⪙ + largs.new_lock_owner = 0; + arg.u.lock = &largs; + status = rpc_call_sync(server->client, &msg, 0); + } + /* increment seqid on success, and * seqid mutating errors*/ + nfs4_increment_lock_seqid(status, lsp); + /* save the returned stateid. */ + if (status == 0) { + memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(nfs4_stateid)); + nfs4_notify_setlk(inode, request, lsp); + } else if (status == -NFS4ERR_DENIED) + status = -EAGAIN; + nfs4_put_lock_state(lsp); +out: + up(&state->lock_sema); + return status; +} + +static int +nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) +{ + struct nfs4_state *state; + unsigned long timeout = NFS4_LOCK_MINTIMEOUT; + int status; + + /* verify open state */ + state = (struct nfs4_state *)filp->private_data; + BUG_ON(!state); + + if (request->fl_start < 0 || request->fl_end < 0) + return -EINVAL; + + if (IS_GETLK(cmd)) + return nfs4_proc_getlk(state, F_GETLK, request); + + if (!(IS_SETLK(cmd) || IS_SETLKW(cmd))) + return -EINVAL; + + if (request->fl_type == F_UNLCK) + return nfs4_proc_unlck(state, cmd, request); + + do { + status = nfs4_proc_setlk(state, cmd, request); + if ((status != -EAGAIN) || IS_SETLK(cmd)) + break; + timeout = nfs4_set_lock_task_retry(timeout); + status = -ERESTARTSYS; + if (signalled()) + break; + } while(status < 0); + + return status; +} + struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ .dentry_ops = &nfs4_dentry_operations, @@ -2067,6 +2322,7 @@ struct nfs_rpc_ops nfs_v4_clientops = { .file_release = nfs4_proc_file_release, .request_init = nfs4_request_init, .request_compatible = nfs4_request_compatible, + .lock = nfs4_proc_lock, }; /* diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0b391c7f4ce4..0694b2e13342 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -43,6 +43,7 @@ #include #include #include +#include #define OPENOWNER_POOL_SIZE 8 @@ -168,7 +169,7 @@ nfs4_put_client(struct nfs4_client *clp) nfs4_free_client(clp); } -static inline u32 +u32 nfs4_alloc_lockowner_id(struct nfs4_client *clp) { return clp->cl_lockowner_id ++; @@ -304,8 +305,12 @@ nfs4_alloc_open_state(void) state->state = 0; state->nreaders = 0; state->nwriters = 0; + state->flags = 0; memset(state->stateid.data, 0, sizeof(state->stateid.data)); atomic_set(&state->count, 1); + INIT_LIST_HEAD(&state->lock_states); + init_MUTEX(&state->lock_sema); + rwlock_init(&state->state_lock); return state; } @@ -453,7 +458,7 @@ nfs4_close_state(struct nfs4_state *state, mode_t mode) list_del_init(&state->inode_states); spin_unlock(&inode->i_lock); do { - newstate = 0; + newstate = 0; if (state->state == 0) break; if (state->nreaders) @@ -478,6 +483,171 @@ nfs4_close_state(struct nfs4_state *state, mode_t mode) nfs4_put_open_state(state); } +/* + * Search the state->lock_states for an existing lock_owner + * that is compatible with current->files + */ +static struct nfs4_lock_state * +__nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +{ + struct nfs4_lock_state *pos; + list_for_each_entry(pos, &state->lock_states, ls_locks) { + if (pos->ls_owner != fl_owner) + continue; + atomic_inc(&pos->ls_count); + return pos; + } + return NULL; +} + +struct nfs4_lock_state * +nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +{ + struct nfs4_lock_state *lsp; + read_lock(&state->state_lock); + lsp = __nfs4_find_lock_state(state, fl_owner); + read_unlock(&state->state_lock); + return lsp; +} + +/* + * Return a compatible lock_state. If no initialized lock_state structure + * exists, return an uninitialized one. + * + * The caller must be holding state->lock_sema + */ +struct nfs4_lock_state * +nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +{ + struct nfs4_lock_state *lsp; + struct nfs4_client *clp = state->owner->so_client; + + lsp = kmalloc(sizeof(*lsp), GFP_KERNEL); + if (lsp == NULL) + return NULL; + lsp->ls_seqid = 0; /* arbitrary */ + lsp->ls_id = -1; + memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data)); + atomic_set(&lsp->ls_count, 1); + lsp->ls_owner = fl_owner; + lsp->ls_parent = state; + INIT_LIST_HEAD(&lsp->ls_locks); + spin_lock(&clp->cl_lock); + lsp->ls_id = nfs4_alloc_lockowner_id(clp); + spin_unlock(&clp->cl_lock); + return lsp; +} + +/* + * Byte-range lock aware utility to initialize the stateid of read/write + * requests. + */ +void +nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner) +{ + if (test_bit(LK_STATE_IN_USE, &state->flags)) { + struct nfs4_lock_state *lsp; + + lsp = nfs4_find_lock_state(state, fl_owner); + if (lsp) { + memcpy(dst, &lsp->ls_stateid, sizeof(*dst)); + nfs4_put_lock_state(lsp); + return; + } + } + memcpy(dst, &state->stateid, sizeof(*dst)); +} + +/* +* Called with state->lock_sema held. +*/ +void +nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp) +{ + if (status == NFS_OK || seqid_mutating_err(-status)) + lsp->ls_seqid++; +} + +/* +* Check to see if the request lock (type FL_UNLK) effects the fl lock. +* +* fl and request must have the same posix owner +* +* return: +* 0 -> fl not effected by request +* 1 -> fl consumed by request +*/ + +static int +nfs4_check_unlock(struct file_lock *fl, struct file_lock *request) +{ + if (fl->fl_start >= request->fl_start && fl->fl_end <= request->fl_end) + return 1; + return 0; +} + +/* + * Post an initialized lock_state on the state->lock_states list. + */ +void +nfs4_notify_setlk(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp) +{ + struct nfs4_state *state = lsp->ls_parent; + + if (!list_empty(&lsp->ls_locks)) + return; + write_lock(&state->state_lock); + list_add(&lsp->ls_locks, &state->lock_states); + set_bit(LK_STATE_IN_USE, &state->flags); + write_unlock(&state->state_lock); +} + +/* + * to decide to 'reap' lock state: + * 1) search i_flock for file_locks with fl.lock_state = to ls. + * 2) determine if unlock will consume found lock. + * if so, reap + * + * else, don't reap. + * + */ +void +nfs4_notify_unlck(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp) +{ + struct nfs4_state *state = lsp->ls_parent; + struct file_lock *fl; + + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!(fl->fl_flags & FL_POSIX)) + continue; + if (fl->fl_owner != lsp->ls_owner) + continue; + /* Exit if we find at least one lock which is not consumed */ + if (nfs4_check_unlock(fl,request) == 0) + return; + } + + write_lock(&state->state_lock); + list_del_init(&lsp->ls_locks); + if (list_empty(&state->lock_states)) + clear_bit(LK_STATE_IN_USE, &state->flags); + write_unlock(&state->state_lock); +} + +/* + * Release reference to lock_state, and free it if we see that + * it is no longer in use + */ +void +nfs4_put_lock_state(struct nfs4_lock_state *lsp) +{ + if (!atomic_dec_and_test(&lsp->ls_count)) + return; + if (!list_empty(&lsp->ls_locks)) + return; + kfree(lsp); +} + /* * Called with sp->so_sema held. * diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 3a372266bb03..f0a688fa675d 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -66,6 +66,10 @@ static int nfs_stat_to_errno(int); #define NFS4_MAXTAGLEN 0 #endif +/* lock,open owner id: + * we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2) + */ +#define owner_id_maxsz 1 + 1 #define compound_encode_hdr_maxsz 3 + (NFS4_MAXTAGLEN >> 2) #define compound_decode_hdr_maxsz 2 + (NFS4_MAXTAGLEN >> 2) #define op_encode_hdr_maxsz 1 @@ -222,6 +226,36 @@ static int nfs_stat_to_errno(int); decode_setclientid_confirm_maxsz + \ decode_putrootfh_maxsz + \ decode_fsinfo_maxsz +#define NFS4_enc_lock_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 1 + 2 + 2 + \ + 1 + 4 + 1 + 2 + \ + owner_id_maxsz +#define NFS4_dec_lock_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_getattr_maxsz + \ + op_decode_hdr_maxsz + \ + 2 + 2 + 1 + 2 + \ + owner_id_maxsz +#define NFS4_enc_lockt_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 2 + 2 + 2 + \ + owner_id_maxsz +#define NFS4_dec_lockt_sz NFS4_dec_lock_sz +#define NFS4_enc_locku_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 1 + 4 + 2 + 2 +#define NFS4_dec_locku_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_getattr_maxsz + \ + op_decode_hdr_maxsz + 4 + static struct { @@ -596,6 +630,80 @@ encode_link(struct xdr_stream *xdr, struct nfs4_link *link) return 0; } +/* + * opcode,type,reclaim,offset,length,new_lock_owner = 32 + * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 + */ +static int +encode_lock(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_lock_opargs *opargs = arg->u.lock; + + RESERVE_SPACE(32); + WRITE32(OP_LOCK); + WRITE32(arg->type); + WRITE32(opargs->reclaim); + WRITE64(arg->offset); + WRITE64(arg->length); + WRITE32(opargs->new_lock_owner); + if (opargs->new_lock_owner){ + struct nfs_open_to_lock *ol = opargs->u.open_lock; + + RESERVE_SPACE(40); + WRITE32(ol->open_seqid); + WRITEMEM(&ol->open_stateid, sizeof(ol->open_stateid)); + WRITE32(ol->lock_seqid); + WRITE64(ol->lock_owner.clientid); + WRITE32(4); + WRITE32(ol->lock_owner.id); + } + else { + struct nfs_exist_lock *el = opargs->u.exist_lock; + + RESERVE_SPACE(20); + WRITEMEM(&el->stateid, sizeof(el->stateid)); + WRITE32(el->seqid); + } + + return 0; +} + +static int +encode_lockt(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_lowner *opargs = arg->u.lockt; + + RESERVE_SPACE(40); + WRITE32(OP_LOCKT); + WRITE32(arg->type); + WRITE64(arg->offset); + WRITE64(arg->length); + WRITE64(opargs->clientid); + WRITE32(4); + WRITE32(opargs->id); + + return 0; +} + +static int +encode_locku(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_locku_opargs *opargs = arg->u.locku; + + RESERVE_SPACE(44); + WRITE32(OP_LOCKU); + WRITE32(arg->type); + WRITE32(opargs->seqid); + WRITEMEM(&opargs->stateid, sizeof(opargs->stateid)); + WRITE64(arg->offset); + WRITE64(arg->length); + + return 0; +} + static int encode_lookup(struct xdr_stream *xdr, struct nfs4_lookup *lookup) { @@ -1175,6 +1283,72 @@ out: return status; } +/* + * Encode a LOCK request + */ +static int +nfs4_xdr_enc_lock(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_lock(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCKT request + */ +static int +nfs4_xdr_enc_lockt(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_lockt(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCKU request + */ +static int +nfs4_xdr_enc_locku(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_locku(&xdr, args); +out: + return status; +} + /* * Encode a READ request */ @@ -1997,6 +2171,66 @@ decode_link(struct xdr_stream *xdr, struct nfs4_link *link) return decode_change_info(xdr, link->ln_cinfo); } +/* + * We create the owner, so we know a proper owner.id length is 4. + */ +static int +decode_lock_denied (struct xdr_stream *xdr, struct nfs_lock_denied *denied) +{ + uint32_t *p; + uint32_t namelen; + + READ_BUF(32); + READ64(denied->offset); + READ64(denied->length); + READ32(denied->type); + READ64(denied->owner.clientid); + READ32(namelen); + READ_BUF(namelen); + if (namelen == 4) + READ32(denied->owner.id); + return -NFS4ERR_DENIED; +} + +static int +decode_lock(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + uint32_t *p; + int status; + + status = decode_op_hdr(xdr, OP_LOCK); + if (status == 0) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(&res->u.stateid, sizeof(res->u.stateid)); + } else if (status == -NFS4ERR_DENIED) + return decode_lock_denied(xdr, &res->u.denied); + return status; +} + +static int +decode_lockt(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + int status; + status = decode_op_hdr(xdr, OP_LOCKT); + if (status == -NFS4ERR_DENIED) + return decode_lock_denied(xdr, &res->u.denied); + return status; +} + +static int +decode_locku(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + uint32_t *p; + int status; + + status = decode_op_hdr(xdr, OP_LOCKU); + if (status == 0) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(&res->u.stateid, sizeof(res->u.stateid)); + } + return status; +} + static int decode_lookup(struct xdr_stream *xdr) { @@ -2037,10 +2271,11 @@ static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res) { uint32_t *p; + int status; - res->status = decode_op_hdr(xdr, OP_OPEN_CONFIRM); - if (res->status) - return res->status; + status = decode_op_hdr(xdr, OP_OPEN_CONFIRM); + if (status) + return status; READ_BUF(sizeof(res->stateid.data)); COPYMEM(res->stateid.data, sizeof(res->stateid.data)); return 0; @@ -2619,6 +2854,71 @@ out: return status; } +/* + * Decode LOCK response + */ +static int +nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_lock(&xdr, res); +out: + return status; +} + +/* + * Decode LOCKT response + */ +static int +nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_lockt(&xdr, res); +out: + return status; +} + +/* + * Decode LOCKU response + */ +static int +nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_locku(&xdr, res); +out: + return status; +} /* * Decode Read response @@ -2915,6 +3215,9 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(RENEW, enc_renew, dec_renew), PROC(SETCLIENTID, enc_setclientid, dec_setclientid), PROC(SETCLIENTID_CONFIRM, enc_setclientid_confirm, dec_setclientid_confirm), + PROC(LOCK, enc_lock, dec_lock), + PROC(LOCKT, enc_lockt, dec_lockt), + PROC(LOCKU, enc_locku, dec_locku), }; struct rpc_version nfs_version4 = { diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 79f18e4cfb81..3b118742286f 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #define NFSDBG_FACILITY NFSDBG_PROC @@ -653,6 +654,12 @@ nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *pag return 1; } +static int +nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl) +{ + return nlmclnt_proc(filp->f_dentry->d_inode, cmd, fl); +} + struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ @@ -689,4 +696,5 @@ struct nfs_rpc_ops nfs_v2_clientops = { .file_release = nfs_release, .request_init = nfs_request_init, .request_compatible = nfs_request_compatible, + .lock = nfs_proc_lock, }; diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 4a61a4682718..35baf20a5b5c 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -297,6 +297,9 @@ enum { NFSPROC4_CLNT_RENEW, NFSPROC4_CLNT_SETCLIENTID, NFSPROC4_CLNT_SETCLIENTID_CONFIRM, + NFSPROC4_CLNT_LOCK, + NFSPROC4_CLNT_LOCKT, + NFSPROC4_CLNT_LOCKU, }; #endif diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 512d9203905f..524eb6d04d7b 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -542,19 +542,43 @@ struct nfs4_state_owner { /* * struct nfs4_state maintains the client-side state for a given - * (state_owner,inode) tuple. + * (state_owner,inode) tuple (OPEN) or state_owner (LOCK). * + * OPEN: * In order to know when to OPEN_DOWNGRADE or CLOSE the state on the server, * we need to know how many files are open for reading or writing on a * given inode. This information too is stored here. + * + * LOCK: one nfs4_state (LOCK) to hold the lock stateid nfs4_state(OPEN) */ + +struct nfs4_lock_state { + struct list_head ls_locks; /* Other lock stateids */ + fl_owner_t ls_owner; /* POSIX lock owner */ + struct nfs4_state * ls_parent; /* Parent nfs4_state */ + u32 ls_seqid; + u32 ls_id; + nfs4_stateid ls_stateid; + atomic_t ls_count; +}; + +/* bits for nfs4_state->flags */ +enum { + LK_STATE_IN_USE, +}; + struct nfs4_state { struct list_head open_states; /* List of states for the same state_owner */ struct list_head inode_states; /* List of states for the same inode */ + struct list_head lock_states; /* List of subservient lock stateids */ struct nfs4_state_owner *owner; /* Pointer to the open owner */ struct inode *inode; /* Pointer to the inode */ + unsigned long flags; /* Do we hold any locks? */ + struct semaphore lock_sema; /* Serializes file locking operations */ + rwlock_t state_lock; /* Protects the lock_states list */ + nfs4_stateid stateid; unsigned int nreaders; @@ -589,6 +613,8 @@ extern void init_nfsv4_state(struct nfs_server *); extern void destroy_nfsv4_state(struct nfs_server *); extern struct nfs4_client *nfs4_get_client(struct in_addr *); extern void nfs4_put_client(struct nfs4_client *clp); +extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *); + extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); @@ -598,6 +624,15 @@ extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mod extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); extern int nfs4_handle_error(struct nfs_server *, int); extern void nfs4_schedule_state_recovery(struct nfs4_client *); +extern struct nfs4_lock_state *nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t); +extern struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t); +extern void nfs4_put_lock_state(struct nfs4_lock_state *state); +extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls); +extern void nfs4_notify_setlk(struct inode *, struct file_lock *, struct nfs4_lock_state *); +extern void nfs4_notify_unlck(struct inode *, struct file_lock *, struct nfs4_lock_state *); +extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); + + struct nfs4_mount_data; #else diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 42677b62e92b..c41a4e75555e 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -26,6 +26,7 @@ struct nfs_page { struct list_head wb_list, /* Defines state of page: */ *wb_list_head; /* read/write/commit */ struct file *wb_file; + fl_owner_t wb_lockowner; struct inode *wb_inode; struct rpc_cred *wb_cred; struct nfs4_state *wb_state; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 21827ad1a71e..a3ecfab78bc6 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -109,7 +109,6 @@ struct nfs_openargs { }; struct nfs_openres { - __u32 status; nfs4_stateid stateid; struct nfs_fh fh; struct nfs4_change_info * cinfo; @@ -129,7 +128,6 @@ struct nfs_open_confirmargs { }; struct nfs_open_confirmres { - __u32 status; nfs4_stateid stateid; }; @@ -157,10 +155,68 @@ struct nfs_closeargs { }; struct nfs_closeres { - __u32 status; nfs4_stateid stateid; }; +/* + * * Arguments to the lock,lockt, and locku call. + * */ +struct nfs_lowner { + __u64 clientid; + u32 id; +}; + +struct nfs_open_to_lock { + __u32 open_seqid; + nfs4_stateid open_stateid; + __u32 lock_seqid; + struct nfs_lowner lock_owner; +}; + +struct nfs_exist_lock { + nfs4_stateid stateid; + __u32 seqid; +}; +struct nfs_lock_opargs { + __u32 reclaim; + __u32 new_lock_owner; + union { + struct nfs_open_to_lock *open_lock; + struct nfs_exist_lock *exist_lock; + } u; +}; + +struct nfs_locku_opargs { + __u32 seqid; + nfs4_stateid stateid; +}; + +struct nfs_lockargs { + struct nfs_fh * fh; + __u32 type; + __u64 offset; + __u64 length; + union { + struct nfs_lock_opargs *lock; /* LOCK */ + struct nfs_lowner *lockt; /* LOCKT */ + struct nfs_locku_opargs *locku; /* LOCKU */ + } u; +}; + +struct nfs_lock_denied { + __u64 offset; + __u64 length; + __u32 type; + struct nfs_lowner owner; +}; + +struct nfs_lockres { + union { + nfs4_stateid stateid;/* LOCK success, LOCKU */ + struct nfs_lock_denied denied; /* LOCK failed, LOCKT success */ + } u; + struct nfs_server * server; +}; /* * Arguments to the read call. @@ -605,6 +661,7 @@ struct nfs_read_data { struct rpc_task task; struct inode *inode; struct rpc_cred *cred; + fl_owner_t lockowner; struct nfs_fattr fattr; /* fattr storage */ struct list_head pages; /* Coalesced read requests */ struct page *pagevec[NFS_READ_MAXIOV]; @@ -620,6 +677,7 @@ struct nfs_write_data { struct rpc_task task; struct inode *inode; struct rpc_cred *cred; + fl_owner_t lockowner; struct nfs_fattr fattr; struct nfs_writeverf verf; struct list_head pages; /* Coalesced requests we wish to flush */ @@ -686,6 +744,7 @@ struct nfs_rpc_ops { int (*file_release) (struct inode *, struct file *); void (*request_init)(struct nfs_page *, struct file *); int (*request_compatible)(struct nfs_page *, struct file *, struct page *); + int (*lock)(struct file *, int, struct file_lock *); }; /* -- cgit v1.2.3 From b8f81320cd8c10f8a5aa6c1d8456d1dc467d9ad9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 7 Feb 2004 17:41:22 -0800 Subject: [PATCH] v850: Define ARCH_HAS_*_EXTABLE macros for v850 From: (Miles Bader) Define ARCH_HAS_*_EXTABLE macros for v850 --- include/asm-v850/module.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-v850/module.h b/include/asm-v850/module.h index 9b2e1e30033a..2c2f4944f09f 100644 --- a/include/asm-v850/module.h +++ b/include/asm-v850/module.h @@ -1,8 +1,8 @@ /* * include/asm-v850/module.h -- Architecture-specific module hooks * - * Copyright (C) 2001,02,03 NEC Corporation - * Copyright (C) 2001,02,03 Miles Bader + * Copyright (C) 2001,02,03,04 NEC Corporation + * Copyright (C) 2001,02,03,04 Miles Bader * Copyright (C) 2001,03 Rusty Russell * * This file is subject to the terms and conditions of the GNU General @@ -50,5 +50,13 @@ search_extable(const struct exception_table_entry *first, { return 0; } +#define ARCH_HAS_SEARCH_EXTABLE +static inline void +sort_extable(struct exception_table_entry *start, + struct exception_table_entry *finish) +{ + /* nada */ +} +#define ARCH_HAS_SORT_EXTABLE #endif /* __V850_MODULE_H__ */ -- cgit v1.2.3 From fb3b8050c442a54e935b86dcfa751b547216b55b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 7 Feb 2004 17:41:31 -0800 Subject: [PATCH] v850: make __delay function handle a loop count of zero From: (Miles Bader) make __delay function handle a loop count of zero. In practice, this only seems to occur in odd debugging situations, but it's quite annoying then. --- include/asm-v850/delay.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/asm-v850/delay.h b/include/asm-v850/delay.h index bad311ba44b5..1ce65d48a7c5 100644 --- a/include/asm-v850/delay.h +++ b/include/asm-v850/delay.h @@ -2,8 +2,8 @@ * include/asm-v850/delay.h -- Delay routines, using a pre-computed * "loops_per_second" value * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader + * Copyright (C) 2001,03 NEC Corporation + * Copyright (C) 2001,03 Miles Bader * Copyright (C) 1994 Hamish Macdonald * * This file is subject to the terms and conditions of the GNU General @@ -18,8 +18,9 @@ extern __inline__ void __delay(unsigned long loops) { - __asm__ __volatile__ ("1: add -1, %0; bnz 1b" - : "=r" (loops) : "0" (loops)); + if (loops) + __asm__ __volatile__ ("1: add -1, %0; bnz 1b" + : "=r" (loops) : "0" (loops)); } /* -- cgit v1.2.3 From 87afa29af1652cfd6c8ad35aadb3102aee4e02a9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 7 Feb 2004 17:41:41 -0800 Subject: [PATCH] add device id to radeonfb From: Andreas Steinmetz The attached patch adds the pci id 5961 to radeonfb. Without the patch my 9200 displays only a blank screen. lspci output below. 05:00.0 VGA compatible controller: ATI Technologies Inc Radeon RV280 [Radeon 9200] (rev 01) (prog-if 00 [VGA]) Subsystem: Giga-byte Technology: Unknown device 4018 Flags: bus master, 66Mhz, medium devsel, latency 64, IRQ 16 Memory at e0000000 (32-bit, prefetchable) [size=128M] I/O ports at b800 [size=256] Memory at feaf0000 (32-bit, non-prefetchable) [size=64K] Expansion ROM at feac0000 [disabled] [size=128K] Capabilities: [58] AGP version 3.0 Capabilities: [50] Power Management version 2 --- drivers/video/radeonfb.c | 2 ++ include/linux/pci_ids.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c index cb7b2b74ded3..8569e1614528 100644 --- a/drivers/video/radeonfb.c +++ b/drivers/video/radeonfb.c @@ -114,6 +114,7 @@ enum radeon_chips { RADEON_Ie, RADEON_If, RADEON_Ig, + RADEON_Ya, RADEON_Yd, RADEON_Ld, RADEON_Le, @@ -208,6 +209,7 @@ static struct pci_device_id radeonfb_pci_table[] = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ie, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ie}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_If, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_If}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ig, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ig}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ya, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ya}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Yd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Yd}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ld, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ld}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Le, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Le}, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index c21bb5925bd1..79a90610f302 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -291,6 +291,7 @@ #define PCI_DEVICE_ID_ATI_RADEON_Ig 0x4967 /* Radeon RV280 (9200) */ #define PCI_DEVICE_ID_ATI_RADEON_Y_ 0x5960 +#define PCI_DEVICE_ID_ATI_RADEON_Ya 0x5961 #define PCI_DEVICE_ID_ATI_RADEON_Yd 0x5964 /* Radeon R300 (9500) */ #define PCI_DEVICE_ID_ATI_RADEON_AD 0x4144 -- cgit v1.2.3 From f2f9bbe5b187b2c9f9b0fc611f29e5531c675673 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 7 Feb 2004 17:42:17 -0800 Subject: [PATCH] alpha: fix build error due to __attribute_const__ conversion Include the needed compiler.h. Also swizze the return type and the attribute around to match everyone else. --- include/asm-alpha/byteorder.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-alpha/byteorder.h b/include/asm-alpha/byteorder.h index e5179c55920c..7af2b8d25486 100644 --- a/include/asm-alpha/byteorder.h +++ b/include/asm-alpha/byteorder.h @@ -2,11 +2,12 @@ #define _ALPHA_BYTEORDER_H #include +#include #include #ifdef __GNUC__ -static __inline __u32 __attribute_const__ __arch__swab32(__u32 x) +static __inline __attribute_const__ __u32 __arch__swab32(__u32 x) { /* * Unfortunately, we can't use the 6 instruction sequence -- cgit v1.2.3 From 6a5b27ef8bbe9e800b6a3f7662a380392f1b3040 Mon Sep 17 00:00:00 2001 From: Hideaki Yoshifuji Date: Sat, 7 Feb 2004 20:54:14 -0800 Subject: [IPV6]: Kill broken and unused IPV6_AUTHHDR support. --- include/linux/ipv6.h | 2 -- net/ipv6/datagram.c | 24 ------------------------ net/ipv6/exthdrs.c | 26 -------------------------- net/ipv6/ipv6_sockglue.c | 9 --------- 4 files changed, 61 deletions(-) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index ab97fc520921..29911bf4218e 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -185,7 +185,6 @@ struct inet6_skb_parm int iif; __u16 ra; __u16 hop; - __u16 auth; __u16 dst0; __u16 srcrt; __u16 dst1; @@ -211,7 +210,6 @@ struct ipv6_pinfo { rxhlim:1, hopopts:1, dstopts:1, - authhdr:1, rxflow:1; } bits; __u8 all; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index a32e0b5d9c82..8c39cf5f6e34 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -242,10 +242,6 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt); put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr); } - if (np->rxopt.bits.authhdr && opt->auth) { - u8 *ptr = skb->nh.raw + opt->auth; - put_cmsg(msg, SOL_IPV6, IPV6_AUTHHDR, (ptr[1]+1)<<2, ptr); - } if (np->rxopt.bits.dstopts && opt->dst1) { u8 *ptr = skb->nh.raw + opt->dst1; put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); @@ -378,26 +374,6 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, opt->dst1opt = hdr; break; - case IPV6_AUTHHDR: - if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { - err = -EINVAL; - goto exit_f; - } - - hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg); - len = ((hdr->hdrlen + 2) << 2); - if (cmsg->cmsg_len < CMSG_LEN(len)) { - err = -EINVAL; - goto exit_f; - } - if (len & ~7) { - err = -EINVAL; - goto exit_f; - } - opt->opt_flen += len; - opt->auth = hdr; - break; - case IPV6_RTHDR: if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) { err = -EINVAL; diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 65f00568b1a4..93b2f8bbf65b 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -518,17 +518,6 @@ static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct return &h->nexthdr; } -static u8 *ipv6_build_authhdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_opt_hdr *opt) -{ - struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, (opt->hdrlen+2)<<2); - - memcpy(h, opt, (opt->hdrlen+2)<<2); - h->nexthdr = *prev_hdr; - *prev_hdr = NEXTHDR_AUTH; - return &h->nexthdr; -} - - u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt, struct in6_addr *daddr, u32 jumbolen) { @@ -567,8 +556,6 @@ u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptio u8 *ipv6_build_frag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt) { - if (opt->auth) - prev_hdr = ipv6_build_authhdr(skb, prev_hdr, opt->auth); if (opt->dst1opt) prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt); return prev_hdr; @@ -608,15 +595,6 @@ static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv *proto = type; } -static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr *opt) -{ - struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, (opt->hdrlen+2)<<2); - - memcpy(h, opt, (opt->hdrlen+2)<<2); - h->nexthdr = *proto; - *proto = NEXTHDR_AUTH; -} - void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto, struct in6_addr **daddr) @@ -633,8 +611,6 @@ void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *pr { if (opt->dst1opt) ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt); - if (opt->auth) - ipv6_push_authhdr(skb, proto, opt->auth); } struct ipv6_txoptions * @@ -652,8 +628,6 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) *((char**)&opt2->dst0opt) += dif; if (opt2->dst1opt) *((char**)&opt2->dst1opt) += dif; - if (opt2->auth) - *((char**)&opt2->auth) += dif; if (opt2->srcrt) *((char**)&opt2->srcrt) += dif; } diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index c9aa51f318b3..52c02189c483 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -230,11 +230,6 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, retv = 0; break; - case IPV6_AUTHHDR: - np->rxopt.bits.authhdr = valbool; - retv = 0; - break; - case IPV6_DSTOPTS: np->rxopt.bits.dstopts = valbool; retv = 0; @@ -623,10 +618,6 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, val = np->rxopt.bits.hopopts; break; - case IPV6_AUTHHDR: - val = np->rxopt.bits.authhdr; - break; - case IPV6_DSTOPTS: val = np->rxopt.bits.dstopts; break; -- cgit v1.2.3 From d89c83bebae011a76e1ca9ec65c973aac0ad5da9 Mon Sep 17 00:00:00 2001 From: Adam Belay Date: Sun, 8 Feb 2004 15:28:23 +0000 Subject: [PNP]: Resource flags update This patch reorganizes resource flags to ensure that manual resource settings are properly recognized. This fix is necessary for many ALSA drivers. It also prevents comparisons between unset resource structures. The bug was discovered by Rene Herman , who also wrote an initial version of this patch. I made further improvements to ensure that the pnp subsystem was compatible with this initial change. --- drivers/pnp/isapnp/core.c | 8 ++++---- drivers/pnp/manager.c | 18 +++++++++--------- drivers/pnp/pnpbios/rsparser.c | 8 ++++---- drivers/pnp/resource.c | 19 +++++++++++-------- include/linux/pnp.h | 16 ++++++++++++---- 5 files changed, 40 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c index b5e3e898f8e5..453f69c55967 100644 --- a/drivers/pnp/isapnp/core.c +++ b/drivers/pnp/isapnp/core.c @@ -1039,17 +1039,17 @@ static int isapnp_set_resources(struct pnp_dev *dev, struct pnp_resource_table * isapnp_cfg_begin(dev->card->number, dev->number); dev->active = 1; - for (tmp = 0; tmp < PNP_MAX_PORT && res->port_resource[tmp].flags & IORESOURCE_IO; tmp++) + for (tmp = 0; tmp < PNP_MAX_PORT && (res->port_resource[tmp].flags & (IORESOURCE_IO | IORESOURCE_UNSET)) == IORESOURCE_IO; tmp++) isapnp_write_word(ISAPNP_CFG_PORT+(tmp<<1), res->port_resource[tmp].start); - for (tmp = 0; tmp < PNP_MAX_IRQ && res->irq_resource[tmp].flags & IORESOURCE_IRQ; tmp++) { + for (tmp = 0; tmp < PNP_MAX_IRQ && (res->irq_resource[tmp].flags & (IORESOURCE_IRQ | IORESOURCE_UNSET)) == IORESOURCE_IRQ; tmp++) { int irq = res->irq_resource[tmp].start; if (irq == 2) irq = 9; isapnp_write_byte(ISAPNP_CFG_IRQ+(tmp<<1), irq); } - for (tmp = 0; tmp < PNP_MAX_DMA && res->dma_resource[tmp].flags & IORESOURCE_DMA; tmp++) + for (tmp = 0; tmp < PNP_MAX_DMA && (res->dma_resource[tmp].flags & (IORESOURCE_DMA | IORESOURCE_UNSET)) == IORESOURCE_DMA; tmp++) isapnp_write_byte(ISAPNP_CFG_DMA+tmp, res->dma_resource[tmp].start); - for (tmp = 0; tmp < PNP_MAX_MEM && res->mem_resource[tmp].flags & IORESOURCE_MEM; tmp++) + for (tmp = 0; tmp < PNP_MAX_MEM && (res->mem_resource[tmp].flags & (IORESOURCE_MEM | IORESOURCE_UNSET)) == IORESOURCE_MEM; tmp++) isapnp_write_word(ISAPNP_CFG_MEM+(tmp<<2), (res->mem_resource[tmp].start >> 8) & 0xffff); /* FIXME: We aren't handling 32bit mems properly here */ isapnp_activate(dev->number); diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 36b6220a6335..bd344233e5e8 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -223,25 +223,25 @@ void pnp_init_resource_table(struct pnp_resource_table *table) table->irq_resource[idx].name = NULL; table->irq_resource[idx].start = -1; table->irq_resource[idx].end = -1; - table->irq_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + table->irq_resource[idx].flags = IORESOURCE_IRQ | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_DMA; idx++) { table->dma_resource[idx].name = NULL; table->dma_resource[idx].start = -1; table->dma_resource[idx].end = -1; - table->dma_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + table->dma_resource[idx].flags = IORESOURCE_DMA | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_PORT; idx++) { table->port_resource[idx].name = NULL; table->port_resource[idx].start = 0; table->port_resource[idx].end = 0; - table->port_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + table->port_resource[idx].flags = IORESOURCE_IO | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_MEM; idx++) { table->mem_resource[idx].name = NULL; table->mem_resource[idx].start = 0; table->mem_resource[idx].end = 0; - table->mem_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + table->mem_resource[idx].flags = IORESOURCE_MEM | IORESOURCE_AUTO | IORESOURCE_UNSET; } } @@ -258,28 +258,28 @@ static void pnp_clean_resource_table(struct pnp_resource_table * res) continue; res->irq_resource[idx].start = -1; res->irq_resource[idx].end = -1; - res->irq_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + res->irq_resource[idx].flags = IORESOURCE_IRQ | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_DMA; idx++) { if (!(res->dma_resource[idx].flags & IORESOURCE_AUTO)) continue; res->dma_resource[idx].start = -1; res->dma_resource[idx].end = -1; - res->dma_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + res->dma_resource[idx].flags = IORESOURCE_DMA | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_PORT; idx++) { if (!(res->port_resource[idx].flags & IORESOURCE_AUTO)) continue; res->port_resource[idx].start = 0; res->port_resource[idx].end = 0; - res->port_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + res->port_resource[idx].flags = IORESOURCE_IO | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_MEM; idx++) { if (!(res->mem_resource[idx].flags & IORESOURCE_AUTO)) continue; res->mem_resource[idx].start = 0; res->mem_resource[idx].end = 0; - res->mem_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + res->mem_resource[idx].flags = IORESOURCE_MEM | IORESOURCE_AUTO | IORESOURCE_UNSET; } } @@ -550,7 +550,7 @@ void pnp_resource_change(struct resource *resource, unsigned long start, unsigne { if (resource == NULL) return; - resource->flags &= ~IORESOURCE_AUTO; + resource->flags &= ~(IORESOURCE_AUTO | IORESOURCE_UNSET); resource->start = start; resource->end = start + size - 1; } diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index 7af3cb0ceceb..758d6804f0ff 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -49,7 +49,7 @@ static void pnpbios_parse_allocated_irqresource(struct pnp_resource_table * res, int irq) { int i = 0; - while ((res->irq_resource[i].flags & IORESOURCE_IRQ) && i < PNP_MAX_IRQ) i++; + while (!(res->irq_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_IRQ) i++; if (i < PNP_MAX_IRQ) { res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag if (irq == -1) { @@ -65,7 +65,7 @@ static void pnpbios_parse_allocated_dmaresource(struct pnp_resource_table * res, int dma) { int i = 0; - while ((res->dma_resource[i].flags & IORESOURCE_DMA) && i < PNP_MAX_DMA) i++; + while (!(res->dma_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_DMA) i++; if (i < PNP_MAX_DMA) { res->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag if (dma == -1) { @@ -81,7 +81,7 @@ static void pnpbios_parse_allocated_ioresource(struct pnp_resource_table * res, int io, int len) { int i = 0; - while ((res->port_resource[i].flags & IORESOURCE_IO) && i < PNP_MAX_PORT) i++; + while (!(res->port_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_PORT) i++; if (i < PNP_MAX_PORT) { res->port_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag if (len <= 0 || (io + len -1) >= 0x10003) { @@ -97,7 +97,7 @@ static void pnpbios_parse_allocated_memresource(struct pnp_resource_table * res, int mem, int len) { int i = 0; - while ((res->mem_resource[i].flags & IORESOURCE_MEM) && i < PNP_MAX_MEM) i++; + while (!(res->mem_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_MEM) i++; if (i < PNP_MAX_MEM) { res->mem_resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag if (len <= 0) { diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index dfe565a82ea8..1c1ab31a619f 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -241,6 +241,9 @@ void pnp_free_option(struct pnp_option *option) (*(enda) >= *(startb) && *(enda) <= *(endb)) || \ (*(starta) < *(startb) && *(enda) > *(endb))) +#define cannot_compare(flags) \ +((flags) & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) + int pnp_check_port(struct pnp_dev * dev, int idx) { int tmp; @@ -250,7 +253,7 @@ int pnp_check_port(struct pnp_dev * dev, int idx) end = &dev->res.port_resource[idx].end; /* if the resource doesn't exist, don't complain about it */ - if (dev->res.port_resource[idx].flags & IORESOURCE_UNSET) + if (cannot_compare(dev->res.port_resource[idx].flags)) return 1; /* check if the resource is already in use, skip if the @@ -284,7 +287,7 @@ int pnp_check_port(struct pnp_dev * dev, int idx) continue; for (tmp = 0; tmp < PNP_MAX_PORT; tmp++) { if (tdev->res.port_resource[tmp].flags & IORESOURCE_IO) { - if (pnp_port_flags(dev, tmp) & IORESOURCE_DISABLED) + if (cannot_compare(tdev->res.port_resource[tmp].flags)) continue; tport = &tdev->res.port_resource[tmp].start; tend = &tdev->res.port_resource[tmp].end; @@ -306,7 +309,7 @@ int pnp_check_mem(struct pnp_dev * dev, int idx) end = &dev->res.mem_resource[idx].end; /* if the resource doesn't exist, don't complain about it */ - if (dev->res.mem_resource[idx].flags & IORESOURCE_UNSET) + if (cannot_compare(dev->res.mem_resource[idx].flags)) return 1; /* check if the resource is already in use, skip if the @@ -340,7 +343,7 @@ int pnp_check_mem(struct pnp_dev * dev, int idx) continue; for (tmp = 0; tmp < PNP_MAX_MEM; tmp++) { if (tdev->res.mem_resource[tmp].flags & IORESOURCE_MEM) { - if (pnp_mem_flags(dev, tmp) & IORESOURCE_DISABLED) + if (cannot_compare(tdev->res.mem_resource[tmp].flags)) continue; taddr = &tdev->res.mem_resource[tmp].start; tend = &tdev->res.mem_resource[tmp].end; @@ -365,7 +368,7 @@ int pnp_check_irq(struct pnp_dev * dev, int idx) unsigned long * irq = &dev->res.irq_resource[idx].start; /* if the resource doesn't exist, don't complain about it */ - if (dev->res.irq_resource[idx].flags & IORESOURCE_UNSET) + if (cannot_compare(dev->res.irq_resource[idx].flags)) return 1; /* check if the resource is valid */ @@ -411,7 +414,7 @@ int pnp_check_irq(struct pnp_dev * dev, int idx) continue; for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) { if (tdev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) { - if (pnp_irq_flags(dev, tmp) & IORESOURCE_DISABLED) + if (cannot_compare(tdev->res.irq_resource[tmp].flags)) continue; if ((tdev->res.irq_resource[tmp].start == *irq)) return 0; @@ -429,7 +432,7 @@ int pnp_check_dma(struct pnp_dev * dev, int idx) unsigned long * dma = &dev->res.dma_resource[idx].start; /* if the resource doesn't exist, don't complain about it */ - if (dev->res.dma_resource[idx].flags & IORESOURCE_UNSET) + if (cannot_compare(dev->res.dma_resource[idx].flags)) return 1; /* check if the resource is valid */ @@ -464,7 +467,7 @@ int pnp_check_dma(struct pnp_dev * dev, int idx) continue; for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) { if (tdev->res.dma_resource[tmp].flags & IORESOURCE_DMA) { - if (pnp_dma_flags(dev, tmp) & IORESOURCE_DISABLED) + if (cannot_compare(tdev->res.dma_resource[tmp].flags)) continue; if ((tdev->res.dma_resource[tmp].start == *dma)) return 0; diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 1dcef561a280..5aa6df8e1a5a 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -33,7 +33,9 @@ struct pnp_dev; #define pnp_port_start(dev,bar) ((dev)->res.port_resource[(bar)].start) #define pnp_port_end(dev,bar) ((dev)->res.port_resource[(bar)].end) #define pnp_port_flags(dev,bar) ((dev)->res.port_resource[(bar)].flags) -#define pnp_port_valid(dev,bar) (pnp_port_flags((dev),(bar)) & IORESOURCE_IO) +#define pnp_port_valid(dev,bar) \ + ((pnp_port_flags((dev),(bar)) & (IORESOURCE_IO | IORESOURCE_UNSET)) \ + == IORESOURCE_IO) #define pnp_port_len(dev,bar) \ ((pnp_port_start((dev),(bar)) == 0 && \ pnp_port_end((dev),(bar)) == \ @@ -45,7 +47,9 @@ struct pnp_dev; #define pnp_mem_start(dev,bar) ((dev)->res.mem_resource[(bar)].start) #define pnp_mem_end(dev,bar) ((dev)->res.mem_resource[(bar)].end) #define pnp_mem_flags(dev,bar) ((dev)->res.mem_resource[(bar)].flags) -#define pnp_mem_valid(dev,bar) (pnp_mem_flags((dev),(bar)) & IORESOURCE_MEM) +#define pnp_mem_valid(dev,bar) \ + ((pnp_mem_flags((dev),(bar)) & (IORESOURCE_MEM | IORESOURCE_UNSET)) \ + == IORESOURCE_MEM) #define pnp_mem_len(dev,bar) \ ((pnp_mem_start((dev),(bar)) == 0 && \ pnp_mem_end((dev),(bar)) == \ @@ -56,11 +60,15 @@ struct pnp_dev; #define pnp_irq(dev,bar) ((dev)->res.irq_resource[(bar)].start) #define pnp_irq_flags(dev,bar) ((dev)->res.irq_resource[(bar)].flags) -#define pnp_irq_valid(dev,bar) (pnp_irq_flags((dev),(bar)) & IORESOURCE_IRQ) +#define pnp_irq_valid(dev,bar) \ + ((pnp_irq_flags((dev),(bar)) & (IORESOURCE_IRQ | IORESOURCE_UNSET)) \ + == IORESOURCE_IRQ) #define pnp_dma(dev,bar) ((dev)->res.dma_resource[(bar)].start) #define pnp_dma_flags(dev,bar) ((dev)->res.dma_resource[(bar)].flags) -#define pnp_dma_valid(dev,bar) (pnp_dma_flags((dev),(bar)) & IORESOURCE_DMA) +#define pnp_dma_valid(dev,bar) \ + ((pnp_dma_flags((dev),(bar)) & (IORESOURCE_DMA | IORESOURCE_UNSET)) \ + == IORESOURCE_DMA) #define PNP_PORT_FLAG_16BITADDR (1<<0) #define PNP_PORT_FLAG_FIXED (1<<1) -- cgit v1.2.3 From 3f0f5c1cc3474d7c13adba438fd313123c8176dc Mon Sep 17 00:00:00 2001 From: Adam Belay Date: Sun, 8 Feb 2004 15:36:08 +0000 Subject: [PNP] Move ID declarations This patch moves the PnP ID declarations to mod_devicetable.h like most of the other buses. It is from Takashi Iwai . --- include/linux/mod_devicetable.h | 17 +++++++++++++++++ include/linux/pnp.h | 16 +--------------- 2 files changed, 18 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 67cce1744c86..c0d7ce138d99 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -148,4 +148,21 @@ struct ccw_device_id { #define CCW_DEVICE_ID_MATCH_DEVICE_MODEL 0x08 +#define PNP_ID_LEN 8 +#define PNP_MAX_DEVICES 8 + +struct pnp_device_id { + __u8 id[PNP_ID_LEN]; + kernel_ulong_t driver_data; +}; + +struct pnp_card_device_id { + __u8 id[PNP_ID_LEN]; + kernel_ulong_t driver_data; + struct { + __u8 id[PNP_ID_LEN]; + } devs[PNP_MAX_DEVICES]; +}; + + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 5aa6df8e1a5a..d728e2f5cf71 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -12,13 +12,12 @@ #include #include #include +#include #define PNP_MAX_PORT 8 #define PNP_MAX_MEM 4 #define PNP_MAX_IRQ 2 #define PNP_MAX_DMA 2 -#define PNP_MAX_DEVICES 8 -#define PNP_ID_LEN 8 #define PNP_NAME_LEN 50 struct pnp_protocol; @@ -287,19 +286,6 @@ struct pnp_id { struct pnp_id * next; }; -struct pnp_device_id { - char id[PNP_ID_LEN]; - unsigned long driver_data; /* data private to the driver */ -}; - -struct pnp_card_device_id { - char id[PNP_ID_LEN]; - unsigned long driver_data; /* data private to the driver */ - struct { - char id[PNP_ID_LEN]; - } devs[PNP_MAX_DEVICES]; /* logical devices */ -}; - struct pnp_driver { char * name; const struct pnp_device_id *id_table; -- cgit v1.2.3 From a6eebfd5c5e94e2d94a672407795e2d1a9045315 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 8 Feb 2004 03:02:20 -0800 Subject: Make SET_INPUT_KEYCODE return the old value, and clean up users of this that were very confused indeed. --- drivers/char/keyboard.c | 3 +-- drivers/input/evdev.c | 8 +++----- include/linux/input.h | 9 ++++++--- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 643014bf8c4c..010bff6e1449 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -201,8 +201,7 @@ int setkeycode(unsigned int scancode, unsigned int keycode) if (scancode < 0 || scancode >= dev->keycodemax) return -EINVAL; - oldkey = INPUT_KEYCODE(dev, scancode); - SET_INPUT_KEYCODE(dev, scancode, oldkey); + oldkey = SET_INPUT_KEYCODE(dev, scancode, keycode); clear_bit(oldkey, dev->keybit); set_bit(keycode, dev->keybit); diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 0e16eeeb9e55..9b26ff99a414 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -209,7 +209,7 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, struct evdev *evdev = list->evdev; struct input_dev *dev = evdev->handle.dev; struct input_absinfo abs; - int i, t, u, v; + int t, u, v; if (!evdev->exist) return -ENODEV; @@ -231,10 +231,8 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if (get_user(t, ((int *) arg) + 0)) return -EFAULT; if (t < 0 || t > dev->keycodemax || !dev->keycodesize) return -EINVAL; if (get_user(v, ((int *) arg) + 1)) return -EFAULT; - u = INPUT_KEYCODE(dev, t); - SET_INPUT_KEYCODE(dev, t, v); - for (i = 0; i < dev->keycodemax; i++) if (v == u) break; - if (i == dev->keycodemax) clear_bit(u, dev->keybit); + u = SET_INPUT_KEYCODE(dev, t, v); + clear_bit(u, dev->keybit); set_bit(v, dev->keybit); return 0; diff --git a/include/linux/input.h b/include/linux/input.h index 59afa63abbb3..77ba731cfb3a 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -752,25 +752,28 @@ struct ff_effect { #define init_input_dev(dev) do { INIT_LIST_HEAD(&((dev)->h_list)); INIT_LIST_HEAD(&((dev)->node)); } while (0) #define SET_INPUT_KEYCODE(dev, scancode, val) \ - do { \ + ({ unsigned __old; \ switch (dev->keycodesize) { \ case 1: { \ u8 *k = (u8 *)dev->keycode; \ + __old = k[scancode]; \ k[scancode] = val; \ break; \ } \ case 2: { \ u16 *k = (u16 *)dev->keycode; \ + __old = k[scancode]; \ k[scancode] = val; \ break; \ } \ - case 4: { \ + default: { \ u32 *k = (u32 *)dev->keycode; \ + __old = k[scancode]; \ k[scancode] = val; \ break; \ } \ } \ - } while (0) + __old; }) struct input_dev { -- cgit v1.2.3 From 016a8f324652ad92272c0b6ed8d899b231beceb0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 8 Feb 2004 19:03:22 -0800 Subject: PCI: remove stupid MSI debugging code that was never used. --- drivers/pci/msi.c | 1 - include/linux/pci_msi.h | 29 +++-------------------------- 2 files changed, 3 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 43fdb340c697..40d7cfc6c5cc 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -21,7 +21,6 @@ #include -_DEFINE_DBG_BUFFER static spinlock_t msi_lock = SPIN_LOCK_UNLOCKED; static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; diff --git a/include/linux/pci_msi.h b/include/linux/pci_msi.h index 0e42e65bc3e1..b638a07ba125 100644 --- a/include/linux/pci_msi.h +++ b/include/linux/pci_msi.h @@ -3,8 +3,8 @@ * */ -#ifndef _ASM_PCI_MSI_H -#define _ASM_PCI_MSI_H +#ifndef PCI_MSI_H +#define PCI_MSI_H #include @@ -82,29 +82,6 @@ extern void restore_ioapic_irq_handler(int irq); #define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) #define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) -extern char __dbg_str_buf[256]; -#define _DEFINE_DBG_BUFFER char __dbg_str_buf[256]; -#define _DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) -#define _DBG_K_TRACE_EXIT ((unsigned int)0x00000002) -#define _DBG_K_INFO ((unsigned int)0x00000004) -#define _DBG_K_ERROR ((unsigned int)0x00000008) -#define _DBG_K_TRACE (_DBG_K_TRACE_ENTRY | _DBG_K_TRACE_EXIT) - -#define _DEBUG_LEVEL (_DBG_K_INFO | _DBG_K_ERROR | _DBG_K_TRACE) -#define _DBG_PRINT( dbg_flags, args... ) \ -if ( _DEBUG_LEVEL & (dbg_flags) ) \ -{ \ - int len; \ - len = sprintf(__dbg_str_buf, "%s:%d: %s ", \ - __FILE__, __LINE__, __FUNCTION__ ); \ - sprintf(__dbg_str_buf + len, args); \ - printk(KERN_INFO "%s\n", __dbg_str_buf); \ -} - -#define MSI_FUNCTION_TRACE_ENTER \ - _DBG_PRINT (_DBG_K_TRACE_ENTRY, "%s", "[Entry]"); -#define MSI_FUNCTION_TRACE_EXIT \ - _DBG_PRINT (_DBG_K_TRACE_EXIT, "%s", "[Entry]"); /* * MSI Defined Data Structures @@ -190,4 +167,4 @@ struct msi_desc { struct pci_dev *dev; }; -#endif /* _ASM_PCI_MSI_H */ +#endif /* PCI_MSI_H */ -- cgit v1.2.3 From 694924e419744f6ea6efdd1c7e4e504ef5a5080f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 8 Feb 2004 19:04:55 -0800 Subject: PCI: move pci_msi.h out of include/linux to drivers/pci where it belongs. --- drivers/pci/msi.c | 2 +- drivers/pci/msi.h | 168 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_msi.h | 170 ------------------------------------------------ 3 files changed, 169 insertions(+), 171 deletions(-) create mode 100644 drivers/pci/msi.h delete mode 100644 include/linux/pci_msi.h (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 40d7cfc6c5cc..2090a7089e41 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -19,7 +19,7 @@ #include #include -#include +#include "msi.h" static spinlock_t msi_lock = SPIN_LOCK_UNLOCKED; diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h new file mode 100644 index 000000000000..dd1c359e7a88 --- /dev/null +++ b/drivers/pci/msi.h @@ -0,0 +1,168 @@ +/* + * msi.h + * + */ + +#ifndef MSI_H +#define MSI_H + +#define MSI_AUTO -1 +#define NR_REPEATS 23 +#define NR_RESERVED_VECTORS 3 /*FIRST_DEVICE_VECTOR,FIRST_SYSTEM_VECTOR,0x80 */ + +/* + * Assume the maximum number of hot plug slots supported by the system is about + * ten. The worstcase is that each of these slots is hot-added with a device, + * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which + * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined + * as below to ensure at least one message is assigned to each detected MSI/ + * MSI-X device function. + */ +#define NR_HP_RESERVED_VECTORS 20 + +extern int vector_irq[NR_IRQS]; +extern cpumask_t pending_irq_balance_cpumask[NR_IRQS]; +extern void (*interrupt[NR_IRQS])(void); + +#ifdef CONFIG_SMP +#define set_msi_irq_affinity set_msi_affinity +#else +#define set_msi_irq_affinity NULL +static inline void move_msi(int vector) {} +#endif + +#ifndef CONFIG_X86_IO_APIC +static inline int get_ioapic_vector(struct pci_dev *dev) { return -1;} +static inline void restore_ioapic_irq_handler(int irq) {} +#else +extern void restore_ioapic_irq_handler(int irq); +#endif + +/* + * MSI-X Address Register + */ +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_MSIX_FLAGS_BITMASK (1 << 0) + +#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0 +#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4 +#define PCI_MSIX_ENTRY_DATA_OFFSET 8 +#define PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET 12 +#define PCI_MSIX_ENTRY_SIZE 16 + +#define msi_control_reg(base) (base + PCI_MSI_FLAGS) +#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO) +#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI) +#define msi_data_reg(base, is64bit) \ + ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 ) +#define msi_mask_bits_reg(base, is64bit) \ + ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) +#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE +#define multi_msi_capable(control) \ + (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) +#define multi_msi_enable(control, num) \ + control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); +#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT) +#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT) +#define msi_enable(control, num) multi_msi_enable(control, num); \ + control |= PCI_MSI_FLAGS_ENABLE + +#define msix_control_reg msi_control_reg +#define msix_table_offset_reg(base) (base + 0x04) +#define msix_pba_offset_reg(base) (base + 0x08) +#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE +#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE +#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) +#define multi_msix_capable msix_table_size +#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK) +#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) +#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) + + +/* + * MSI Defined Data Structures + */ +#define MSI_ADDRESS_HEADER 0xfee +#define MSI_ADDRESS_HEADER_SHIFT 12 +#define MSI_ADDRESS_HEADER_MASK 0xfff000 +#define MSI_TARGET_CPU_SHIFT 4 +#define MSI_TARGET_CPU_MASK 0xff +#define MSI_DELIVERY_MODE 0 +#define MSI_LEVEL_MODE 1 /* Edge always assert */ +#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */ +#define MSI_LOGICAL_MODE 1 +#define MSI_REDIRECTION_HINT_MODE 0 +#ifdef CONFIG_SMP +#define MSI_TARGET_CPU logical_smp_processor_id() +#else +#define MSI_TARGET_CPU TARGET_CPUS +#endif + +struct msg_data { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u32 vector : 8; + __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ + __u32 reserved_1 : 3; + __u32 level : 1; /* 0: deassert | 1: assert */ + __u32 trigger : 1; /* 0: edge | 1: level */ + __u32 reserved_2 : 16; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u32 reserved_2 : 16; + __u32 trigger : 1; /* 0: edge | 1: level */ + __u32 level : 1; /* 0: deassert | 1: assert */ + __u32 reserved_1 : 3; + __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ + __u32 vector : 8; +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif +} __attribute__ ((packed)); + +struct msg_address { + union { + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u32 reserved_1 : 2; + __u32 dest_mode : 1; /*0:physic | 1:logic */ + __u32 redirection_hint: 1; /*0: dedicated CPU + 1: lowest priority */ + __u32 reserved_2 : 4; + __u32 dest_id : 24; /* Destination ID */ +#elif defined(__BIG_ENDIAN_BITFIELD) + __u32 dest_id : 24; /* Destination ID */ + __u32 reserved_2 : 4; + __u32 redirection_hint: 1; /*0: dedicated CPU + 1: lowest priority */ + __u32 dest_mode : 1; /*0:physic | 1:logic */ + __u32 reserved_1 : 2; +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + }u; + __u32 value; + }lo_address; + __u32 hi_address; +} __attribute__ ((packed)); + +struct msi_desc { + struct { + __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ + __u8 maskbit : 1; /* mask-pending bit supported ? */ + __u8 reserved: 2; /* reserved */ + __u8 entry_nr; /* specific enabled entry */ + __u8 default_vector; /* default pre-assigned vector */ + __u8 current_cpu; /* current destination cpu */ + }msi_attrib; + + struct { + __u16 head; + __u16 tail; + }link; + + unsigned long mask_base; + struct pci_dev *dev; +}; + +#endif /* MSI_H */ diff --git a/include/linux/pci_msi.h b/include/linux/pci_msi.h deleted file mode 100644 index b638a07ba125..000000000000 --- a/include/linux/pci_msi.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * ../include/linux/pci_msi.h - * - */ - -#ifndef PCI_MSI_H -#define PCI_MSI_H - -#include - -#define MSI_AUTO -1 -#define NR_REPEATS 23 -#define NR_RESERVED_VECTORS 3 /*FIRST_DEVICE_VECTOR,FIRST_SYSTEM_VECTOR,0x80 */ - -/* - * Assume the maximum number of hot plug slots supported by the system is about - * ten. The worstcase is that each of these slots is hot-added with a device, - * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which - * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined - * as below to ensure at least one message is assigned to each detected MSI/ - * MSI-X device function. - */ -#define NR_HP_RESERVED_VECTORS 20 - -extern int vector_irq[NR_IRQS]; -extern cpumask_t pending_irq_balance_cpumask[NR_IRQS]; -extern void (*interrupt[NR_IRQS])(void); - -#ifdef CONFIG_SMP -#define set_msi_irq_affinity set_msi_affinity -#else -#define set_msi_irq_affinity NULL -static inline void move_msi(int vector) {} -#endif - -#ifndef CONFIG_X86_IO_APIC -static inline int get_ioapic_vector(struct pci_dev *dev) { return -1;} -static inline void restore_ioapic_irq_handler(int irq) {} -#else -extern void restore_ioapic_irq_handler(int irq); -#endif - -/* - * MSI-X Address Register - */ -#define PCI_MSIX_FLAGS_QSIZE 0x7FF -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) -#define PCI_MSIX_FLAGS_BITMASK (1 << 0) - -#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0 -#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4 -#define PCI_MSIX_ENTRY_DATA_OFFSET 8 -#define PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET 12 -#define PCI_MSIX_ENTRY_SIZE 16 - -#define msi_control_reg(base) (base + PCI_MSI_FLAGS) -#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO) -#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI) -#define msi_data_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 ) -#define msi_mask_bits_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) -#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE -#define multi_msi_capable(control) \ - (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) -#define multi_msi_enable(control, num) \ - control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); -#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT) -#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT) -#define msi_enable(control, num) multi_msi_enable(control, num); \ - control |= PCI_MSI_FLAGS_ENABLE - -#define msix_control_reg msi_control_reg -#define msix_table_offset_reg(base) (base + 0x04) -#define msix_pba_offset_reg(base) (base + 0x08) -#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE -#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE -#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) -#define multi_msix_capable msix_table_size -#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK) -#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) -#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) - - -/* - * MSI Defined Data Structures - */ -#define MSI_ADDRESS_HEADER 0xfee -#define MSI_ADDRESS_HEADER_SHIFT 12 -#define MSI_ADDRESS_HEADER_MASK 0xfff000 -#define MSI_TARGET_CPU_SHIFT 4 -#define MSI_TARGET_CPU_MASK 0xff -#define MSI_DELIVERY_MODE 0 -#define MSI_LEVEL_MODE 1 /* Edge always assert */ -#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */ -#define MSI_LOGICAL_MODE 1 -#define MSI_REDIRECTION_HINT_MODE 0 -#ifdef CONFIG_SMP -#define MSI_TARGET_CPU logical_smp_processor_id() -#else -#define MSI_TARGET_CPU TARGET_CPUS -#endif - -struct msg_data { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u32 vector : 8; - __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ - __u32 reserved_1 : 3; - __u32 level : 1; /* 0: deassert | 1: assert */ - __u32 trigger : 1; /* 0: edge | 1: level */ - __u32 reserved_2 : 16; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u32 reserved_2 : 16; - __u32 trigger : 1; /* 0: edge | 1: level */ - __u32 level : 1; /* 0: deassert | 1: assert */ - __u32 reserved_1 : 3; - __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ - __u32 vector : 8; -#else -#error "Bitfield endianness not defined! Check your byteorder.h" -#endif -} __attribute__ ((packed)); - -struct msg_address { - union { - struct { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u32 reserved_1 : 2; - __u32 dest_mode : 1; /*0:physic | 1:logic */ - __u32 redirection_hint: 1; /*0: dedicated CPU - 1: lowest priority */ - __u32 reserved_2 : 4; - __u32 dest_id : 24; /* Destination ID */ -#elif defined(__BIG_ENDIAN_BITFIELD) - __u32 dest_id : 24; /* Destination ID */ - __u32 reserved_2 : 4; - __u32 redirection_hint: 1; /*0: dedicated CPU - 1: lowest priority */ - __u32 dest_mode : 1; /*0:physic | 1:logic */ - __u32 reserved_1 : 2; -#else -#error "Bitfield endianness not defined! Check your byteorder.h" -#endif - }u; - __u32 value; - }lo_address; - __u32 hi_address; -} __attribute__ ((packed)); - -struct msi_desc { - struct { - __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ - __u8 maskbit : 1; /* mask-pending bit supported ? */ - __u8 reserved: 2; /* reserved */ - __u8 entry_nr; /* specific enabled entry */ - __u8 default_vector; /* default pre-assigned vector */ - __u8 current_cpu; /* current destination cpu */ - }msi_attrib; - - struct { - __u16 head; - __u16 tail; - }link; - - unsigned long mask_base; - struct pci_dev *dev; -}; - -#endif /* PCI_MSI_H */ -- cgit v1.2.3 From a5cfa3a229b52bce6155190df6f640248ceb9c0f Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Sun, 8 Feb 2004 19:14:24 -0800 Subject: [SCTP] Removed SCTP specific rmem/wmem sysctl's and use the global rmem_default/wmem_default values for SCTP socket buffer sizes. --- include/net/sctp/structs.h | 6 ------ net/sctp/associola.c | 4 ++-- net/sctp/endpointola.c | 4 ---- net/sctp/protocol.c | 4 ---- net/sctp/sysctl.c | 16 ---------------- net/sctp/ulpqueue.c | 2 +- 6 files changed, 3 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index fa98456f5a0b..92394f3de76a 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -193,10 +193,6 @@ extern struct sctp_globals { /* Flag to indicate if addip is enabled. */ int addip_enable; - - /* socket receive and send buffer sizes. */ - int rmem; - int wmem; } sctp_globals; #define sctp_rto_initial (sctp_globals.rto_initial) @@ -225,8 +221,6 @@ extern struct sctp_globals { #define sctp_local_addr_list (sctp_globals.local_addr_list) #define sctp_local_addr_lock (sctp_globals.local_addr_lock) #define sctp_addip_enable (sctp_globals.addip_enable) -#define sctp_rmem (sctp_globals.rmem) -#define sctp_wmem (sctp_globals.wmem) /* SCTP Socket type: UDP or TCP style. */ typedef enum { diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 29fa694a2344..5c15d829abba 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -192,7 +192,7 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc, asoc->rwnd_over = 0; /* Use my own max window until I learn something better. */ - asoc->peer.rwnd = sctp_rmem; + asoc->peer.rwnd = SCTP_DEFAULT_MAXWINDOW; /* Set the sndbuf size for transmit. */ asoc->sndbuf_used = 0; @@ -498,7 +498,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, * so initialize ssthresh to the default value and it will be set * later when we process the INIT. */ - peer->ssthresh = sctp_rmem; + peer->ssthresh = SCTP_DEFAULT_MAXWINDOW; peer->partial_bytes_acked = 0; peer->flight_size = 0; diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 6391db36d14b..e706368b6aa7 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -147,10 +147,6 @@ struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = sp->autoclose * HZ; - /* Set up the default send/receive buffer space. */ - sk->sk_rcvbuf = sctp_rmem; - sk->sk_sndbuf = sctp_wmem; - /* Use SCTP specific send buffer space queues. */ sk->sk_write_space = sctp_write_space; sk->sk_use_write_queue = 1; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 2e796280fe29..e903cf341126 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1049,10 +1049,6 @@ __init int sctp_init(void) sctp_max_instreams = SCTP_DEFAULT_INSTREAMS; sctp_max_outstreams = SCTP_DEFAULT_OUTSTREAMS; - /* Initialize default send & receive buffer sizes. */ - sctp_rmem = SCTP_DEFAULT_MAXWINDOW; - sctp_wmem = SCTP_DEFAULT_MAXWINDOW; - /* Size and allocate the association hash table. * The methodology is similar to that of the tcp hash tables. */ diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 573278445bb4..15c5f3bfecbf 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -170,22 +170,6 @@ static ctl_table sctp_table[] = { .mode = 0644, .proc_handler = &proc_dointvec }, - { - .ctl_name = NET_SCTP_RMEM, - .procname = "rmem", - .data = &sctp_rmem, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { - .ctl_name = NET_SCTP_WMEM, - .procname = "wmem", - .data = &sctp_wmem, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, { .ctl_name = 0 } }; diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index d9ec4affd16a..a02b2143239b 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -768,7 +768,7 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk, needed = ntohs(chunk->chunk_hdr->length); needed -= sizeof(sctp_data_chunk_t); } else - needed = sctp_rmem; + needed = SCTP_DEFAULT_MAXWINDOW; freed = 0; -- cgit v1.2.3 From bc7e061c860f4bb766c3b013c4c554cb01d87290 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 8 Feb 2004 20:16:07 -0800 Subject: [PATCH] USB: re-factor enumeration/reset paths This patch starts dis-entangling some of the enumeration logic by moving initialization code into the usb_alloc_dev() constructor. Some call signatures changed; a usbcore-internal declaration was moved in to a more appropriate location. With the driver model init now more centralized, it's safer to use driver model calls (including dev_info) a lot earlier, so the "new device at address N" message now does that. It also reports the device speed, which may not be evident otherwise. --- drivers/usb/core/hcd.c | 10 +++---- drivers/usb/core/hcd.h | 4 ++- drivers/usb/core/hub.c | 44 +++++++--------------------- drivers/usb/core/usb.c | 65 +++++++++++++++++++++++++----------------- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/hc_sl811_rh.c | 2 +- drivers/usb/host/ohci-hcd.c | 2 +- drivers/usb/host/uhci-hcd.c | 2 +- include/linux/usb.h | 1 - 9 files changed, 62 insertions(+), 70 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 8ee641df6b9c..e76cb4c1cf4d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -738,7 +738,7 @@ EXPORT_SYMBOL (usb_deregister_bus); * * The USB host controller calls this function to register the root hub * properly with the USB subsystem. It sets up the device properly in - * the driverfs tree, and then calls usb_new_device() to register the + * the device model tree, and then calls usb_new_device() to register the * usb device. It also assigns the root hub's USB address (always 1). */ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev) @@ -746,14 +746,14 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev const int devnum = 1; int retval; - sprintf (&usb_dev->dev.bus_id[0], "usb%d", usb_dev->bus->busnum); - usb_dev->state = USB_STATE_DEFAULT; - usb_dev->devnum = devnum; usb_dev->bus->devnum_next = devnum + 1; + memset (&usb_dev->bus->devmap.devicemap, 0, + sizeof usb_dev->bus->devmap.devicemap); set_bit (devnum, usb_dev->bus->devmap.devicemap); + usb_dev->state = USB_STATE_ADDRESS; - retval = usb_new_device (usb_dev, parent_dev); + retval = usb_new_device (usb_dev); if (retval) dev_err (parent_dev, "can't register root hub for %s, %d\n", usb_dev->dev.bus_id, retval); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 1d0ea74d0148..725a92f29092 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -241,7 +241,9 @@ extern void usb_hc_died (struct usb_hcd *hcd); /* -------------------------------------------------------------------------- */ /* Enumeration is only for the hub driver, or HCD virtual root hubs */ -extern int usb_new_device(struct usb_device *dev, struct device *parent); +extern struct usb_device *usb_alloc_dev(struct usb_device *parent, + struct usb_bus *, unsigned port); +extern int usb_new_device(struct usb_device *dev); extern void usb_choose_address(struct usb_device *dev); extern void usb_disconnect(struct usb_device **); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 6c2976d24562..e9db8522acd7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -930,11 +930,9 @@ static void hub_port_connect_change(struct usb_hub *hubstate, int port, down(&usb_address0_sem); for (i = 0; i < HUB_PROBE_TRIES; i++) { - struct usb_device *pdev; - int len; /* Allocate a new device struct */ - dev = usb_alloc_dev(hub, hub->bus); + dev = usb_alloc_dev(hub, hub->bus, port); if (!dev) { dev_err (&hubstate->intf->dev, "couldn't allocate usb_device\n"); @@ -962,38 +960,18 @@ static void hub_port_connect_change(struct usb_hub *hubstate, int port, dev->ttport = port + 1; } - /* Save readable and stable topology id, distinguishing - * devices by location for diagnostics, tools, etc. The - * string is a path along hub ports, from the root. Each - * device's id will be stable until USB is re-cabled, and - * hubs are often labeled with these port numbers. - * - * Initial size: ".NN" times five hubs + NUL = 16 bytes max - * (quite rare, since most hubs have 4-6 ports). - */ - pdev = dev->parent; - if (pdev->devpath [0] != '0') /* parent not root? */ - len = snprintf (dev->devpath, sizeof dev->devpath, - "%s.%d", pdev->devpath, port + 1); - /* root == "0", root port 2 == "2", port 3 that hub "2.3" */ - else - len = snprintf (dev->devpath, sizeof dev->devpath, - "%d", port + 1); - if (len == sizeof dev->devpath) - dev_err (&hubstate->intf->dev, - "devpath size! usb/%03d/%03d path %s\n", - dev->bus->busnum, dev->devnum, dev->devpath); - dev_info (&hubstate->intf->dev, - "new USB device on port %d, assigned address %d\n", - port + 1, dev->devnum); - - /* put the device in the global device tree. the hub port - * is the "bus_id"; hubs show in hierarchy like bridges - */ - dev->dev.parent = dev->parent->dev.parent->parent; + dev_info (&dev->dev, + "new %s speed USB device using address %d\n", + ({ char *speed; switch (dev->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + }; speed;}), + dev->devnum); /* Run it through the hoops (find a driver, etc) */ - if (!usb_new_device(dev, &hub->dev)) { + if (usb_new_device(dev) == 0) { hub->children[port] = dev; goto done; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 378d51ce2374..07ddb791beed 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -678,17 +678,19 @@ static void usb_release_dev(struct device *dev) } /** - * usb_alloc_dev - allocate a usb device structure (usbcore-internal) - * @parent: hub to which device is connected + * usb_alloc_dev - usb device constructor (usbcore-internal) + * @parent: hub to which device is connected; null to allocate a root hub * @bus: bus used to access the device + * @port: zero based index of port; ignored for root hubs * Context: !in_interrupt () * * Only hub drivers (including virtual root hub drivers for host * controllers) should ever call this. * - * This call is synchronous, and may not be used in an interrupt context. + * This call may not be used in a non-sleeping context. */ -struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) +struct usb_device * +usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port) { struct usb_device *dev; @@ -705,11 +707,42 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) } device_initialize(&dev->dev); + dev->dev.bus = &usb_bus_type; + dev->dev.dma_mask = bus->controller->dma_mask; + dev->dev.driver_data = &usb_generic_driver_data; + dev->dev.driver = &usb_generic_driver; dev->dev.release = usb_release_dev; dev->state = USB_STATE_ATTACHED; - if (!parent) + /* Save readable and stable topology id, distinguishing devices + * by location for diagnostics, tools, driver model, etc. The + * string is a path along hub ports, from the root. Each device's + * dev->devpath will be stable until USB is re-cabled, and hubs + * are often labeled with these port numbers. The bus_id isn't + * as stable: bus->busnum changes easily from modprobe order, + * cardbus or pci hotplugging, and so on. + */ + if (unlikely (!parent)) { dev->devpath [0] = '0'; + + dev->dev.parent = bus->controller; + sprintf (&dev->dev.bus_id[0], "usb%d", bus->busnum); + } else { + /* match any labeling on the hubs; it's one-based */ + if (parent->devpath [0] == '0') + snprintf (dev->devpath, sizeof dev->devpath, + "%d", port + 1); + else + snprintf (dev->devpath, sizeof dev->devpath, + "%s.%d", parent->devpath, port + 1); + + dev->dev.parent = &parent->dev; + sprintf (&dev->dev.bus_id[0], "%d-%s", + bus->busnum, dev->devpath); + + /* hub driver sets up TT records */ + } + dev->bus = bus; dev->parent = parent; INIT_LIST_HEAD(&dev->filelist); @@ -1011,33 +1044,13 @@ static inline void usb_show_string(struct usb_device *dev, char *id, int index) */ #define NEW_DEVICE_RETRYS 2 #define SET_ADDRESS_RETRYS 2 -int usb_new_device(struct usb_device *dev, struct device *parent) +int usb_new_device(struct usb_device *dev) { int err = -EINVAL; int i; int j; int config; - /* - * Set the driver for the usb device to point to the "generic" driver. - * This prevents the main usb device from being sent to the usb bus - * probe function. Yes, it's a hack, but a nice one :) - * - * Do it asap, so more driver model stuff (like the device.h message - * utilities) can be used in hcd submit/unlink code paths. - */ - usb_generic_driver.bus = &usb_bus_type; - dev->dev.parent = parent; - dev->dev.driver = &usb_generic_driver; - dev->dev.bus = &usb_bus_type; - dev->dev.driver_data = &usb_generic_driver_data; - if (dev->dev.bus_id[0] == 0) - sprintf (&dev->dev.bus_id[0], "%d-%s", - dev->bus->busnum, dev->devpath); - - /* dma masks come from the controller; readonly, except to hcd */ - dev->dev.dma_mask = parent->dma_mask; - /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... * it's fixed size except for full speed devices. */ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 5c58a16c7693..0db191fb8ea0 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -473,7 +473,7 @@ static int ehci_start (struct usb_hcd *hcd) /* wire up the root hub */ bus = hcd_to_bus (hcd); - bus->root_hub = udev = usb_alloc_dev (NULL, bus); + bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0); if (!udev) { done2: ehci_mem_cleanup (ehci); diff --git a/drivers/usb/host/hc_sl811_rh.c b/drivers/usb/host/hc_sl811_rh.c index 42ec077b72bf..a4dd4628ed66 100644 --- a/drivers/usb/host/hc_sl811_rh.c +++ b/drivers/usb/host/hc_sl811_rh.c @@ -559,7 +559,7 @@ static int rh_connect_rh (hci_t * hci) struct usb_device *usb_dev; hci->rh.devnum = 0; - usb_dev = usb_alloc_dev (NULL, hci->bus); + usb_dev = usb_alloc_dev (NULL, hci->bus, 0); if (!usb_dev) return -ENOMEM; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index cc2c9c259197..53c86a42d4ba 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -536,7 +536,7 @@ static int hc_start (struct ohci_hcd *ohci) /* connect the virtual root hub */ bus = hcd_to_bus (&ohci->hcd); - bus->root_hub = udev = usb_alloc_dev (NULL, bus); + bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0); ohci->hcd.state = USB_STATE_RUNNING; if (!udev) { disable (ohci); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 652f85120d75..0678fdf8d7b1 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -2315,7 +2315,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->rh_numports = port; - hcd->self.root_hub = udev = usb_alloc_dev(NULL, &hcd->self); + hcd->self.root_hub = udev = usb_alloc_dev(NULL, &hcd->self, 0); if (!udev) { err("unable to allocate root hub"); goto err_alloc_root_hub; diff --git a/include/linux/usb.h b/include/linux/usb.h index c16b0125baa0..45caca344ca3 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -274,7 +274,6 @@ struct usb_device { }; #define to_usb_device(d) container_of(d, struct usb_device, dev) -extern struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *); extern struct usb_device *usb_get_dev(struct usb_device *dev); extern void usb_put_dev(struct usb_device *dev); -- cgit v1.2.3 From a1eefc490265c01da425ed7bd2458ca1a67fca8a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 8 Feb 2004 21:18:09 -0800 Subject: [PATCH] I2C: add new chip driver: fscher This is a new ported driver, fscher, which supports the FSC Hermes chip. The original driver was written by Reinhard Nissl, who also ported it to Linux 2.6, as discussed on the lm_sensors mailing list during the last two weeks. I reviewed the code and we made the necessary changes, so that what we have now looks good to me. Please apply on top of your i2c patches stack. --- drivers/i2c/chips/Kconfig | 11 + drivers/i2c/chips/Makefile | 1 + drivers/i2c/chips/fscher.c | 681 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c-id.h | 1 + 4 files changed, 694 insertions(+) create mode 100644 drivers/i2c/chips/fscher.c (limited to 'include') diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 50cdb11a4723..8386041a3e7f 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -45,6 +45,17 @@ config SENSORS_EEPROM This driver can also be built as a module. If so, the module will be called eeprom. +config SENSORS_FSCHER + tristate "FSC Hermes" + depends on I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Fujitsu Siemens + Computers Hermes sensor chips. + + This driver can also be built as a module. If so, the module + will be called fscher. + config SENSORS_GL518SM tristate "Genesys Logic GL518SM" depends on I2C && EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 11d592ca28ae..c673ed1b4573 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o +obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_LM75) += lm75.o diff --git a/drivers/i2c/chips/fscher.c b/drivers/i2c/chips/fscher.c new file mode 100644 index 000000000000..3140b6d2a3d9 --- /dev/null +++ b/drivers/i2c/chips/fscher.c @@ -0,0 +1,681 @@ +/* + * fscher.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003, 2004 Reinhard Nissl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * fujitsu siemens hermes chip, + * module based on fscpos.c + * Copyright (C) 2000 Hermann Jung + * Copyright (C) 1998, 1999 Frodo Looijaard + * and Philip Edelbrock + */ + +#include +#include +#include +#include +#include + +/* + * Addresses to scan + */ + +static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; +static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(fscher); + +/* + * The FSCHER registers + */ + +/* chip identification */ +#define FSCHER_REG_IDENT_0 0x00 +#define FSCHER_REG_IDENT_1 0x01 +#define FSCHER_REG_IDENT_2 0x02 +#define FSCHER_REG_REVISION 0x03 + +/* global control and status */ +#define FSCHER_REG_EVENT_STATE 0x04 +#define FSCHER_REG_CONTROL 0x05 + +/* watchdog */ +#define FSCHER_REG_WDOG_PRESET 0x28 +#define FSCHER_REG_WDOG_STATE 0x23 +#define FSCHER_REG_WDOG_CONTROL 0x21 + +/* fan 0 */ +#define FSCHER_REG_FAN0_MIN 0x55 +#define FSCHER_REG_FAN0_ACT 0x0e +#define FSCHER_REG_FAN0_STATE 0x0d +#define FSCHER_REG_FAN0_RIPPLE 0x0f + +/* fan 1 */ +#define FSCHER_REG_FAN1_MIN 0x65 +#define FSCHER_REG_FAN1_ACT 0x6b +#define FSCHER_REG_FAN1_STATE 0x62 +#define FSCHER_REG_FAN1_RIPPLE 0x6f + +/* fan 2 */ +#define FSCHER_REG_FAN2_MIN 0xb5 +#define FSCHER_REG_FAN2_ACT 0xbb +#define FSCHER_REG_FAN2_STATE 0xb2 +#define FSCHER_REG_FAN2_RIPPLE 0xbf + +/* voltage supervision */ +#define FSCHER_REG_VOLT_12 0x45 +#define FSCHER_REG_VOLT_5 0x42 +#define FSCHER_REG_VOLT_BATT 0x48 + +/* temperature 0 */ +#define FSCHER_REG_TEMP0_ACT 0x64 +#define FSCHER_REG_TEMP0_STATE 0x71 + +/* temperature 1 */ +#define FSCHER_REG_TEMP1_ACT 0x32 +#define FSCHER_REG_TEMP1_STATE 0x81 + +/* temperature 2 */ +#define FSCHER_REG_TEMP2_ACT 0x35 +#define FSCHER_REG_TEMP2_STATE 0x91 + +/* + * Functions declaration + */ + +static int fscher_attach_adapter(struct i2c_adapter *adapter); +static int fscher_detect(struct i2c_adapter *adapter, int address, int kind); +static int fscher_detach_client(struct i2c_client *client); +static void fscher_update_client(struct i2c_client *client); +static void fscher_init_client(struct i2c_client *client); + +static int fscher_read_value(struct i2c_client *client, u8 reg); +static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver fscher_driver = { + .owner = THIS_MODULE, + .name = "fscher", + .id = I2C_DRIVERID_FSCHER, + .flags = I2C_DF_NOTIFY, + .attach_adapter = fscher_attach_adapter, + .detach_client = fscher_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct fscher_data { + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* register values */ + u8 revision; /* revision of chip */ + u8 global_event; /* global event status */ + u8 global_control; /* global control register */ + u8 watchdog[3]; /* watchdog */ + u8 volt[3]; /* 12, 5, battery voltage */ + u8 temp_act[3]; /* temperature */ + u8 temp_status[3]; /* status of sensor */ + u8 fan_act[3]; /* fans revolutions per second */ + u8 fan_status[3]; /* fan status */ + u8 fan_min[3]; /* fan min value for rps */ + u8 fan_ripple[3]; /* divider for rps */ +}; + +/* + * Internal variables + */ + +static int fscher_id = 0; + +/* + * Sysfs stuff + */ + +#define sysfs_r(kind, offset, reg) \ +static ssize_t show_##kind (struct fscher_data *, char *, int); \ +static ssize_t show_##kind##offset (struct device *, char *); \ +static ssize_t show_##kind##offset (struct device *dev, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct fscher_data *data = i2c_get_clientdata(client); \ + fscher_update_client(client); \ + return show_##kind(data, buf, (offset)); \ +} + +#define sysfs_w(kind, offset, reg) \ +static ssize_t set_##kind (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \ +static ssize_t set_##kind##offset (struct device *, const char *, size_t); \ +static ssize_t set_##kind##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct fscher_data *data = i2c_get_clientdata(client); \ + return set_##kind(client, data, buf, count, (offset), reg); \ +} + +#define sysfs_rw_n(kind, offset, reg) \ +sysfs_r(kind, offset, reg) \ +sysfs_w(kind, offset, reg) \ +static DEVICE_ATTR(kind##offset, S_IRUGO | S_IWUSR, show_##kind##offset, set_##kind##offset); + +#define sysfs_rw(kind, reg) \ +sysfs_r(kind, 0, reg) \ +sysfs_w(kind, 0, reg) \ +static DEVICE_ATTR(kind, S_IRUGO | S_IWUSR, show_##kind##0, set_##kind##0); + +#define sysfs_ro_n(kind, offset, reg) \ +sysfs_r(kind, offset, reg) \ +static DEVICE_ATTR(kind##offset, S_IRUGO, show_##kind##offset, NULL); + +#define sysfs_ro(kind, reg) \ +sysfs_r(kind, 0, reg) \ +static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0, NULL); + +#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \ +sysfs_rw_n(pwm , offset, reg_min) \ +sysfs_rw_n(fan_status, offset, reg_status) \ +sysfs_rw_n(fan_div , offset, reg_ripple) \ +sysfs_ro_n(fan_input , offset, reg_act) + +#define sysfs_temp(offset, reg_status, reg_act) \ +sysfs_rw_n(temp_status, offset, reg_status) \ +sysfs_ro_n(temp_input , offset, reg_act) + +#define sysfs_in(offset, reg_act) \ +sysfs_ro_n(in_input, offset, reg_act) + +#define sysfs_revision(reg_revision) \ +sysfs_ro(revision, reg_revision) + +#define sysfs_alarms(reg_events) \ +sysfs_ro(alarms, reg_events) + +#define sysfs_control(reg_control) \ +sysfs_rw(control, reg_control) + +#define sysfs_watchdog(reg_control, reg_status, reg_preset) \ +sysfs_rw(watchdog_control, reg_control) \ +sysfs_rw(watchdog_status , reg_status) \ +sysfs_rw(watchdog_preset , reg_preset) + +sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN, + FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT) +sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN, + FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT) +sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN, + FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT) + +sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT) +sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT) +sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT) + +sysfs_in(0, FSCHER_REG_VOLT_12) +sysfs_in(1, FSCHER_REG_VOLT_5) +sysfs_in(2, FSCHER_REG_VOLT_BATT) + +sysfs_revision(FSCHER_REG_REVISION) +sysfs_alarms(FSCHER_REG_EVENTS) +sysfs_control(FSCHER_REG_CONTROL) +sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET) + +#define device_create_file_fan(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_fan_status##offset); \ + device_create_file(&client->dev, &dev_attr_pwm##offset); \ + device_create_file(&client->dev, &dev_attr_fan_div##offset); \ + device_create_file(&client->dev, &dev_attr_fan_input##offset); \ +} while (0) + +#define device_create_file_temp(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_temp_status##offset); \ + device_create_file(&client->dev, &dev_attr_temp_input##offset); \ +} while (0) + +#define device_create_file_in(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_in_input##offset); \ +} while (0) + +#define device_create_file_revision(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_revision); \ +} while (0) + +#define device_create_file_alarms(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_alarms); \ +} while (0) + +#define device_create_file_control(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_control); \ +} while (0) + +#define device_create_file_watchdog(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_watchdog_status); \ + device_create_file(&client->dev, &dev_attr_watchdog_control); \ + device_create_file(&client->dev, &dev_attr_watchdog_preset); \ +} while (0) + +/* + * Real code + */ + +static int fscher_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) + return 0; + return i2c_detect(adapter, &addr_data, fscher_detect); +} + +static int fscher_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct fscher_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + * client structure, even though we cannot fill it completely yet. + * But it allows us to access i2c_smbus_read_byte_data. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct fscher_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(new_client, 0x00, sizeof(struct i2c_client) + + sizeof(struct fscher_data)); + + /* The Hermes-specific data is placed right after the common I2C + * client data. */ + data = (struct fscher_data *) (new_client + 1); + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &fscher_driver; + new_client->flags = 0; + + /* Do the remaining detection unless force or force_fscher parameter */ + if (kind < 0) { + if ((i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_0) != 0x48) /* 'H' */ + || (i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_1) != 0x45) /* 'E' */ + || (i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */ + goto exit_free; + } + + /* Fill in the remaining client fields and put it into the + * global list */ + strlcpy(new_client->name, "fscher", I2C_NAME_SIZE); + new_client->id = fscher_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + fscher_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file_revision(new_client); + device_create_file_alarms(new_client); + device_create_file_control(new_client); + device_create_file_watchdog(new_client); + + device_create_file_in(new_client, 0); + device_create_file_in(new_client, 1); + device_create_file_in(new_client, 2); + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + device_create_file_fan(new_client, 3); + + device_create_file_temp(new_client, 1); + device_create_file_temp(new_client, 2); + device_create_file_temp(new_client, 3); + + return 0; + +exit_free: + kfree(new_client); +exit: + return err; +} + +static int fscher_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(client); + return 0; +} + +static int fscher_read_value(struct i2c_client *client, u8 reg) +{ + dev_dbg(&client->dev, "read reg 0x%02x\n", reg); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n", + reg, value); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new FSC Hermes. */ +static void fscher_init_client(struct i2c_client *client) +{ + struct fscher_data *data = i2c_get_clientdata(client); + + /* Read revision from chip */ + data->revision = fscher_read_value(client, FSCHER_REG_REVISION); +} + +static void fscher_update_client(struct i2c_client *client) +{ + struct fscher_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + + dev_dbg(&client->dev, "Starting fscher update\n"); + + data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT); + data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT); + data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT); + data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE); + data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE); + data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE); + + data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12); + data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5); + data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT); + + data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT); + data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT); + data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT); + data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE); + data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE); + data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE); + data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN); + data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN); + data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN); + data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE); + data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE); + data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE); + + data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET); + data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE); + data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL); + + data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + + +#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1) + +static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 0..1, 3..7 reserved => mask with 0x04 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04; + data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v; + + fscher_write_value(client, reg, v); + return count; +} + +static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0..1, 3..7 reserved => mask with 0x04 */ + return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04); +} + +static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + data->fan_min[FAN_INDEX_FROM_NUM(nr)] = simple_strtoul(buf, NULL, 10) & 0xff; + + fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]); + return count; +} + +static ssize_t show_pwm (struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]); +} + +static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* supported values: 2, 4, 8 */ + unsigned long v = simple_strtoul(buf, NULL, 10); + + switch (v) { + case 2: v = 1; break; + case 4: v = 2; break; + case 8: v = 3; break; + default: + dev_err(&client->dev, "fan_div value %ld not " + "supported. Choose one of 2, 4 or 8!\n", v); + return -1; + } + + /* bits 2..7 reserved => mask with 0x03 */ + data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03; + data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v; + + fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]); + return count; +} + +static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2..7 reserved => mask with 0x03 */ + return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03)); +} + +#define RPM_FROM_REG(val) (val*60) + +static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)])); +} + + + +#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1) + +static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 2..7 reserved, 0 read only => mask with 0x02 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; + data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v; + + fscher_write_value(client, reg, v); + return count; +} + +static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2..7 reserved => mask with 0x03 */ + return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03); +} + +#define TEMP_FROM_REG(val) (((val) - 128) * 1000) + +static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)])); +} + +/* + * The final conversion is specified in sensors.conf, as it depends on + * mainboard specific values. We export the registers contents as + * pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much + * sense per se, but it minimizes the conversions count and keeps the + * values within a usual range. + */ +#define VOLT_FROM_REG(val) ((val) * 10) + +static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr])); +} + + + +static ssize_t show_revision(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->revision); +} + + + +static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2, 5..6 reserved => mask with 0x9b */ + return sprintf(buf, "%u\n", data->global_event & 0x9b); +} + + + +static ssize_t set_control(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 1..7 reserved => mask with 0x01 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01; + data->global_control &= ~v; + + fscher_write_value(client, reg, v); + return count; +} + +static ssize_t show_control(struct fscher_data *data, char *buf, int nr) +{ + /* bits 1..7 reserved => mask with 0x01 */ + return sprintf(buf, "%u\n", data->global_control & 0x01); +} + + + +static ssize_t set_watchdog_control(struct i2c_client *client, struct + fscher_data *data, const char *buf, size_t count, + int nr, int reg) +{ + /* bits 0..3 reserved => mask with 0xf0 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; + data->watchdog[2] &= ~0xf0; + data->watchdog[2] |= v; + + fscher_write_value(client, reg, data->watchdog[2]); + return count; +} + +static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */ + return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0); +} + +static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 0, 2..7 reserved => mask with 0x02 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; + data->watchdog[1] &= ~v; + + fscher_write_value(client, reg, v); + return count; +} + +static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0, 2..7 reserved => mask with 0x02 */ + return sprintf(buf, "%u\n", data->watchdog[1] & 0x02); +} + +static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + data->watchdog[0] = simple_strtoul(buf, NULL, 10) & 0xff; + + fscher_write_value(client, reg, data->watchdog[0]); + return count; +} + +static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->watchdog[0]); +} + +static int __init sensors_fscher_init(void) +{ + return i2c_add_driver(&fscher_driver); +} + +static void __exit sensors_fscher_exit(void) +{ + i2c_del_driver(&fscher_driver); +} + +MODULE_AUTHOR("Reinhard Nissl "); +MODULE_DESCRIPTION("FSC Hermes driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_fscher_init); +module_exit(sensors_fscher_exit); diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index d4dcd2126ce1..e988edbcb5b8 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -156,6 +156,7 @@ #define I2C_DRIVERID_LM83 1040 #define I2C_DRIVERID_LM90 1042 #define I2C_DRIVERID_ASB100 1043 +#define I2C_DRIVERID_FSCHER 1046 #define I2C_DRIVERID_W83L785TS 1047 /* -- cgit v1.2.3 From d9efde4dda528c08db2e64f1624ca13d91910fdd Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 9 Feb 2004 00:25:04 -0800 Subject: Add forward-declaration of "struct nfs4_client" to make nfs_idmap.h independent of CONFIG_NFS4 and other header files. --- include/linux/nfs_idmap.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 50df56b5a01e..a0f1f25e0ead 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -60,6 +60,10 @@ struct idmap_msg { }; #ifdef __KERNEL__ + +/* Forward declaration to make this header independent of others */ +struct nfs4_client; + void nfs_idmap_new(struct nfs4_client *); void nfs_idmap_delete(struct nfs4_client *); -- cgit v1.2.3 From fa36f31ad068e0498c61a1454598be8d8333ee05 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 9 Feb 2004 00:26:00 -0800 Subject: Clean up dentry pointer validation by moving it into a function of its own. This also allows us to do a better job, since slab.c can now do more proper tests. --- fs/dcache.c | 13 ++----------- include/linux/slab.h | 1 + mm/slab.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/dcache.c b/fs/dcache.c index 03825e26dc49..95941d73cd20 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1035,20 +1035,11 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name) int d_validate(struct dentry *dentry, struct dentry *dparent) { - unsigned long dent_addr = (unsigned long) dentry; - unsigned long min_addr = PAGE_OFFSET; - unsigned long align_mask = 0x0F; struct hlist_head *base; struct hlist_node *lhp; - if (dent_addr < min_addr) - goto out; - if (dent_addr > (unsigned long)high_memory - sizeof(struct dentry)) - goto out; - if (dent_addr & align_mask) - goto out; - if ((!kern_addr_valid(dent_addr)) || (!kern_addr_valid(dent_addr -1 + - sizeof(struct dentry)))) + /* Check whether the ptr might be valid at all.. */ + if (!kmem_ptr_validate(dentry_cache, dentry)) goto out; if (dentry->d_parent != dparent) diff --git a/include/linux/slab.h b/include/linux/slab.h index d797c981f37e..69be5b308a11 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -101,6 +101,7 @@ extern void kfree(const void *); extern unsigned int ksize(const void *); extern int FASTCALL(kmem_cache_reap(int)); +extern int FASTCALL(kmem_ptr_validate(kmem_cache_t *cachep, void *ptr)); /* System wide caches */ extern kmem_cache_t *vm_area_cachep; diff --git a/mm/slab.c b/mm/slab.c index 155399a02481..744b70e4e563 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2073,6 +2073,48 @@ void * kmem_cache_alloc (kmem_cache_t *cachep, int flags) EXPORT_SYMBOL(kmem_cache_alloc); +/** + * kmem_ptr_validate - check if an untrusted pointer might + * be a slab entry. + * @cachep: the cache we're checking against + * @ptr: pointer to validate + * + * This verifies that the untrusted pointer looks sane: + * it is _not_ a guarantee that the pointer is actually + * part of the slab cache in question, but it at least + * validates that the pointer can be dereferenced and + * looks half-way sane. + * + * Currently only used for dentry validation. + */ +int kmem_ptr_validate(kmem_cache_t *cachep, void *ptr) +{ + unsigned long addr = (unsigned long) ptr; + unsigned long min_addr = PAGE_OFFSET; + unsigned long align_mask = BYTES_PER_WORD-1; + unsigned long size = cachep->objsize; + struct page *page; + + if (unlikely(addr < min_addr)) + goto out; + if (unlikely(addr > (unsigned long)high_memory - size)) + goto out; + if (unlikely(addr & align_mask)) + goto out; + if (unlikely(!kern_addr_valid(addr))) + goto out; + if (unlikely(!kern_addr_valid(addr + size - 1))) + goto out; + page = virt_to_page(ptr); + if (unlikely(!PageSlab(page))) + goto out; + if (unlikely(GET_PAGE_CACHE(page) != cachep)) + goto out; + return 1; +out: + return 0; +} + /** * kmalloc - allocate memory * @size: how many bytes of memory are required. -- cgit v1.2.3 From 56c6dd277c9d6a63f6f8dd61fac57b2906cb92c3 Mon Sep 17 00:00:00 2001 From: Jean Tourrilhes Date: Mon, 9 Feb 2004 02:35:34 -0800 Subject: [IRDA]: Ultra sendto support. o [CORRECT] Always initialise Ultra packet/header size. o [CORRECT] Don't allow Ultra send on unbound sockets if no dest address is given. o [FEATURE] Properly support Ultra sendto on unbound sockets. --- include/net/irda/irlmp.h | 2 +- net/irda/af_irda.c | 39 +++++++++++++++++++++++++++++++++++---- net/irda/irlmp.c | 8 ++++++-- 3 files changed, 42 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/irda/irlmp.h b/include/net/irda/irlmp.h index 32bf8ea47d14..d92b02561993 100644 --- a/include/net/irda/irlmp.h +++ b/include/net/irda/irlmp.h @@ -237,7 +237,7 @@ int irlmp_udata_request(struct lsap_cb *, struct sk_buff *); void irlmp_udata_indication(struct lsap_cb *, struct sk_buff *); #ifdef CONFIG_IRDA_ULTRA -int irlmp_connless_data_request(struct lsap_cb *, struct sk_buff *); +int irlmp_connless_data_request(struct lsap_cb *, struct sk_buff *, __u8); void irlmp_connless_data_indication(struct lsap_cb *, struct sk_buff *); #endif /* CONFIG_IRDA_ULTRA */ diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 3b4f9c49062f..8755709686a1 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -802,9 +802,6 @@ static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (err < 0) return err; - self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER; - self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER; - /* Pretend we are connected */ sock->state = SS_CONNECTED; sk->sk_state = TCP_ESTABLISHED; @@ -1122,6 +1119,10 @@ static int irda_create(struct socket *sock, int protocol) #ifdef CONFIG_IRDA_ULTRA case IRDAPROTO_ULTRA: sock->ops = &irda_ultra_ops; + /* Initialise now, because we may send on unbound + * sockets. Jean II */ + self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER; + self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER; break; #endif /* CONFIG_IRDA_ULTRA */ case IRDAPROTO_UNITDATA: @@ -1584,6 +1585,8 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, { struct sock *sk = sock->sk; struct irda_sock *self; + __u8 pid = 0; + int bound = 0; struct sk_buff *skb; unsigned char *asmptr; int err; @@ -1601,6 +1604,33 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, self = irda_sk(sk); ASSERT(self != NULL, return -1;); + /* Check if an address was specified with sendto. Jean II */ + if (msg->msg_name) { + struct sockaddr_irda *addr = (struct sockaddr_irda *) msg->msg_name; + /* Check address, extract pid. Jean II */ + if (msg->msg_namelen < sizeof(*addr)) + return -EINVAL; + if (addr->sir_family != AF_IRDA) + return -EINVAL; + + pid = addr->sir_lsap_sel; + if (pid & 0x80) { + IRDA_DEBUG(0, "%s(), extension in PID not supp!\n", __FUNCTION__); + return -EOPNOTSUPP; + } + } else { + /* Check that the socket is properly bound to an Ultra + * port. Jean II */ + if ((self->lsap == NULL) || + (sk->sk_state != TCP_ESTABLISHED)) { + IRDA_DEBUG(0, "%s(), socket not bound to Ultra PID.\n", + __FUNCTION__); + return -ENOTCONN; + } + /* Use PID from socket */ + bound = 1; + } + /* * Check that we don't send out to big frames. This is an unreliable * service, so we have no fragmentation and no coalescence @@ -1627,7 +1657,8 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, return err; } - err = irlmp_connless_data_request(self->lsap, skb); + err = irlmp_connless_data_request((bound ? self->lsap : NULL), + skb, pid); if (err) { IRDA_DEBUG(0, "%s(), err=%d\n", __FUNCTION__, err); return err; diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c index f702c9c30461..fb7397480564 100644 --- a/net/irda/irlmp.c +++ b/net/irda/irlmp.c @@ -1193,7 +1193,8 @@ void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb) * Function irlmp_connless_data_request (self, skb) */ #ifdef CONFIG_IRDA_ULTRA -int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata) +int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata, + __u8 pid) { struct sk_buff *clone_skb; struct lap_cb *lap; @@ -1208,7 +1209,10 @@ int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata) /* Insert protocol identifier */ skb_push(userdata, LMP_PID_HEADER); - userdata->data[0] = self->pid; + if(self != NULL) + userdata->data[0] = self->pid; + else + userdata->data[0] = pid; /* Connectionless sockets must use 0x70 */ skb_push(userdata, LMP_HEADER); -- cgit v1.2.3 From 52bd2e7544be9ec275fe3ab5e2c8dc9f5c2af272 Mon Sep 17 00:00:00 2001 From: Jean Tourrilhes Date: Mon, 9 Feb 2004 02:39:49 -0800 Subject: [IRDA]: IrLAP disconnection pending race. o [FEATURE] Don't drop IrLAP connection is we *just* received an incomming IrLMP connection request. --- include/net/irda/irlap.h | 6 ++++++ net/irda/irlmp_event.c | 8 ++++++++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/net/irda/irlap.h b/include/net/irda/irlap.h index 9385cffa836c..c7c2aecaf637 100644 --- a/include/net/irda/irlap.h +++ b/include/net/irda/irlap.h @@ -283,4 +283,10 @@ static inline int irlap_is_primary(struct irlap_cb *self) return(ret); } +/* Clear a pending IrLAP disconnect. - Jean II */ +static inline void irlap_clear_disconnect(struct irlap_cb *self) +{ + self->disconnect_pending = FALSE; +} + #endif diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c index 7ef8fd5ddf04..d6e551226e5b 100644 --- a/net/irda/irlmp_event.c +++ b/net/irda/irlmp_event.c @@ -390,6 +390,14 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, case LM_LAP_CONNECT_REQUEST: IRDA_DEBUG(4, "%s(), LS_CONNECT_REQUEST\n", __FUNCTION__); + /* + * IrLAP may have a pending disconnect. We tried to close + * IrLAP, but it was postponed because the link was + * busy or we were still sending packets. As we now + * need it, make sure it stays on. Jean II + */ + irlap_clear_disconnect(self->irlap); + /* * LAP connection already active, just bounce back! Since we * don't know which LSAP that tried to do this, we have to -- cgit v1.2.3 From af3ad15398b72407954bccca0d9952527e42da05 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 9 Feb 2004 16:11:44 -0800 Subject: [PATCH] Export OF device path for PCI devices Here's a new version of the patch according to our discussion. This allows the platform to add its own platform files to the sysfs PCI device node through a "pcibios_add_platform_entries()" call. I added an empty pcibios_add_platform_entries() inline to all archs that apparently have PCI and implemented the OF stuff for ppc and ppc64. The name is still "devspec" for now, I doubt it will conflict and that's consistent with our use of the "devspec" name in other places. --- arch/ppc/kernel/pci.c | 23 +++++++++++++++++++++++ arch/ppc64/kernel/pci.c | 22 ++++++++++++++++++++++ drivers/pci/pci-sysfs.c | 3 +++ include/asm-alpha/pci.h | 4 ++++ include/asm-arm/pci.h | 4 ++++ include/asm-h8300/pci.h | 4 ++++ include/asm-i386/pci.h | 5 +++++ include/asm-ia64/pci.h | 4 ++++ include/asm-m68k/pci.h | 4 ++++ include/asm-m68knommu/pci.h | 4 ++++ include/asm-mips/pci.h | 4 ++++ include/asm-parisc/pci.h | 4 ++++ include/asm-ppc/pci.h | 2 ++ include/asm-ppc64/pci.h | 2 ++ include/asm-sh/pci.h | 4 ++++ include/asm-sparc/pci.h | 4 ++++ include/asm-sparc64/pci.h | 4 ++++ include/asm-v850/pci.h | 4 ++++ include/asm-x86_64/pci.h | 4 ++++ 19 files changed, 109 insertions(+) (limited to 'include') diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index bb1a29201330..3c57648569d8 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1022,8 +1022,31 @@ pci_create_OF_bus_map(void) prom_add_property(find_path_device("/"), of_prop); } } + +static ssize_t pci_show_devspec(struct device *dev, char *buf) +{ + struct pci_dev *pdev; + struct device_node *np; + + pdev = to_pci_dev (dev); + np = pci_device_to_OF_node(pdev); + if (np == NULL || np->full_name == NULL) + return 0; + return sprintf(buf, "%s", np->full_name); +} +static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); + #endif /* CONFIG_PPC_OF */ +/* Add sysfs properties */ +void pcibios_add_platform_entries(struct pci_dev *dev) +{ +#ifdef CONFIG_PPC_OF + device_create_file(&pdev->dev, &dev_attr_devspec); +#endif /* CONFIG_PPC_OF */ +} + + #ifdef CONFIG_PPC_PMAC /* * This set of routines checks for PCI<->PCI bridges that have closed diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index 2dc7b69b8426..f37fc0aa3f2f 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -540,3 +540,25 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, return ret; } + +#ifdef CONFIG_PPC_PSERIES +static ssize_t pci_show_devspec(struct device *dev, char *buf) +{ + struct pci_dev *pdev; + struct device_node *np; + + pdev = to_pci_dev (dev); + np = pci_device_to_OF_node(pdev); + if (np == NULL || np->full_name == NULL) + return 0; + return sprintf(buf, "%s", np->full_name); +} +static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); +#endif /* CONFIG_PPC_PSERIES */ + +void pcibios_add_platform_entries(struct pci_dev *pdev) +{ +#ifdef CONFIG_PPC_PSERIES + device_create_file(&pdev->dev, &dev_attr_devspec); +#endif /* CONFIG_PPC_PSERIES */ +} diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index fd94569cd910..bffe34b22a6a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -180,4 +180,7 @@ void pci_create_sysfs_dev_files (struct pci_dev *pdev) device_create_file (dev, &dev_attr_irq); device_create_file (dev, &dev_attr_resource); sysfs_create_bin_file(&dev->kobj, &pci_config_attr); + + /* add platform-specific attributes */ + pcibios_add_platform_entries(pdev); } diff --git a/include/asm-alpha/pci.h b/include/asm-alpha/pci.h index faeee0303518..8eba57310ad2 100644 --- a/include/asm-alpha/pci.h +++ b/include/asm-alpha/pci.h @@ -208,6 +208,10 @@ static inline int pci_name_bus(char *name, struct pci_bus *bus) return 0; } +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __KERNEL__ */ /* Values for the `which' argument to sys_pciconfig_iobase. */ diff --git a/include/asm-arm/pci.h b/include/asm-arm/pci.h index 5280c8fa574d..6456092a5ab8 100644 --- a/include/asm-arm/pci.h +++ b/include/asm-arm/pci.h @@ -186,6 +186,10 @@ extern void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res); +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __KERNEL__ */ #endif diff --git a/include/asm-h8300/pci.h b/include/asm-h8300/pci.h index f4813f0a9021..d032729b19df 100644 --- a/include/asm-h8300/pci.h +++ b/include/asm-h8300/pci.h @@ -22,4 +22,8 @@ extern inline void pcibios_penalize_isa_irq(int irq) #define PCI_DMA_BUS_IS_PHYS (1) +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* _ASM_H8300_PCI_H */ diff --git a/include/asm-i386/pci.h b/include/asm-i386/pci.h index 83aede6ce595..909a2c152862 100644 --- a/include/asm-i386/pci.h +++ b/include/asm-i386/pci.h @@ -89,6 +89,11 @@ pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); + +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __KERNEL__ */ /* implement the pci_ DMA API in terms of the generic device dma_ one */ diff --git a/include/asm-ia64/pci.h b/include/asm-ia64/pci.h index 42f66dda432b..cbf5c0d9529c 100644 --- a/include/asm-ia64/pci.h +++ b/include/asm-ia64/pci.h @@ -112,6 +112,10 @@ static inline int pci_name_bus(char *name, struct pci_bus *bus) return 0; } +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + /* generic pci stuff */ #include diff --git a/include/asm-m68k/pci.h b/include/asm-m68k/pci.h index 57e1487bac3f..9e7d79ab5d13 100644 --- a/include/asm-m68k/pci.h +++ b/include/asm-m68k/pci.h @@ -54,4 +54,8 @@ static inline void pcibios_penalize_isa_irq(int irq) */ #define PCI_DMA_BUS_IS_PHYS (1) +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* _ASM_M68K_PCI_H */ diff --git a/include/asm-m68knommu/pci.h b/include/asm-m68knommu/pci.h index a99ce768f4a6..e04c77e1184d 100644 --- a/include/asm-m68knommu/pci.h +++ b/include/asm-m68knommu/pci.h @@ -30,6 +30,10 @@ static inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) */ #define pci_dac_dma_supported(pci_dev, mask) (0) +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* CONFIG_COMEMPCI */ #endif /* M68KNOMMU_PCI_H */ diff --git a/include/asm-mips/pci.h b/include/asm-mips/pci.h index 3943c111647e..75a2b5536bb0 100644 --- a/include/asm-mips/pci.h +++ b/include/asm-mips/pci.h @@ -120,6 +120,10 @@ static inline void pci_dac_dma_sync_single(struct pci_dev *pdev, dma_cache_wback_inv(addr, len); } +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __KERNEL__ */ /* implement the pci_ DMA API in terms of the generic device dma_ one */ diff --git a/include/asm-parisc/pci.h b/include/asm-parisc/pci.h index d5dca01282f0..6d803a9e8967 100644 --- a/include/asm-parisc/pci.h +++ b/include/asm-parisc/pci.h @@ -196,4 +196,8 @@ extern void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res); +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __ASM_PARISC_PCI_H */ diff --git a/include/asm-ppc/pci.h b/include/asm-ppc/pci.h index 16d617508242..0228e6e40e05 100644 --- a/include/asm-ppc/pci.h +++ b/include/asm-ppc/pci.h @@ -282,6 +282,8 @@ extern void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res); +extern void pcibios_add_platform_entries(struct pci_dev *dev); + #endif /* __KERNEL__ */ #endif /* __PPC_PCI_H */ diff --git a/include/asm-ppc64/pci.h b/include/asm-ppc64/pci.h index 60d3832dd934..835b882fb18d 100644 --- a/include/asm-ppc64/pci.h +++ b/include/asm-ppc64/pci.h @@ -152,6 +152,8 @@ pcibios_fixup_device_resources(struct pci_dev *dev, struct pci_bus *bus); extern int pci_read_irq_line(struct pci_dev *dev); +extern void pcibios_add_platform_entries(struct pci_dev *dev); + #endif /* __KERNEL__ */ #endif /* __PPC64_PCI_H */ diff --git a/include/asm-sh/pci.h b/include/asm-sh/pci.h index fe2c08b2e7cd..b16a62e61771 100644 --- a/include/asm-sh/pci.h +++ b/include/asm-sh/pci.h @@ -256,6 +256,10 @@ extern void pcibios_fixup_irqs(void); extern int pciauto_assign_resources(int busno, struct pci_channel *hose); #endif +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __KERNEL__ */ /* generic pci stuff */ diff --git a/include/asm-sparc/pci.h b/include/asm-sparc/pci.h index e97afc5a44db..e78d2cbf0426 100644 --- a/include/asm-sparc/pci.h +++ b/include/asm-sparc/pci.h @@ -141,6 +141,10 @@ extern inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) #define pci_dac_dma_supported(dev, mask) (0) +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __KERNEL__ */ /* generic pci stuff */ diff --git a/include/asm-sparc64/pci.h b/include/asm-sparc64/pci.h index ea722dcb49d8..e64c30c45b10 100644 --- a/include/asm-sparc64/pci.h +++ b/include/asm-sparc64/pci.h @@ -215,6 +215,10 @@ extern void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, struct pci_bus_region *region); +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __KERNEL__ */ #endif /* __SPARC64_PCI_H */ diff --git a/include/asm-v850/pci.h b/include/asm-v850/pci.h index 81addb7df017..e3a33920e693 100644 --- a/include/asm-v850/pci.h +++ b/include/asm-v850/pci.h @@ -76,4 +76,8 @@ extern void pci_free_consistent (struct pci_dev *pdev, size_t size, void *cpu_addr, dma_addr_t dma_addr); +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __V850_PCI_H__ */ diff --git a/include/asm-x86_64/pci.h b/include/asm-x86_64/pci.h index 6feb36107d64..1ba95e06f32f 100644 --- a/include/asm-x86_64/pci.h +++ b/include/asm-x86_64/pci.h @@ -263,6 +263,10 @@ pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); +static inline void pcibios_add_platform_entries(struct pci_dev *dev) +{ +} + #endif /* __KERNEL__ */ /* generic pci stuff */ -- cgit v1.2.3 From 11c975e549c81716b8329f51f26228381d06eda4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 9 Feb 2004 17:00:29 -0800 Subject: [PATCH] fix build for CONFIG_BLK_DEV_IDEDMA=n Ths "fix duplication of DMA {black,white}list in icside.c" patch broke it. Noticed by Geert Uytterhoeven . --- include/linux/ide.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ide.h b/include/linux/ide.h index d247474de882..1533e9ab256e 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1626,6 +1626,7 @@ extern int __ide_dma_count(ide_drive_t *); extern int __ide_dma_verbose(ide_drive_t *); extern int __ide_dma_lostirq(ide_drive_t *); extern int __ide_dma_timeout(ide_drive_t *); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ #ifdef CONFIG_BLK_DEV_IDE_TCQ extern int __ide_dma_queued_on(ide_drive_t *drive); @@ -1634,13 +1635,12 @@ extern ide_startstop_t __ide_dma_queued_read(ide_drive_t *drive); extern ide_startstop_t __ide_dma_queued_write(ide_drive_t *drive); extern ide_startstop_t __ide_dma_queued_start(ide_drive_t *drive); #endif +#endif /* CONFIG_BLK_DEV_IDEDMA */ -#else +#ifndef CONFIG_BLK_DEV_IDEDMA_PCI static inline void ide_release_dma(ide_hwif_t *drive) {;} #endif -#endif /* CONFIG_BLK_DEV_IDEDMA */ - extern int ide_hwif_request_regions(ide_hwif_t *hwif); extern void ide_hwif_release_regions(ide_hwif_t* hwif); extern void ide_unregister (unsigned int index); -- cgit v1.2.3 From 122123c4b298a688d43666d0355ca253070c1ea8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 9 Feb 2004 17:10:33 -0800 Subject: Make a bit more palatable to user program inclusion. It's still wrong to include kernel headers from user programs. Oh, well. --- include/linux/compiler.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/compiler.h b/include/linux/compiler.h index ba3ef774bc17..cc46aa0584f9 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -9,6 +9,8 @@ # define __kernel #endif +#ifdef __KERNEL__ + #ifndef __ASSEMBLY__ #if __GNUC__ > 3 # include /* catch-all for GCC 4, 5, etc. */ @@ -106,4 +108,6 @@ (typeof(ptr)) (__ptr + (off)); }) #endif +#endif /* __KERNEL__ */ + #endif /* __LINUX_COMPILER_H */ -- cgit v1.2.3 From 33d55a8ad0fb53f8222c4f895cf48652b562f40a Mon Sep 17 00:00:00 2001 From: Jack Steiner Date: Mon, 9 Feb 2004 20:16:10 -0800 Subject: [PATCH] ia64: Enable cpu_vm_mask maintenance and improve SN2 TLB flushing --- arch/ia64/sn/kernel/sn2/sn2_smp.c | 60 +++++++++++++++++++++++++++++++++------ include/asm-ia64/mmu_context.h | 3 ++ 2 files changed, 55 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/ia64/sn/kernel/sn2/sn2_smp.c b/arch/ia64/sn/kernel/sn2/sn2_smp.c index 14b3f21fc9be..3cfb3cd74d51 100644 --- a/arch/ia64/sn/kernel/sn2/sn2_smp.c +++ b/arch/ia64/sn/kernel/sn2/sn2_smp.c @@ -5,7 +5,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved. */ #include @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -67,14 +69,56 @@ wait_piowc(void) * * Purges the translation caches of all processors of the given virtual address * range. + * + * Note: + * - cpu_vm_mask is a bit mask that indicates which cpus have loaded the context. + * - cpu_vm_mask is converted into a nodemask of the nodes containing the + * cpus in cpu_vm_mask. + * - if only one bit is set in cpu_vm_mask & it is the current cpu, + * then only the local TLB needs to be flushed. This flushing can be done + * using ptc.l. This is the common case & avoids the global spinlock. + * - if multiple cpus have loaded the context, then flushing has to be + * done with ptc.g/MMRs under protection of the global ptc_lock. */ void sn2_global_tlb_purge (unsigned long start, unsigned long end, unsigned long nbits) { - int cnode, mycnode, nasid, flushed=0; + int i, cnode, mynasid, cpu, lcpu=0, nasid, flushed=0; volatile unsigned long *ptc0, *ptc1; unsigned long flags=0, data0, data1; + struct mm_struct *mm=current->active_mm; + short nasids[NR_NODES], nix; + DECLARE_BITMAP(nodes_flushed, NR_NODES); + + CLEAR_BITMAP(nodes_flushed, NR_NODES); + + i = 0; + + for_each_cpu_mask(cpu, mm->cpu_vm_mask) { + cnode = cpu_to_node(cpu); + __set_bit(cnode, nodes_flushed); + lcpu = cpu; + i++; + } + + preempt_disable(); + + if (likely(i == 1 && lcpu == smp_processor_id())) { + do { + ia64_ptcl(start, nbits<<2); + start += (1UL << nbits); + } while (start < end); + ia64_srlz_i(); + preempt_enable(); + return; + } + + nix = 0; + for (cnode=find_first_bit(&nodes_flushed, NR_NODES); cnode < NR_NODES; + cnode=find_next_bit(&nodes_flushed, NR_NODES, ++cnode)) + nasids[nix++] = cnodeid_to_nasid(cnode); + data0 = (1UL<context; if (context == 0) { + cpus_clear(mm->cpu_vm_mask); if (ia64_ctx.next >= ia64_ctx.limit) wrap_mmu_context(mm); mm->context = context = ia64_ctx.next++; @@ -170,6 +171,8 @@ activate_context (struct mm_struct *mm) do { context = get_mmu_context(mm); MMU_TRACE('A', smp_processor_id(), mm, context); + if (!cpu_isset(smp_processor_id(), mm->cpu_vm_mask)) + cpu_set(smp_processor_id(), mm->cpu_vm_mask); reload_context(context); MMU_TRACE('a', smp_processor_id(), mm, context); /* in the unlikely event of a TLB-flush by another thread, redo the load: */ -- cgit v1.2.3 From 926c765e39a443f4f07f13d3e21ec51e891aaae4 Mon Sep 17 00:00:00 2001 From: Keith Owens Date: Mon, 9 Feb 2004 22:11:58 -0800 Subject: [PATCH] ia64: mca.c cleanup - Delete all record printing code, moved to salinfo_decode in user space --- arch/ia64/kernel/mca.c | 1065 +-------------------------------------------- arch/ia64/sn/kernel/mca.c | 14 - include/asm-ia64/mca.h | 12 - 3 files changed, 2 insertions(+), 1089 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 7db28d2c9a13..fca5f43fb376 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -43,6 +43,7 @@ * * 2004-02-01 Keith Owens * Avoid deadlock when using printk() for MCA and INIT records. + * Delete all record printing code, moved to salinfo_decode in user space. */ #include #include @@ -438,6 +439,7 @@ ia64_mca_check_errors (void) /* * If there is an MCA error record pending, get it and log it. */ + printk(KERN_INFO "CPU %d: checking for saved MCA error records\n", smp_processor_id()); ia64_mca_log_sal_error_record(SAL_INFO_TYPE_MCA, 1); return 0; @@ -561,60 +563,6 @@ ia64_mca_cmc_vector_enable (void *dummy) smp_processor_id(), cmcv.cmcv_vector); } - -#if defined(MCA_TEST) - -sal_log_processor_info_t slpi_buf; - -void -mca_test(void) -{ - slpi_buf.valid.psi_static_struct = 1; - slpi_buf.valid.num_cache_check = 1; - slpi_buf.valid.num_tlb_check = 1; - slpi_buf.valid.num_bus_check = 1; - slpi_buf.valid.processor_static_info.minstate = 1; - slpi_buf.valid.processor_static_info.br = 1; - slpi_buf.valid.processor_static_info.cr = 1; - slpi_buf.valid.processor_static_info.ar = 1; - slpi_buf.valid.processor_static_info.rr = 1; - slpi_buf.valid.processor_static_info.fr = 1; - - ia64_os_mca_dispatch(); -} - -#endif /* #if defined(MCA_TEST) */ - - -/* - * verify_guid - * - * Compares a test guid to a target guid and returns result. - * - * Inputs - * test_guid * (ptr to guid to be verified) - * target_guid * (ptr to standard guid to be verified against) - * - * Outputs - * 0 (test verifies against target) - * non-zero (test guid does not verify) - */ -static int -verify_guid (efi_guid_t *test, efi_guid_t *target) -{ - int rc; -#ifdef IA64_MCA_DEBUG_INFO - char out[40]; -#endif - - if ((rc = efi_guidcmp(*test, *target))) { - IA64_MCA_DEBUG(KERN_DEBUG - "verify_guid: invalid GUID = %s\n", - efi_guid_unparse(test, out)); - } - return rc; -} - /* * ia64_mca_cmc_vector_disable_keventd * @@ -1344,68 +1292,6 @@ ia64_init_handler (struct pt_regs *pt, struct switch_stack *sw) init_handler_platform(ms, pt, sw); /* call platform specific routines */ } -/* - * ia64_log_prt_guid - * - * Print a formatted GUID. - * - * Inputs : p_guid (ptr to the GUID) - * prfunc (print function) - * Outputs : None - * - */ -void -ia64_log_prt_guid (efi_guid_t *p_guid, prfunc_t prfunc) -{ - char out[40]; - printk(KERN_DEBUG "GUID = %s\n", efi_guid_unparse(p_guid, out)); -} - -static void -ia64_log_hexdump(unsigned char *p, unsigned long n_ch, prfunc_t prfunc) -{ - unsigned long i; - int j; - - if (!p) - return; - - for (i = 0; i < n_ch;) { - prfunc("%p ", (void *)p); - for (j = 0; (j < 16) && (i < n_ch); i++, j++, p++) { - prfunc("%02x ", *p); - } - prfunc("\n"); - } -} - -#ifdef MCA_PRT_XTRA_DATA // for test only @FVL - -static void -ia64_log_prt_record_header (sal_log_record_header_t *rh, prfunc_t prfunc) -{ - prfunc("SAL RECORD HEADER: Record buffer = %p, header size = %ld\n", - (void *)rh, sizeof(sal_log_record_header_t)); - ia64_log_hexdump((unsigned char *)rh, sizeof(sal_log_record_header_t), - (prfunc_t)prfunc); - prfunc("Total record length = %d\n", rh->len); - ia64_log_prt_guid(&rh->platform_guid, prfunc); - prfunc("End of SAL RECORD HEADER\n"); -} - -static void -ia64_log_prt_section_header (sal_log_section_hdr_t *sh, prfunc_t prfunc) -{ - prfunc("SAL SECTION HEADER: Record buffer = %p, header size = %ld\n", - (void *)sh, sizeof(sal_log_section_hdr_t)); - ia64_log_hexdump((unsigned char *)sh, sizeof(sal_log_section_hdr_t), - (prfunc_t)prfunc); - prfunc("Length of section & header = %d\n", sh->len); - ia64_log_prt_guid(&sh->guid, prfunc); - prfunc("End of SAL SECTION HEADER\n"); -} -#endif // MCA_PRT_XTRA_DATA for test only @FVL - /* * ia64_log_init * Reset the OS ia64 log buffer @@ -1472,953 +1358,6 @@ ia64_log_get(int sal_info_type, u8 **buffer) } } -/* - * ia64_log_prt_oem_data - * - * Print OEM specific data if included. - * - * Inputs : header_len (length passed in section header) - * sect_len (default length of section type) - * p_data (ptr to data) - * prfunc (print function) - * Outputs : None - * - */ -void -ia64_log_prt_oem_data (int header_len, int sect_len, u8 *p_data, prfunc_t prfunc) -{ - int oem_data_len, i; - - if ((oem_data_len = header_len - sect_len) > 0) { - prfunc(" OEM Specific Data:"); - for (i = 0; i < oem_data_len; i++, p_data++) - prfunc(" %02x", *p_data); - } - prfunc("\n"); -} - -/* - * ia64_log_rec_header_print - * - * Log info from the SAL error record header. - * - * Inputs : lh * (ptr to SAL log error record header) - * prfunc (fn ptr of log output function to use) - * Outputs : None - */ -void -ia64_log_rec_header_print (sal_log_record_header_t *lh, prfunc_t prfunc) -{ - prfunc("+Err Record ID: %ld SAL Rev: %2x.%02x\n", lh->id, - lh->revision.major, lh->revision.minor); - prfunc("+Time: %02x/%02x/%02x%02x %02x:%02x:%02x Severity %d\n", - lh->timestamp.slh_month, lh->timestamp.slh_day, - lh->timestamp.slh_century, lh->timestamp.slh_year, - lh->timestamp.slh_hour, lh->timestamp.slh_minute, - lh->timestamp.slh_second, lh->severity); -} - -/* - * ia64_log_processor_regs_print - * Print the contents of the saved processor register(s) in the format - * [] - * - * Inputs : regs (Register save buffer) - * reg_num (# of registers) - * reg_class (application/banked/control/bank1_general) - * reg_prefix (ar/br/cr/b1_gr) - * Outputs : None - * - */ -void -ia64_log_processor_regs_print(u64 *regs, - int reg_num, - char *reg_class, - char *reg_prefix, - prfunc_t prfunc) -{ - int i; - - prfunc("+%s Registers\n", reg_class); - for (i = 0; i < reg_num; i++) - prfunc("+ %s[%d] 0x%lx\n", reg_prefix, i, regs[i]); -} - -/* - * ia64_log_processor_fp_regs_print - * Print the contents of the saved floating page register(s) in the format - * [] - * - * Inputs: ia64_fpreg (Register save buffer) - * reg_num (# of registers) - * reg_class (application/banked/control/bank1_general) - * reg_prefix (ar/br/cr/b1_gr) - * Outputs: None - * - */ -void -ia64_log_processor_fp_regs_print (struct ia64_fpreg *regs, - int reg_num, - char *reg_class, - char *reg_prefix, - prfunc_t prfunc) -{ - int i; - - prfunc("+%s Registers\n", reg_class); - for (i = 0; i < reg_num; i++) - prfunc("+ %s[%d] 0x%lx%016lx\n", reg_prefix, i, regs[i].u.bits[1], - regs[i].u.bits[0]); -} - -static char *pal_mesi_state[] = { - "Invalid", - "Shared", - "Exclusive", - "Modified", - "Reserved1", - "Reserved2", - "Reserved3", - "Reserved4" -}; - -static char *pal_cache_op[] = { - "Unknown", - "Move in", - "Cast out", - "Coherency check", - "Internal", - "Instruction fetch", - "Implicit Writeback", - "Reserved" -}; - -/* - * ia64_log_cache_check_info_print - * Display the machine check information related to cache error(s). - * Inputs: i (Multiple errors are logged, i - index of logged error) - * cc_info * (Ptr to cache check info logged by the PAL and later - * captured by the SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_cache_check_info_print (int i, - sal_log_mod_error_info_t *cache_check_info, - prfunc_t prfunc) -{ - pal_cache_check_info_t *info; - u64 target_addr; - - if (!cache_check_info->valid.check_info) { - IA64_MCA_DEBUG("ia64_mca_log_print: invalid cache_check_info[%d]\n",i); - return; /* If check info data not valid, skip it */ - } - - info = (pal_cache_check_info_t *)&cache_check_info->check_info; - target_addr = cache_check_info->target_identifier; - - prfunc("+ Cache check info[%d]\n+", i); - prfunc(" Level: L%d,",info->level); - if (info->mv) - prfunc(" Mesi: %s,",pal_mesi_state[info->mesi]); - prfunc(" Index: %d,", info->index); - if (info->ic) - prfunc(" Cache: Instruction,"); - if (info->dc) - prfunc(" Cache: Data,"); - if (info->tl) - prfunc(" Line: Tag,"); - if (info->dl) - prfunc(" Line: Data,"); - prfunc(" Operation: %s,", pal_cache_op[info->op]); - if (info->wiv) - prfunc(" Way: %d,", info->way); - if (cache_check_info->valid.target_identifier) - /* Hope target address is saved in target_identifier */ - if (info->tv) - prfunc(" Target Addr: 0x%lx,", target_addr); - if (info->mcc) - prfunc(" MC: Corrected"); - prfunc("\n"); -} - -/* - * ia64_log_tlb_check_info_print - * Display the machine check information related to tlb error(s). - * Inputs: i (Multiple errors are logged, i - index of logged error) - * tlb_info * (Ptr to machine check info logged by the PAL and later - * captured by the SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_tlb_check_info_print (int i, - sal_log_mod_error_info_t *tlb_check_info, - prfunc_t prfunc) - -{ - pal_tlb_check_info_t *info; - - if (!tlb_check_info->valid.check_info) { - IA64_MCA_DEBUG("ia64_mca_log_print: invalid tlb_check_info[%d]\n", i); - return; /* If check info data not valid, skip it */ - } - - info = (pal_tlb_check_info_t *)&tlb_check_info->check_info; - - prfunc("+ TLB Check Info [%d]\n+", i); - if (info->itc) - prfunc(" Failure: Instruction Translation Cache"); - if (info->dtc) - prfunc(" Failure: Data Translation Cache"); - if (info->itr) { - prfunc(" Failure: Instruction Translation Register"); - prfunc(" ,Slot: %ld", info->tr_slot); - } - if (info->dtr) { - prfunc(" Failure: Data Translation Register"); - prfunc(" ,Slot: %ld", info->tr_slot); - } - if (info->mcc) - prfunc(" ,MC: Corrected"); - prfunc("\n"); -} - -/* - * ia64_log_bus_check_info_print - * Display the machine check information related to bus error(s). - * Inputs: i (Multiple errors are logged, i - index of logged error) - * bus_info * (Ptr to machine check info logged by the PAL and later - * captured by the SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_bus_check_info_print (int i, - sal_log_mod_error_info_t *bus_check_info, - prfunc_t prfunc) -{ - pal_bus_check_info_t *info; - u64 req_addr; /* Address of the requestor of the transaction */ - u64 resp_addr; /* Address of the responder of the transaction */ - u64 targ_addr; /* Address where the data was to be delivered to */ - /* or obtained from */ - - if (!bus_check_info->valid.check_info) { - IA64_MCA_DEBUG("ia64_mca_log_print: invalid bus_check_info[%d]\n", i); - return; /* If check info data not valid, skip it */ - } - - info = (pal_bus_check_info_t *)&bus_check_info->check_info; - req_addr = bus_check_info->requestor_identifier; - resp_addr = bus_check_info->responder_identifier; - targ_addr = bus_check_info->target_identifier; - - prfunc("+ BUS Check Info [%d]\n+", i); - prfunc(" Status Info: %d", info->bsi); - prfunc(" ,Severity: %d", info->sev); - prfunc(" ,Transaction Type: %d", info->type); - prfunc(" ,Transaction Size: %d", info->size); - if (info->cc) - prfunc(" ,Cache-cache-transfer"); - if (info->ib) - prfunc(" ,Error: Internal"); - if (info->eb) - prfunc(" ,Error: External"); - if (info->mcc) - prfunc(" ,MC: Corrected"); - if (info->tv) - prfunc(" ,Target Address: 0x%lx", targ_addr); - if (info->rq) - prfunc(" ,Requestor Address: 0x%lx", req_addr); - if (info->tv) - prfunc(" ,Responder Address: 0x%lx", resp_addr); - prfunc("\n"); -} - -/* - * ia64_log_mem_dev_err_info_print - * - * Format and log the platform memory device error record section data. - * - * Inputs: mem_dev_err_info * (Ptr to memory device error record section - * returned by SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_mem_dev_err_info_print (sal_log_mem_dev_err_info_t *mdei, - prfunc_t prfunc) -{ - prfunc("+ Mem Error Detail: "); - - if (mdei->valid.error_status) - prfunc(" Error Status: %#lx,", mdei->error_status); - if (mdei->valid.physical_addr) - prfunc(" Physical Address: %#lx,", mdei->physical_addr); - if (mdei->valid.addr_mask) - prfunc(" Address Mask: %#lx,", mdei->addr_mask); - if (mdei->valid.node) - prfunc(" Node: %d,", mdei->node); - if (mdei->valid.card) - prfunc(" Card: %d,", mdei->card); - if (mdei->valid.module) - prfunc(" Module: %d,", mdei->module); - if (mdei->valid.bank) - prfunc(" Bank: %d,", mdei->bank); - if (mdei->valid.device) - prfunc(" Device: %d,", mdei->device); - if (mdei->valid.row) - prfunc(" Row: %d,", mdei->row); - if (mdei->valid.column) - prfunc(" Column: %d,", mdei->column); - if (mdei->valid.bit_position) - prfunc(" Bit Position: %d,", mdei->bit_position); - if (mdei->valid.target_id) - prfunc(" ,Target Address: %#lx,", mdei->target_id); - if (mdei->valid.requestor_id) - prfunc(" ,Requestor Address: %#lx,", mdei->requestor_id); - if (mdei->valid.responder_id) - prfunc(" ,Responder Address: %#lx,", mdei->responder_id); - if (mdei->valid.bus_spec_data) - prfunc(" Bus Specific Data: %#lx,", mdei->bus_spec_data); - prfunc("\n"); - - if (mdei->valid.oem_id) { - u8 *p_data = &(mdei->oem_id[0]); - int i; - - prfunc(" OEM Memory Controller ID:"); - for (i = 0; i < 16; i++, p_data++) - prfunc(" %02x", *p_data); - prfunc("\n"); - } - - if (mdei->valid.oem_data) { - platform_mem_dev_err_print((int)mdei->header.len, - (int)sizeof(sal_log_mem_dev_err_info_t) - 1, - &(mdei->oem_data[0]), prfunc); - } -} - -/* - * ia64_log_sel_dev_err_info_print - * - * Format and log the platform SEL device error record section data. - * - * Inputs: sel_dev_err_info * (Ptr to the SEL device error record section - * returned by SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_sel_dev_err_info_print (sal_log_sel_dev_err_info_t *sdei, - prfunc_t prfunc) -{ - int i; - - prfunc("+ SEL Device Error Detail: "); - - if (sdei->valid.record_id) - prfunc(" Record ID: %#x", sdei->record_id); - if (sdei->valid.record_type) - prfunc(" Record Type: %#x", sdei->record_type); - prfunc(" Time Stamp: "); - for (i = 0; i < 4; i++) - prfunc("%1d", sdei->timestamp[i]); - if (sdei->valid.generator_id) - prfunc(" Generator ID: %#x", sdei->generator_id); - if (sdei->valid.evm_rev) - prfunc(" Message Format Version: %#x", sdei->evm_rev); - if (sdei->valid.sensor_type) - prfunc(" Sensor Type: %#x", sdei->sensor_type); - if (sdei->valid.sensor_num) - prfunc(" Sensor Number: %#x", sdei->sensor_num); - if (sdei->valid.event_dir) - prfunc(" Event Direction Type: %#x", sdei->event_dir); - if (sdei->valid.event_data1) - prfunc(" Data1: %#x", sdei->event_data1); - if (sdei->valid.event_data2) - prfunc(" Data2: %#x", sdei->event_data2); - if (sdei->valid.event_data3) - prfunc(" Data3: %#x", sdei->event_data3); - prfunc("\n"); - -} - -/* - * ia64_log_pci_bus_err_info_print - * - * Format and log the platform PCI bus error record section data. - * - * Inputs: pci_bus_err_info * (Ptr to the PCI bus error record section - * returned by SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_pci_bus_err_info_print (sal_log_pci_bus_err_info_t *pbei, - prfunc_t prfunc) -{ - prfunc("+ PCI Bus Error Detail: "); - - if (pbei->valid.err_status) - prfunc(" Error Status: %#lx", pbei->err_status); - if (pbei->valid.err_type) - prfunc(" Error Type: %#x", pbei->err_type); - if (pbei->valid.bus_id) - prfunc(" Bus ID: %#x", pbei->bus_id); - if (pbei->valid.bus_address) - prfunc(" Bus Address: %#lx", pbei->bus_address); - if (pbei->valid.bus_data) - prfunc(" Bus Data: %#lx", pbei->bus_data); - if (pbei->valid.bus_cmd) - prfunc(" Bus Command: %#lx", pbei->bus_cmd); - if (pbei->valid.requestor_id) - prfunc(" Requestor ID: %#lx", pbei->requestor_id); - if (pbei->valid.responder_id) - prfunc(" Responder ID: %#lx", pbei->responder_id); - if (pbei->valid.target_id) - prfunc(" Target ID: %#lx", pbei->target_id); - if (pbei->valid.oem_data) - prfunc("\n"); - - if (pbei->valid.oem_data) { - platform_pci_bus_err_print((int)pbei->header.len, - (int)sizeof(sal_log_pci_bus_err_info_t) - 1, - &(pbei->oem_data[0]), prfunc); - } -} - -/* - * ia64_log_smbios_dev_err_info_print - * - * Format and log the platform SMBIOS device error record section data. - * - * Inputs: smbios_dev_err_info * (Ptr to the SMBIOS device error record - * section returned by SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_smbios_dev_err_info_print (sal_log_smbios_dev_err_info_t *sdei, - prfunc_t prfunc) -{ - u8 i; - - prfunc("+ SMBIOS Device Error Detail: "); - - if (sdei->valid.event_type) - prfunc(" Event Type: %#x", sdei->event_type); - if (sdei->valid.time_stamp) { - prfunc(" Time Stamp: "); - for (i = 0; i < 6; i++) - prfunc("%d", sdei->time_stamp[i]); - } - if ((sdei->valid.data) && (sdei->valid.length)) { - prfunc(" Data: "); - for (i = 0; i < sdei->length; i++) - prfunc(" %02x", sdei->data[i]); - } - prfunc("\n"); -} - -/* - * ia64_log_pci_comp_err_info_print - * - * Format and log the platform PCI component error record section data. - * - * Inputs: pci_comp_err_info * (Ptr to the PCI component error record section - * returned by SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_pci_comp_err_info_print(sal_log_pci_comp_err_info_t *pcei, - prfunc_t prfunc) -{ - u32 n_mem_regs, n_io_regs; - u64 i, n_pci_data; - u64 *p_reg_data; - u8 *p_oem_data; - - prfunc("+ PCI Component Error Detail: "); - - if (pcei->valid.err_status) - prfunc(" Error Status: %#lx\n", pcei->err_status); - if (pcei->valid.comp_info) - prfunc(" Component Info: Vendor Id = %#x, Device Id = %#x," - " Class Code = %#x, Seg/Bus/Dev/Func = %d/%d/%d/%d\n", - pcei->comp_info.vendor_id, pcei->comp_info.device_id, - pcei->comp_info.class_code, pcei->comp_info.seg_num, - pcei->comp_info.bus_num, pcei->comp_info.dev_num, - pcei->comp_info.func_num); - - n_mem_regs = (pcei->valid.num_mem_regs) ? pcei->num_mem_regs : 0; - n_io_regs = (pcei->valid.num_io_regs) ? pcei->num_io_regs : 0; - p_reg_data = &(pcei->reg_data_pairs[0]); - p_oem_data = (u8 *)p_reg_data + - (n_mem_regs + n_io_regs) * 2 * sizeof(u64); - n_pci_data = p_oem_data - (u8 *)pcei; - - if (n_pci_data > pcei->header.len) { - prfunc(" Invalid PCI Component Error Record format: length = %ld, " - " Size PCI Data = %d, Num Mem-Map/IO-Map Regs = %ld/%ld\n", - pcei->header.len, n_pci_data, n_mem_regs, n_io_regs); - return; - } - - if (n_mem_regs) { - prfunc(" Memory Mapped Registers\n Address \tValue\n"); - for (i = 0; i < pcei->num_mem_regs; i++) { - prfunc(" %#lx %#lx\n", p_reg_data[0], p_reg_data[1]); - p_reg_data += 2; - } - } - if (n_io_regs) { - prfunc(" I/O Mapped Registers\n Address \tValue\n"); - for (i = 0; i < pcei->num_io_regs; i++) { - prfunc(" %#lx %#lx\n", p_reg_data[0], p_reg_data[1]); - p_reg_data += 2; - } - } - if (pcei->valid.oem_data) { - platform_pci_comp_err_print((int)pcei->header.len, n_pci_data, - p_oem_data, prfunc); - prfunc("\n"); - } -} - -/* - * ia64_log_plat_specific_err_info_print - * - * Format and log the platform specifie error record section data. - * - * Inputs: sel_dev_err_info * (Ptr to the platform specific error record - * section returned by SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_plat_specific_err_info_print (sal_log_plat_specific_err_info_t *psei, - prfunc_t prfunc) -{ - prfunc("+ Platform Specific Error Detail: "); - - if (psei->valid.err_status) - prfunc(" Error Status: %#lx", psei->err_status); - if (psei->valid.guid) { - prfunc(" GUID: "); - ia64_log_prt_guid(&psei->guid, prfunc); - } - if (psei->valid.oem_data) { - platform_plat_specific_err_print((int) psei->header.len, - (char *) psei->oem_data - (char *) psei, - &psei->oem_data[0], prfunc); - } - prfunc("\n"); -} - -/* - * ia64_log_host_ctlr_err_info_print - * - * Format and log the platform host controller error record section data. - * - * Inputs: host_ctlr_err_info * (Ptr to the host controller error record - * section returned by SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_host_ctlr_err_info_print (sal_log_host_ctlr_err_info_t *hcei, - prfunc_t prfunc) -{ - prfunc("+ Host Controller Error Detail: "); - - if (hcei->valid.err_status) - prfunc(" Error Status: %#lx", hcei->err_status); - if (hcei->valid.requestor_id) - prfunc(" Requestor ID: %#lx", hcei->requestor_id); - if (hcei->valid.responder_id) - prfunc(" Responder ID: %#lx", hcei->responder_id); - if (hcei->valid.target_id) - prfunc(" Target ID: %#lx", hcei->target_id); - if (hcei->valid.bus_spec_data) - prfunc(" Bus Specific Data: %#lx", hcei->bus_spec_data); - if (hcei->valid.oem_data) { - platform_host_ctlr_err_print((int)hcei->header.len, - (int)sizeof(sal_log_host_ctlr_err_info_t) - 1, - &(hcei->oem_data[0]), prfunc); - } - prfunc("\n"); -} - -/* - * ia64_log_plat_bus_err_info_print - * - * Format and log the platform bus error record section data. - * - * Inputs: plat_bus_err_info * (Ptr to the platform bus error record section - * returned by SAL) - * prfunc (fn ptr of print function to be used for output) - * Outputs: None - */ -void -ia64_log_plat_bus_err_info_print (sal_log_plat_bus_err_info_t *pbei, - prfunc_t prfunc) -{ - prfunc("+ Platform Bus Error Detail: "); - - if (pbei->valid.err_status) - prfunc(" Error Status: %#lx", pbei->err_status); - if (pbei->valid.requestor_id) - prfunc(" Requestor ID: %#lx", pbei->requestor_id); - if (pbei->valid.responder_id) - prfunc(" Responder ID: %#lx", pbei->responder_id); - if (pbei->valid.target_id) - prfunc(" Target ID: %#lx", pbei->target_id); - if (pbei->valid.bus_spec_data) - prfunc(" Bus Specific Data: %#lx", pbei->bus_spec_data); - if (pbei->valid.oem_data) { - platform_plat_bus_err_print((int)pbei->header.len, - (int)sizeof(sal_log_plat_bus_err_info_t) - 1, - &(pbei->oem_data[0]), prfunc); - } - prfunc("\n"); -} - -/* - * ia64_log_proc_dev_err_info_print - * - * Display the processor device error record. - * - * Inputs: sal_log_processor_info_t * (Ptr to processor device error record - * section body). - * prfunc (fn ptr of print function to be used - * for output). - * Outputs: None - */ -void -ia64_log_proc_dev_err_info_print (sal_log_processor_info_t *slpi, - prfunc_t prfunc) -{ -#ifdef MCA_PRT_XTRA_DATA - size_t d_len = slpi->header.len - sizeof(sal_log_section_hdr_t); -#endif - sal_processor_static_info_t *spsi; - int i; - sal_log_mod_error_info_t *p_data; - - prfunc("+Processor Device Error Info Section\n"); - -#ifdef MCA_PRT_XTRA_DATA // for test only @FVL - { - char *p_data = (char *)&slpi->valid; - - prfunc("SAL_PROC_DEV_ERR SECTION DATA: Data buffer = %p, " - "Data size = %ld\n", (void *)p_data, d_len); - ia64_log_hexdump(p_data, d_len, prfunc); - prfunc("End of SAL_PROC_DEV_ERR SECTION DATA\n"); - } -#endif // MCA_PRT_XTRA_DATA for test only @FVL - - if (slpi->valid.proc_error_map) - prfunc(" Processor Error Map: %#lx\n", slpi->proc_error_map); - - if (slpi->valid.proc_state_param) - prfunc(" Processor State Param: %#lx\n", slpi->proc_state_parameter); - - if (slpi->valid.proc_cr_lid) - prfunc(" Processor LID: %#lx\n", slpi->proc_cr_lid); - - /* - * Note: March 2001 SAL spec states that if the number of elements in any - * of the MOD_ERROR_INFO_STRUCT arrays is zero, the entire array is - * absent. Also, current implementations only allocate space for number of - * elements used. So we walk the data pointer from here on. - */ - p_data = &slpi->info[0]; - - /* Print the cache check information if any*/ - for (i = 0 ; i < slpi->valid.num_cache_check; i++, p_data++) - ia64_log_cache_check_info_print(i, p_data, prfunc); - - /* Print the tlb check information if any*/ - for (i = 0 ; i < slpi->valid.num_tlb_check; i++, p_data++) - ia64_log_tlb_check_info_print(i, p_data, prfunc); - - /* Print the bus check information if any*/ - for (i = 0 ; i < slpi->valid.num_bus_check; i++, p_data++) - ia64_log_bus_check_info_print(i, p_data, prfunc); - - /* Print the reg file check information if any*/ - for (i = 0 ; i < slpi->valid.num_reg_file_check; i++, p_data++) - ia64_log_hexdump((u8 *)p_data, sizeof(sal_log_mod_error_info_t), - prfunc); /* Just hex dump for now */ - - /* Print the ms check information if any*/ - for (i = 0 ; i < slpi->valid.num_ms_check; i++, p_data++) - ia64_log_hexdump((u8 *)p_data, sizeof(sal_log_mod_error_info_t), - prfunc); /* Just hex dump for now */ - - /* Print CPUID registers if any*/ - if (slpi->valid.cpuid_info) { - u64 *p = (u64 *)p_data; - - prfunc(" CPUID Regs: %#lx %#lx %#lx %#lx\n", p[0], p[1], p[2], p[3]); - p_data++; - } - - /* Print processor static info if any */ - if (slpi->valid.psi_static_struct) { - spsi = (sal_processor_static_info_t *)p_data; - - /* Print branch register contents if valid */ - if (spsi->valid.br) - ia64_log_processor_regs_print(spsi->br, 8, "Branch", "br", - prfunc); - - /* Print control register contents if valid */ - if (spsi->valid.cr) - ia64_log_processor_regs_print(spsi->cr, 128, "Control", "cr", - prfunc); - - /* Print application register contents if valid */ - if (spsi->valid.ar) - ia64_log_processor_regs_print(spsi->ar, 128, "Application", - "ar", prfunc); - - /* Print region register contents if valid */ - if (spsi->valid.rr) - ia64_log_processor_regs_print(spsi->rr, 8, "Region", "rr", - prfunc); - - /* Print floating-point register contents if valid */ - if (spsi->valid.fr) - ia64_log_processor_fp_regs_print(spsi->fr, 128, "Floating-point", "fr", - prfunc); - } -} - -/* - * ia64_log_processor_info_print - * - * Display the processor-specific information logged by PAL as a part - * of MCA or INIT or CMC. - * - * Inputs : lh (Pointer of the sal log header which specifies the - * format of SAL state info as specified by the SAL spec). - * prfunc (fn ptr of print function to be used for output). - * Outputs : None - */ -void -ia64_log_processor_info_print(sal_log_record_header_t *lh, prfunc_t prfunc) -{ - sal_log_section_hdr_t *slsh; - int n_sects; - u32 ercd_pos; - - if (!lh) - return; - -#ifdef MCA_PRT_XTRA_DATA // for test only @FVL - ia64_log_prt_record_header(lh, prfunc); -#endif // MCA_PRT_XTRA_DATA for test only @FVL - - if ((ercd_pos = sizeof(sal_log_record_header_t)) >= lh->len) { - IA64_MCA_DEBUG("ia64_mca_log_print: " - "truncated SAL CMC error record. len = %d\n", - lh->len); - return; - } - - /* Print record header info */ - ia64_log_rec_header_print(lh, prfunc); - - for (n_sects = 0; (ercd_pos < lh->len); n_sects++, ercd_pos += slsh->len) { - /* point to next section header */ - slsh = (sal_log_section_hdr_t *)((char *)lh + ercd_pos); - -#ifdef MCA_PRT_XTRA_DATA // for test only @FVL - ia64_log_prt_section_header(slsh, prfunc); -#endif // MCA_PRT_XTRA_DATA for test only @FVL - - if (verify_guid(&slsh->guid, &(SAL_PROC_DEV_ERR_SECT_GUID))) { - IA64_MCA_DEBUG("ia64_mca_log_print: unsupported record section\n"); - continue; - } - - /* - * Now process processor device error record section - */ - ia64_log_proc_dev_err_info_print((sal_log_processor_info_t *)slsh, printk); - } - - IA64_MCA_DEBUG("ia64_mca_log_print: " - "found %d sections in SAL CMC error record. len = %d\n", - n_sects, lh->len); - if (!n_sects) { - prfunc("No Processor Device Error Info Section found\n"); - return; - } -} - -/* - * ia64_log_platform_info_print - * - * Format and Log the SAL Platform Error Record. - * - * Inputs : lh (Pointer to the sal error record header with format - * specified by the SAL spec). - * prfunc (fn ptr of log output function to use) - * Outputs : platform error status - */ -int -ia64_log_platform_info_print (sal_log_record_header_t *lh, prfunc_t prfunc) -{ - sal_log_section_hdr_t *slsh; - int n_sects; - u32 ercd_pos; - int platform_err = 0; - - if (!lh) - return platform_err; - -#ifdef MCA_PRT_XTRA_DATA // for test only @FVL - ia64_log_prt_record_header(lh, prfunc); -#endif // MCA_PRT_XTRA_DATA for test only @FVL - - if ((ercd_pos = sizeof(sal_log_record_header_t)) >= lh->len) { - IA64_MCA_DEBUG("ia64_mca_log_print: " - "truncated SAL error record. len = %d\n", - lh->len); - return platform_err; - } - - /* Print record header info */ - ia64_log_rec_header_print(lh, prfunc); - - for (n_sects = 0; (ercd_pos < lh->len); n_sects++, ercd_pos += slsh->len) { - /* point to next section header */ - slsh = (sal_log_section_hdr_t *)((char *)lh + ercd_pos); - -#ifdef MCA_PRT_XTRA_DATA // for test only @FVL - ia64_log_prt_section_header(slsh, prfunc); - - if (efi_guidcmp(slsh->guid, SAL_PROC_DEV_ERR_SECT_GUID) != 0) { - size_t d_len = slsh->len - sizeof(sal_log_section_hdr_t); - char *p_data = (char *)&((sal_log_mem_dev_err_info_t *)slsh)->valid; - - prfunc("Start of Platform Err Data Section: Data buffer = %p, " - "Data size = %ld\n", (void *)p_data, d_len); - ia64_log_hexdump(p_data, d_len, prfunc); - prfunc("End of Platform Err Data Section\n"); - } -#endif // MCA_PRT_XTRA_DATA for test only @FVL - - /* - * Now process CPE error record section - */ - if (efi_guidcmp(slsh->guid, SAL_PROC_DEV_ERR_SECT_GUID) == 0) { - ia64_log_proc_dev_err_info_print((sal_log_processor_info_t *)slsh, - prfunc); - } else if (efi_guidcmp(slsh->guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID) == 0) { - platform_err = 1; - prfunc("+Platform Memory Device Error Info Section\n"); - ia64_log_mem_dev_err_info_print((sal_log_mem_dev_err_info_t *)slsh, - prfunc); - } else if (efi_guidcmp(slsh->guid, SAL_PLAT_SEL_DEV_ERR_SECT_GUID) == 0) { - platform_err = 1; - prfunc("+Platform SEL Device Error Info Section\n"); - ia64_log_sel_dev_err_info_print((sal_log_sel_dev_err_info_t *)slsh, - prfunc); - } else if (efi_guidcmp(slsh->guid, SAL_PLAT_PCI_BUS_ERR_SECT_GUID) == 0) { - platform_err = 1; - prfunc("+Platform PCI Bus Error Info Section\n"); - ia64_log_pci_bus_err_info_print((sal_log_pci_bus_err_info_t *)slsh, - prfunc); - } else if (efi_guidcmp(slsh->guid, SAL_PLAT_SMBIOS_DEV_ERR_SECT_GUID) == 0) { - platform_err = 1; - prfunc("+Platform SMBIOS Device Error Info Section\n"); - ia64_log_smbios_dev_err_info_print((sal_log_smbios_dev_err_info_t *)slsh, - prfunc); - } else if (efi_guidcmp(slsh->guid, SAL_PLAT_PCI_COMP_ERR_SECT_GUID) == 0) { - platform_err = 1; - prfunc("+Platform PCI Component Error Info Section\n"); - ia64_log_pci_comp_err_info_print((sal_log_pci_comp_err_info_t *)slsh, - prfunc); - } else if (efi_guidcmp(slsh->guid, SAL_PLAT_SPECIFIC_ERR_SECT_GUID) == 0) { - platform_err = 1; - prfunc("+Platform Specific Error Info Section\n"); - ia64_log_plat_specific_err_info_print((sal_log_plat_specific_err_info_t *) - slsh, - prfunc); - } else if (efi_guidcmp(slsh->guid, SAL_PLAT_HOST_CTLR_ERR_SECT_GUID) == 0) { - platform_err = 1; - prfunc("+Platform Host Controller Error Info Section\n"); - ia64_log_host_ctlr_err_info_print((sal_log_host_ctlr_err_info_t *)slsh, - prfunc); - } else if (efi_guidcmp(slsh->guid, SAL_PLAT_BUS_ERR_SECT_GUID) == 0) { - platform_err = 1; - prfunc("+Platform Bus Error Info Section\n"); - ia64_log_plat_bus_err_info_print((sal_log_plat_bus_err_info_t *)slsh, - prfunc); - } else { - IA64_MCA_DEBUG("ia64_mca_log_print: unsupported record section\n"); - continue; - } - } - - IA64_MCA_DEBUG("ia64_mca_log_print: found %d sections in SAL error record. len = %d\n", - n_sects, lh->len); - if (!n_sects) { - prfunc("No Platform Error Info Sections found\n"); - return platform_err; - } - return platform_err; -} - -/* - * ia64_log_print - * - * Displays the contents of the OS error log information - * - * Inputs : info_type (SAL_INFO_TYPE_{MCA,INIT,CMC,CPE}) - * prfunc (fn ptr of log output function to use) - * Outputs : platform error status - */ -int -ia64_log_print(int sal_info_type, prfunc_t prfunc) -{ - int platform_err = 0; - - switch(sal_info_type) { - case SAL_INFO_TYPE_MCA: - prfunc("+CPU %d: SAL log contains MCA error record\n", smp_processor_id()); - ia64_log_rec_header_print(IA64_LOG_CURR_BUFFER(sal_info_type), prfunc); - break; - case SAL_INFO_TYPE_INIT: - prfunc("+CPU %d: SAL log contains INIT error record\n", smp_processor_id()); - ia64_log_rec_header_print(IA64_LOG_CURR_BUFFER(sal_info_type), prfunc); - break; - case SAL_INFO_TYPE_CMC: - prfunc("+BEGIN HARDWARE ERROR STATE AT CMC\n"); - ia64_log_processor_info_print(IA64_LOG_CURR_BUFFER(sal_info_type), prfunc); - prfunc("+END HARDWARE ERROR STATE AT CMC\n"); - break; - case SAL_INFO_TYPE_CPE: - prfunc("+BEGIN HARDWARE ERROR STATE AT CPE\n"); - ia64_log_platform_info_print(IA64_LOG_CURR_BUFFER(sal_info_type), prfunc); - prfunc("+END HARDWARE ERROR STATE AT CPE\n"); - break; - default: - prfunc("+MCA UNKNOWN ERROR LOG (UNIMPLEMENTED)\n"); - break; - } - return platform_err; -} - static int __init ia64_mca_disable_cpe_polling(char *str) { diff --git a/arch/ia64/sn/kernel/mca.c b/arch/ia64/sn/kernel/mca.c index efe346c14ac8..417e054f0705 100644 --- a/arch/ia64/sn/kernel/mca.c +++ b/arch/ia64/sn/kernel/mca.c @@ -68,20 +68,6 @@ print_hook(const char *fmt, ...) } - -/* - * ia64_sn2_platform_plat_specific_err_print - * - * Called by the MCA handler to log platform-specific errors. - */ -void -ia64_sn2_platform_plat_specific_err_print(int header_len, int sect_len, u8 *p_data, prfunc_t prfunc) -{ - ia64_sn_plat_specific_err_print(print_hook, p_data - sect_len); -} - - - static void sn_cpei_handler(int irq, void *devid, struct pt_regs *regs) { diff --git a/include/asm-ia64/mca.h b/include/asm-ia64/mca.h index f6439e7dd68e..0ef3f96a75f6 100644 --- a/include/asm-ia64/mca.h +++ b/include/asm-ia64/mca.h @@ -136,8 +136,6 @@ typedef struct ia64_mca_os_to_sal_state_s { */ } ia64_mca_os_to_sal_state_t; -typedef int (*prfunc_t)(const char * fmt, ...); - extern void ia64_mca_init(void); extern void ia64_os_mca_dispatch(void); extern void ia64_os_mca_dispatch_end(void); @@ -150,19 +148,9 @@ extern irqreturn_t ia64_mca_cmc_int_handler(int,void *,struct pt_regs *); extern irqreturn_t ia64_mca_cmc_int_caller(int,void *,struct pt_regs *); extern irqreturn_t ia64_mca_cpe_int_handler(int,void *,struct pt_regs *); extern irqreturn_t ia64_mca_cpe_int_caller(int,void *,struct pt_regs *); -extern int ia64_log_print(int,prfunc_t); extern void ia64_mca_cmc_vector_setup(void); extern int ia64_mca_check_errors(void); -#define PLATFORM_CALL(fn, args) printk("Platform call TBD\n") - -#define platform_mem_dev_err_print ia64_log_prt_oem_data -#define platform_pci_bus_err_print ia64_log_prt_oem_data -#define platform_pci_comp_err_print ia64_log_prt_oem_data -#define platform_plat_specific_err_print ia64_log_prt_oem_data -#define platform_host_ctlr_err_print ia64_log_prt_oem_data -#define platform_plat_bus_err_print ia64_log_prt_oem_data - #undef MCA_TEST #undef IA64_MCA_DEBUG_INFO -- cgit v1.2.3 From 5376a5774cb5499bef5d82468db4b030540a60f1 Mon Sep 17 00:00:00 2001 From: Keith Owens Date: Mon, 9 Feb 2004 22:12:30 -0800 Subject: [PATCH] ia64: mca.c cleanup - Mark variables and functions static where possible --- arch/ia64/kernel/mca.c | 41 ++++++++++++++++++++++++----------------- include/asm-ia64/mca.h | 8 +------- 2 files changed, 25 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index fca5f43fb376..2bedd10bcc72 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -44,6 +44,7 @@ * 2004-02-01 Keith Owens * Avoid deadlock when using printk() for MCA and INIT records. * Delete all record printing code, moved to salinfo_decode in user space. + * Mark variables and functions static where possible. */ #include #include @@ -79,7 +80,7 @@ typedef struct ia64_fptr { unsigned long gp; } ia64_fptr_t; -ia64_mc_info_t ia64_mc_info; +static ia64_mc_info_t ia64_mc_info; ia64_mca_sal_to_os_state_t ia64_sal_to_os_handoff_state; ia64_mca_os_to_sal_state_t ia64_os_to_sal_handoff_state; u64 ia64_mca_proc_state_dump[512]; @@ -93,6 +94,12 @@ static void ia64_mca_wakeup_ipi_wait(void); static void ia64_mca_wakeup(int cpu); static void ia64_mca_wakeup_all(void); static void ia64_log_init(int); +static irqreturn_t ia64_mca_rendez_int_handler(int,void *,struct pt_regs *); +static irqreturn_t ia64_mca_wakeup_int_handler(int,void *,struct pt_regs *); +static irqreturn_t ia64_mca_cmc_int_handler(int,void *,struct pt_regs *); +static irqreturn_t ia64_mca_cpe_int_handler(int, void *, struct pt_regs *); +static irqreturn_t ia64_mca_cmc_int_caller(int,void *,struct pt_regs *); +static irqreturn_t ia64_mca_cpe_int_caller(int,void *,struct pt_regs *); extern void ia64_monarch_init_handler (void); extern void ia64_slave_init_handler (void); static u64 ia64_log_get(int sal_info_type, u8 **buffer); @@ -199,13 +206,13 @@ ia64_mca_log_sal_error_record(int sal_info_type, int called_from_init) * platform dependent error handling */ #ifndef PLATFORM_MCA_HANDLERS -void +static void mca_handler_platform (void) { } -irqreturn_t +static irqreturn_t ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs) { IA64_MCA_DEBUG("ia64_mca_cpe_int_handler: received interrupt. CPU:%d vector = %#x\n", @@ -357,7 +364,7 @@ fetch_min_state (pal_min_state_area_t *ms, struct pt_regs *pt, struct switch_sta PUT_NAT_BIT(sw->caller_unat, &pt->r30); PUT_NAT_BIT(sw->caller_unat, &pt->r31); } -void +static void init_handler_platform (pal_min_state_area_t *ms, struct pt_regs *pt, struct switch_stack *sw) { @@ -415,7 +422,7 @@ init_handler_platform (pal_min_state_area_t *ms, * Outputs * None */ -void +static void ia64_mca_init_platform (void) { @@ -521,7 +528,7 @@ ia64_mca_cmc_vector_setup (void) * Outputs * None */ -void +static void ia64_mca_cmc_vector_disable (void *dummy) { cmcv_reg_t cmcv; @@ -548,7 +555,7 @@ ia64_mca_cmc_vector_disable (void *dummy) * Outputs * None */ -void +static void ia64_mca_cmc_vector_enable (void *dummy) { cmcv_reg_t cmcv; @@ -782,7 +789,7 @@ ia64_mca_init(void) * Inputs : None * Outputs : None */ -void +static void ia64_mca_wakeup_ipi_wait(void) { int irr_num = (IA64_MCA_WAKEUP_VECTOR >> 6); @@ -816,7 +823,7 @@ ia64_mca_wakeup_ipi_wait(void) * Inputs : cpuid * Outputs : None */ -void +static void ia64_mca_wakeup(int cpu) { platform_send_ipi(cpu, IA64_MCA_WAKEUP_VECTOR, IA64_IPI_DM_INT, 0); @@ -832,7 +839,7 @@ ia64_mca_wakeup(int cpu) * Inputs : None * Outputs : None */ -void +static void ia64_mca_wakeup_all(void) { int cpu; @@ -857,7 +864,7 @@ ia64_mca_wakeup_all(void) * Inputs : None * Outputs : None */ -irqreturn_t +static irqreturn_t ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *ptregs) { unsigned long flags; @@ -899,7 +906,7 @@ ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *ptregs) * Outputs : None * */ -irqreturn_t +static irqreturn_t ia64_mca_wakeup_int_handler(int wakeup_irq, void *arg, struct pt_regs *ptregs) { return IRQ_HANDLED; @@ -919,7 +926,7 @@ ia64_mca_wakeup_int_handler(int wakeup_irq, void *arg, struct pt_regs *ptregs) * Outputs : None */ -void +static void ia64_return_to_sal_check(void) { pal_processor_state_info_t *psp = (pal_processor_state_info_t *) @@ -1002,7 +1009,7 @@ static DECLARE_WORK(cmc_enable_work, ia64_mca_cmc_vector_enable_keventd, NULL); * Outputs * None */ -irqreturn_t +static irqreturn_t ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs) { static unsigned long cmc_history[CMC_HISTORY_LENGTH]; @@ -1105,7 +1112,7 @@ static ia64_state_log_t ia64_state_log[IA64_MAX_LOG_TYPES]; * Outputs * handled */ -irqreturn_t +static irqreturn_t ia64_mca_cmc_int_caller(int cpe_irq, void *arg, struct pt_regs *ptregs) { static int start_count = -1; @@ -1172,7 +1179,7 @@ ia64_mca_cmc_poll (unsigned long dummy) * Outputs * handled */ -irqreturn_t +static irqreturn_t ia64_mca_cpe_int_caller(int cpe_irq, void *arg, struct pt_regs *ptregs) { static int start_count = -1; @@ -1298,7 +1305,7 @@ ia64_init_handler (struct pt_regs *pt, struct switch_stack *sw) * Inputs : info_type (SAL_INFO_TYPE_{MCA,INIT,CMC,CPE}) * Outputs : None */ -void +static void ia64_log_init(int sal_info_type) { u64 max_size = 0; diff --git a/include/asm-ia64/mca.h b/include/asm-ia64/mca.h index 0ef3f96a75f6..5e56fe097585 100644 --- a/include/asm-ia64/mca.h +++ b/include/asm-ia64/mca.h @@ -2,7 +2,7 @@ * File: mca.h * Purpose: Machine check handling specific defines * - * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 1999, 2004 Silicon Graphics, Inc. * Copyright (C) Vijay Chander (vijay@engr.sgi.com) * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) */ @@ -142,12 +142,6 @@ extern void ia64_os_mca_dispatch_end(void); extern void ia64_mca_ucmc_handler(void); extern void ia64_monarch_init_handler(void); extern void ia64_slave_init_handler(void); -extern irqreturn_t ia64_mca_rendez_int_handler(int,void *,struct pt_regs *); -extern irqreturn_t ia64_mca_wakeup_int_handler(int,void *,struct pt_regs *); -extern irqreturn_t ia64_mca_cmc_int_handler(int,void *,struct pt_regs *); -extern irqreturn_t ia64_mca_cmc_int_caller(int,void *,struct pt_regs *); -extern irqreturn_t ia64_mca_cpe_int_handler(int,void *,struct pt_regs *); -extern irqreturn_t ia64_mca_cpe_int_caller(int,void *,struct pt_regs *); extern void ia64_mca_cmc_vector_setup(void); extern int ia64_mca_check_errors(void); -- cgit v1.2.3 From 9cd09cb79c45160c30a3805a91a533fa37638cfc Mon Sep 17 00:00:00 2001 From: Keith Owens Date: Mon, 9 Feb 2004 22:13:21 -0800 Subject: [PATCH] ia64: mca.c cleanup - Delete dead variables and functions --- arch/ia64/kernel/mca.c | 60 +++++++++++++------------------------------------- include/asm-ia64/mca.h | 33 --------------------------- 2 files changed, 15 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 2bedd10bcc72..3bc9284fc949 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -45,6 +45,7 @@ * Avoid deadlock when using printk() for MCA and INIT records. * Delete all record printing code, moved to salinfo_decode in user space. * Mark variables and functions static where possible. + * Delete dead variables and functions. */ #include #include @@ -73,14 +74,12 @@ #include #include -#undef MCA_PRT_XTRA_DATA - typedef struct ia64_fptr { unsigned long fp; unsigned long gp; } ia64_fptr_t; -static ia64_mc_info_t ia64_mc_info; +/* Used by mca_asm.S */ ia64_mca_sal_to_os_state_t ia64_sal_to_os_handoff_state; ia64_mca_os_to_sal_state_t ia64_os_to_sal_handoff_state; u64 ia64_mca_proc_state_dump[512]; @@ -88,8 +87,19 @@ u64 ia64_mca_stack[1024] __attribute__((aligned(16))); u64 ia64_mca_stackframe[32]; u64 ia64_mca_bspstore[1024]; u64 ia64_init_stack[KERNEL_STACK_SIZE/8] __attribute__((aligned(16))); -u64 ia64_os_mca_recovery_successful; u64 ia64_mca_serialize; + +/* In mca_asm.S */ +extern void ia64_monarch_init_handler (void); +extern void ia64_slave_init_handler (void); + +static ia64_mc_info_t ia64_mc_info; + +extern struct hw_interrupt_type irq_type_iosapic_level; + +struct ia64_mca_tlb_info ia64_mca_tlb_list[NR_CPUS]; + +/* Forward declarations, the code is not in a nice order */ static void ia64_mca_wakeup_ipi_wait(void); static void ia64_mca_wakeup(int cpu); static void ia64_mca_wakeup_all(void); @@ -100,12 +110,7 @@ static irqreturn_t ia64_mca_cmc_int_handler(int,void *,struct pt_regs *); static irqreturn_t ia64_mca_cpe_int_handler(int, void *, struct pt_regs *); static irqreturn_t ia64_mca_cmc_int_caller(int,void *,struct pt_regs *); static irqreturn_t ia64_mca_cpe_int_caller(int,void *,struct pt_regs *); -extern void ia64_monarch_init_handler (void); -extern void ia64_slave_init_handler (void); static u64 ia64_log_get(int sal_info_type, u8 **buffer); -extern struct hw_interrupt_type irq_type_iosapic_level; - -struct ia64_mca_tlb_info ia64_mca_tlb_list[NR_CPUS]; static struct irqaction cmci_irqaction = { .handler = ia64_mca_cmc_int_handler, @@ -206,11 +211,6 @@ ia64_mca_log_sal_error_record(int sal_info_type, int called_from_init) * platform dependent error handling */ #ifndef PLATFORM_MCA_HANDLERS -static void -mca_handler_platform (void) -{ - -} static irqreturn_t ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs) @@ -411,23 +411,6 @@ init_handler_platform (pal_min_state_area_t *ms, while (1); /* hang city if no debugger */ } -/* - * ia64_mca_init_platform - * - * External entry for platform specific MCA initialization. - * - * Inputs - * None - * - * Outputs - * None - */ -static void -ia64_mca_init_platform (void) -{ - -} - /* * ia64_mca_check_errors * @@ -627,9 +610,6 @@ ia64_mca_init(void) IA64_MCA_DEBUG("ia64_mca_init: begin\n"); - /* initialize recovery success indicator */ - ia64_os_mca_recovery_successful = 0; - /* Clear the Rendez checkin flag for all cpus */ for(i = 0 ; i < NR_CPUS; i++) ia64_mc_info.imi_rendez_checkin[i] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE; @@ -697,7 +677,7 @@ ia64_mca_init(void) /* * XXX - disable SAL checksum by setting size to 0, should be - * IA64_INIT_HANDLER_SIZE + * size of the actual init handler in mca_asm.S. */ ia64_mc_info.imi_monarch_init_handler = ia64_tpa(mon_init_ptr->fp); ia64_mc_info.imi_monarch_init_handler_size = 0; @@ -766,17 +746,7 @@ ia64_mca_init(void) ia64_log_init(SAL_INFO_TYPE_CMC); ia64_log_init(SAL_INFO_TYPE_CPE); -#if defined(MCA_TEST) - mca_test(); -#endif /* #if defined(MCA_TEST) */ - printk(KERN_INFO "Mca related initialization done\n"); - - /* commented out because this is done elsewhere */ -#if 0 - /* Do post-failure MCA error logging */ - ia64_mca_check_errors(); -#endif } /* diff --git a/include/asm-ia64/mca.h b/include/asm-ia64/mca.h index 5e56fe097585..91b46124c9b2 100644 --- a/include/asm-ia64/mca.h +++ b/include/asm-ia64/mca.h @@ -20,23 +20,8 @@ #include #include -/* These are the return codes from all the IA64_MCA specific interfaces */ -typedef int ia64_mca_return_code_t; - -enum { - IA64_MCA_SUCCESS = 0, - IA64_MCA_FAILURE = 1 -}; - #define IA64_MCA_RENDEZ_TIMEOUT (20 * 1000) /* value in milliseconds - 20 seconds */ -#define IA64_CMC_INT_DISABLE 0 -#define IA64_CMC_INT_ENABLE 1 - - -typedef u32 int_vector_t; -typedef u64 millisec_t; - typedef union cmcv_reg_u { u64 cmcv_regval; struct { @@ -53,10 +38,6 @@ typedef union cmcv_reg_u { #define cmcv_mask cmcv_reg_s.cmcr_mask #define cmcv_vector cmcv_reg_s.cmcr_vector - -#define IA64_MCA_UCMC_HANDLER_SIZE 0x10 -#define IA64_INIT_HANDLER_SIZE 0x10 - enum { IA64_MCA_RENDEZ_CHECKIN_NOTDONE = 0x0, IA64_MCA_RENDEZ_CHECKIN_DONE = 0x1 @@ -85,16 +66,6 @@ typedef struct ia64_mc_info_s { } ia64_mc_info_t; -/* Possible rendez states passed from SAL to OS during MCA - * handoff - */ -enum { - IA64_MCA_RENDEZ_NOT_RQD = 0x0, - IA64_MCA_RENDEZ_DONE_WITHOUT_INIT = 0x1, - IA64_MCA_RENDEZ_DONE_WITH_INIT = 0x2, - IA64_MCA_RENDEZ_FAILURE = -1 -}; - typedef struct ia64_mca_sal_to_os_state_s { u64 imsto_os_gp; /* GP of the os registered with the SAL */ u64 imsto_pal_proc; /* PAL_PROC entry point - physical addr */ @@ -145,10 +116,6 @@ extern void ia64_slave_init_handler(void); extern void ia64_mca_cmc_vector_setup(void); extern int ia64_mca_check_errors(void); -#undef MCA_TEST - -#undef IA64_MCA_DEBUG_INFO - #if defined(IA64_MCA_DEBUG_INFO) # define IA64_MCA_DEBUG(fmt...) printk(fmt) #else -- cgit v1.2.3 From ae2057b69383dd5972e781857ae1b6e1f1ef2cf3 Mon Sep 17 00:00:00 2001 From: Keith Owens Date: Mon, 9 Feb 2004 22:14:47 -0800 Subject: [PATCH] ia64: mca.c cleanup - Bjorn's printk cleanup --- arch/ia64/kernel/mca.c | 78 +++++++++++++++++++++++++++----------------------- include/asm-ia64/mca.h | 5 ---- 2 files changed, 42 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 2569c6e0aef0..4d5b8e5df3d2 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -76,6 +76,12 @@ #include #include +#if defined(IA64_MCA_DEBUG_INFO) +# define IA64_MCA_DEBUG(fmt...) printk(fmt) +#else +# define IA64_MCA_DEBUG(fmt...) +#endif + typedef struct ia64_fptr { unsigned long fp; unsigned long gp; @@ -215,8 +221,8 @@ ia64_log_get(int sal_info_type, u8 **buffer) IA64_LOG_INDEX_INC(sal_info_type); IA64_LOG_UNLOCK(sal_info_type); if (irq_safe) { - IA64_MCA_DEBUG("ia64_log_get: SAL error record type %d retrieved. " - "Record length = %ld\n", sal_info_type, total_len); + IA64_MCA_DEBUG("%s: SAL error record type %d retrieved. " + "Record length = %ld\n", __FUNCTION__, sal_info_type, total_len); } *buffer = (u8 *) log_buffer; return total_len; @@ -267,8 +273,8 @@ ia64_mca_log_sal_error_record(int sal_info_type, int called_from_init) static irqreturn_t ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs) { - IA64_MCA_DEBUG("ia64_mca_cpe_int_handler: received interrupt. CPU:%d vector = %#x\n", - smp_processor_id(), cpe_irq); + IA64_MCA_DEBUG("%s: received interrupt. CPU:%d vector = %#x\n", + __FUNCTION__, smp_processor_id(), cpe_irq); /* SAL spec states this should run w/ interrupts enabled */ local_irq_enable(); @@ -509,13 +515,13 @@ ia64_mca_register_cpev (int cpev) isrv = ia64_sal_mc_set_params(SAL_MC_PARAM_CPE_INT, SAL_MC_PARAM_MECHANISM_INT, cpev, 0, 0); if (isrv.status) { - printk(KERN_ERR "ia64_mca_platform_init: failed to register Corrected " - "Platform Error interrupt vector with SAL.\n"); + printk(KERN_ERR "Failed to register Corrected Platform " + "Error interrupt vector with SAL (status %ld)\n", isrv.status); return; } - IA64_MCA_DEBUG("ia64_mca_platform_init: corrected platform error " - "vector %#x setup and enabled\n", cpev); + IA64_MCA_DEBUG("%s: corrected platform error " + "vector %#x setup and enabled\n", __FUNCTION__, cpev); } #endif /* CONFIG_ACPI */ @@ -543,12 +549,12 @@ ia64_mca_cmc_vector_setup (void) cmcv.cmcv_vector = IA64_CMC_VECTOR; ia64_setreg(_IA64_REG_CR_CMCV, cmcv.cmcv_regval); - IA64_MCA_DEBUG("ia64_mca_platform_init: CPU %d corrected " + IA64_MCA_DEBUG("%s: CPU %d corrected " "machine check vector %#x setup and enabled.\n", - smp_processor_id(), IA64_CMC_VECTOR); + __FUNCTION__, smp_processor_id(), IA64_CMC_VECTOR); - IA64_MCA_DEBUG("ia64_mca_platform_init: CPU %d CMCV = %#016lx\n", - smp_processor_id(), ia64_getreg(_IA64_REG_CR_CMCV)); + IA64_MCA_DEBUG("%s: CPU %d CMCV = %#016lx\n", + __FUNCTION__, smp_processor_id(), ia64_getreg(_IA64_REG_CR_CMCV)); } /* @@ -573,9 +579,9 @@ ia64_mca_cmc_vector_disable (void *dummy) cmcv.cmcv_mask = 1; /* Mask/disable interrupt */ ia64_setreg(_IA64_REG_CR_CMCV, cmcv.cmcv_regval) - IA64_MCA_DEBUG("ia64_mca_cmc_vector_disable: CPU %d corrected " + IA64_MCA_DEBUG("%s: CPU %d corrected " "machine check vector %#x disabled.\n", - smp_processor_id(), cmcv.cmcv_vector); + __FUNCTION__, smp_processor_id(), cmcv.cmcv_vector); } /* @@ -600,9 +606,9 @@ ia64_mca_cmc_vector_enable (void *dummy) cmcv.cmcv_mask = 0; /* Unmask/enable interrupt */ ia64_setreg(_IA64_REG_CR_CMCV, cmcv.cmcv_regval) - IA64_MCA_DEBUG("ia64_mca_cmc_vector_enable: CPU %d corrected " + IA64_MCA_DEBUG("%s: CPU %d corrected " "machine check vector %#x enabled.\n", - smp_processor_id(), cmcv.cmcv_vector); + __FUNCTION__, smp_processor_id(), cmcv.cmcv_vector); } /* @@ -865,8 +871,8 @@ ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs) static int index; static spinlock_t cmc_history_lock = SPIN_LOCK_UNLOCKED; - IA64_MCA_DEBUG("ia64_mca_cmc_int_handler: received interrupt vector = %#x on CPU %d\n", - cmc_irq, smp_processor_id()); + IA64_MCA_DEBUG("%s: received interrupt vector = %#x on CPU %d\n", + __FUNCTION__, cmc_irq, smp_processor_id()); /* SAL spec states this should run w/ interrupts enabled */ local_irq_enable(); @@ -896,7 +902,7 @@ ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs) * make sure there's a log somewhere that indicates * something is generating more than we can handle. */ - printk(KERN_WARNING "%s: WARNING: Switching to polling CMC handler, error records may be lost\n", __FUNCTION__); + printk(KERN_WARNING "WARNING: Switching to polling CMC handler; error records may be lost\n"); mod_timer(&cmc_poll_timer, jiffies + CMC_POLL_INTERVAL); @@ -948,7 +954,7 @@ ia64_mca_cmc_int_caller(int cpe_irq, void *arg, struct pt_regs *ptregs) /* If no log record, switch out of polling mode */ if (start_count == IA64_LOG_COUNT(SAL_INFO_TYPE_CMC)) { - printk(KERN_WARNING "%s: Returning to interrupt driven CMC handler\n", __FUNCTION__); + printk(KERN_WARNING "Returning to interrupt driven CMC handler\n"); schedule_work(&cmc_enable_work); cmc_polling_enabled = 0; @@ -1156,7 +1162,7 @@ ia64_mca_init(void) struct ia64_sal_retval isrv; u64 timeout = IA64_MCA_RENDEZ_TIMEOUT; /* platform specific */ - IA64_MCA_DEBUG("ia64_mca_init: begin\n"); + IA64_MCA_DEBUG("%s: begin\n", __FUNCTION__); /* Clear the Rendez checkin flag for all cpus */ for(i = 0 ; i < NR_CPUS; i++) @@ -1177,13 +1183,13 @@ ia64_mca_init(void) if (rc == 0) break; if (rc == -2) { - printk(KERN_INFO "ia64_mca_init: increasing MCA rendezvous timeout from " - "%ld to %ld\n", timeout, isrv.v0); + printk(KERN_INFO "Increasing MCA rendezvous timeout from " + "%ld to %ld milliseconds\n", timeout, isrv.v0); timeout = isrv.v0; continue; } - printk(KERN_ERR "ia64_mca_init: Failed to register rendezvous interrupt " - "with SAL. rc = %ld\n", rc); + printk(KERN_ERR "Failed to register rendezvous interrupt " + "with SAL (status %ld)\n", rc); return; } @@ -1194,12 +1200,12 @@ ia64_mca_init(void) 0, 0); rc = isrv.status; if (rc) { - printk(KERN_ERR "ia64_mca_init: Failed to register wakeup interrupt with SAL. " - "rc = %ld\n", rc); + printk(KERN_ERR "Failed to register wakeup interrupt with SAL " + "(status %ld)\n", rc); return; } - IA64_MCA_DEBUG("ia64_mca_init: registered mca rendezvous spinloop and wakeup mech.\n"); + IA64_MCA_DEBUG("%s: registered MCA rendezvous spinloop and wakeup mech.\n", __FUNCTION__); ia64_mc_info.imi_mca_handler = ia64_tpa(mca_hldlr_ptr->fp); /* @@ -1215,12 +1221,12 @@ ia64_mca_init(void) ia64_mc_info.imi_mca_handler_size, 0, 0, 0))) { - printk(KERN_ERR "ia64_mca_init: Failed to register os mca handler with SAL. " - "rc = %ld\n", rc); + printk(KERN_ERR "Failed to register OS MCA handler with SAL " + "(status %ld)\n", rc); return; } - IA64_MCA_DEBUG("ia64_mca_init: registered os mca handler with SAL at 0x%lx, gp = 0x%lx\n", + IA64_MCA_DEBUG("%s: registered OS MCA handler with SAL at 0x%lx, gp = 0x%lx\n", __FUNCTION__, ia64_mc_info.imi_mca_handler, ia64_tpa(mca_hldlr_ptr->gp)); /* @@ -1232,7 +1238,7 @@ ia64_mca_init(void) ia64_mc_info.imi_slave_init_handler = ia64_tpa(slave_init_ptr->fp); ia64_mc_info.imi_slave_init_handler_size = 0; - IA64_MCA_DEBUG("ia64_mca_init: os init handler at %lx\n", + IA64_MCA_DEBUG("%s: OS INIT handler at %lx\n", __FUNCTION__, ia64_mc_info.imi_monarch_init_handler); /* Register the os init handler with SAL */ @@ -1244,12 +1250,12 @@ ia64_mca_init(void) ia64_tpa(ia64_getreg(_IA64_REG_GP)), ia64_mc_info.imi_slave_init_handler_size))) { - printk(KERN_ERR "ia64_mca_init: Failed to register m/s init handlers with SAL. " - "rc = %ld\n", rc); + printk(KERN_ERR "Failed to register m/s INIT handlers with SAL " + "(status %ld)\n", rc); return; } - IA64_MCA_DEBUG("ia64_mca_init: registered os init handler with SAL\n"); + IA64_MCA_DEBUG("%s: registered OS INIT handler with SAL\n", __FUNCTION__); /* * Configure the CMCI/P vector and handler. Interrupts for CMC are @@ -1294,7 +1300,7 @@ ia64_mca_init(void) ia64_log_init(SAL_INFO_TYPE_CMC); ia64_log_init(SAL_INFO_TYPE_CPE); - printk(KERN_INFO "Mca related initialization done\n"); + printk(KERN_INFO "MCA related initialization done\n"); } /* diff --git a/include/asm-ia64/mca.h b/include/asm-ia64/mca.h index 91b46124c9b2..81fce8f49dff 100644 --- a/include/asm-ia64/mca.h +++ b/include/asm-ia64/mca.h @@ -116,10 +116,5 @@ extern void ia64_slave_init_handler(void); extern void ia64_mca_cmc_vector_setup(void); extern int ia64_mca_check_errors(void); -#if defined(IA64_MCA_DEBUG_INFO) -# define IA64_MCA_DEBUG(fmt...) printk(fmt) -#else -# define IA64_MCA_DEBUG(fmt...) -#endif #endif /* !__ASSEMBLY__ */ #endif /* _ASM_IA64_MCA_H */ -- cgit v1.2.3 From 91aea06ec6d0a0349a7829454c986f253909c37a Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Tue, 10 Feb 2004 02:51:34 -0800 Subject: ia64: Based on patch by Stephane Eranian: Make fpswa version info available via /proc/efi/fpswa, rather than printing it at boot time. --- arch/ia64/kernel/efivars.c | 43 ++++++++++++++++++++++++++++++++++++++++++- arch/ia64/kernel/traps.c | 11 ++--------- include/asm-ia64/fpswa.h | 2 ++ 3 files changed, 46 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/efivars.c b/arch/ia64/kernel/efivars.c index 631fda907201..1412b78ca6bb 100644 --- a/arch/ia64/kernel/efivars.c +++ b/arch/ia64/kernel/efivars.c @@ -29,9 +29,12 @@ * * Changelog: * + * 10 Feb 2004 - Stephane Eranian + * Provide FPSWA version number via /proc/efi/fpswa + * * 10 Dec 2002 - Matt Domsch * fix locking per Peter Chubb's findings - * + * * 25 Mar 2002 - Matt Domsch * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse() * @@ -70,6 +73,7 @@ #include #include +#include #include MODULE_AUTHOR("Matt Domsch "); @@ -407,6 +411,37 @@ static struct file_operations efi_systab_fops = { .read = efi_systab_read, }; +static ssize_t +efi_fpswa_read (struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + ssize_t size, length; + char str[32]; + void *data; + + snprintf(str, sizeof(str), "revision=%u.%u\n", + fpswa_interface->revision >> 16, fpswa_interface->revision & 0xffff); + + length = strlen(str); + + if (*ppos >= length) + return 0; + + data = str + file->f_pos; + size = length - file->f_pos; + if (size > count) + size = count; + if (copy_to_user(buffer, data, size)) + return -EFAULT; + + *ppos += size; + return size; +} + +static struct proc_dir_entry *efi_fpswa_entry; +static struct file_operations efi_fpswa_fops = { + .read = efi_fpswa_read, +}; + static int __init efivars_init(void) { @@ -429,6 +464,12 @@ efivars_init(void) if (efi_systab_entry) efi_systab_entry->proc_fops = &efi_systab_fops; + if (fpswa_interface) { + efi_fpswa_entry = create_proc_entry("fpswa", S_IRUGO, efi_dir); + if (efi_fpswa_entry) + efi_fpswa_entry->proc_fops = &efi_fpswa_fops; + } + efi_vars_dir = proc_mkdir("vars", efi_dir); /* Per EFI spec, the maximum storage allocated for both diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index 0db770ff9183..ec0b260dbb65 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -46,21 +46,14 @@ register double f30 asm ("f30"); register double f31 asm ("f31"); extern spinlock_t timerlist_lock; -static fpswa_interface_t *fpswa_interface; +fpswa_interface_t *fpswa_interface; void __init trap_init (void) { - int major = 0, minor = 0; - - if (ia64_boot_param->fpswa) { + if (ia64_boot_param->fpswa) /* FPSWA fixup: make the interface pointer a kernel virtual address: */ fpswa_interface = __va(ia64_boot_param->fpswa); - major = fpswa_interface->revision >> 16; - minor = fpswa_interface->revision & 0xffff; - } - printk(KERN_INFO "fpswa interface at %lx (rev %d.%d)\n", - ia64_boot_param->fpswa, major, minor); } /* diff --git a/include/asm-ia64/fpswa.h b/include/asm-ia64/fpswa.h index 8986f033c815..62edfceadaa6 100644 --- a/include/asm-ia64/fpswa.h +++ b/include/asm-ia64/fpswa.h @@ -68,4 +68,6 @@ typedef struct { efi_fpswa_t fpswa; } fpswa_interface_t; +extern fpswa_interface_t *fpswa_interface; + #endif /* _ASM_IA64_FPSWA_H */ -- cgit v1.2.3 From 4c5eb8aad0f842e81baf13d39d940b871899544b Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Tue, 10 Feb 2004 02:59:35 -0800 Subject: ia64: Fix some more warnings caused by casts used as l-values. --- arch/ia64/lib/io.c | 16 ++++++++-------- include/asm-ia64/percpu.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/ia64/lib/io.c b/arch/ia64/lib/io.c index abdb6ba06148..90f0f3061f91 100644 --- a/arch/ia64/lib/io.c +++ b/arch/ia64/lib/io.c @@ -9,13 +9,13 @@ * This needs to be optimized. */ void -__ia64_memcpy_fromio (void * to, unsigned long from, long count) +__ia64_memcpy_fromio (void *to, unsigned long from, long count) { + char *dst = to; + while (count) { count--; - *(char *) to = readb(from); - ((char *) to)++; - from++; + *dst++ = readb(from++); } } EXPORT_SYMBOL(__ia64_memcpy_fromio); @@ -25,13 +25,13 @@ EXPORT_SYMBOL(__ia64_memcpy_fromio); * This needs to be optimized. */ void -__ia64_memcpy_toio (unsigned long to, void * from, long count) +__ia64_memcpy_toio (unsigned long to, void *from, long count) { + char *src = from; + while (count) { count--; - writeb(*(char *) from, to); - ((char *) from)++; - to++; + writeb(*src++, to++); } } EXPORT_SYMBOL(__ia64_memcpy_toio); diff --git a/include/asm-ia64/percpu.h b/include/asm-ia64/percpu.h index a44ee78e999f..1e87f19dad56 100644 --- a/include/asm-ia64/percpu.h +++ b/include/asm-ia64/percpu.h @@ -50,7 +50,7 @@ extern void *per_cpu_init(void); #else /* ! SMP */ -#define per_cpu(var, cpu) ((void)cpu, per_cpu__##var) +#define per_cpu(var, cpu) (*((void)cpu, &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var #define per_cpu_init() (__phys_per_cpu_start) -- cgit v1.2.3 From 7baf73ec2214d1ad091f8768ca38b39f6ec4a57a Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Tue, 10 Feb 2004 05:13:54 -0800 Subject: ia64: Correct init_task.rbs_bot value (not that it matters). --- include/asm-ia64/processor.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index 5641a23f990c..4b1870a784cb 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -289,19 +289,19 @@ struct thread_struct { struct ia64_fpreg fph[96]; /* saved/loaded on demand */ }; -#define INIT_THREAD { \ - .flags = 0, \ - .on_ustack = 0, \ - .ksp = 0, \ - .map_base = DEFAULT_MAP_BASE, \ - .rbs_bot = DEFAULT_USER_STACK_SIZE, \ - .task_size = DEFAULT_TASK_SIZE, \ - .last_fph_cpu = -1, \ - INIT_THREAD_IA32 \ - INIT_THREAD_PM \ - .dbr = {0, }, \ - .ibr = {0, }, \ - .fph = {{{{0}}}, } \ +#define INIT_THREAD { \ + .flags = 0, \ + .on_ustack = 0, \ + .ksp = 0, \ + .map_base = DEFAULT_MAP_BASE, \ + .rbs_bot = STACK_TOP - DEFAULT_USER_STACK_SIZE, \ + .task_size = DEFAULT_TASK_SIZE, \ + .last_fph_cpu = -1, \ + INIT_THREAD_IA32 \ + INIT_THREAD_PM \ + .dbr = {0, }, \ + .ibr = {0, }, \ + .fph = {{{{0}}}, } \ } #define start_thread(regs,new_ip,new_sp) do { \ -- cgit v1.2.3 From 92a14cc38977554322c188adfba4486274f4b407 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 12:50:52 +1100 Subject: ppc64: Start of PowerMac G5 merge, add all arch and include files --- arch/ppc64/kernel/btext.c | 748 ++++++++++++++++++++++++++++++++++ arch/ppc64/kernel/cpu_setup_power4.S | 179 +++++++++ arch/ppc64/kernel/idle_power4.S | 79 ++++ arch/ppc64/kernel/of_device.c | 272 +++++++++++++ arch/ppc64/kernel/open_pic_u3.c | 348 ++++++++++++++++ arch/ppc64/kernel/pSeries_nvram.c | 150 +++++++ arch/ppc64/kernel/pci_dma_direct.c | 88 ++++ arch/ppc64/kernel/pmac.h | 32 ++ arch/ppc64/kernel/pmac_feature.c | 654 ++++++++++++++++++++++++++++++ arch/ppc64/kernel/pmac_low_i2c.c | 513 ++++++++++++++++++++++++ arch/ppc64/kernel/pmac_nvram.c | 495 +++++++++++++++++++++++ arch/ppc64/kernel/pmac_pci.c | 755 +++++++++++++++++++++++++++++++++++ arch/ppc64/kernel/pmac_setup.c | 504 +++++++++++++++++++++++ arch/ppc64/kernel/pmac_smp.c | 174 ++++++++ arch/ppc64/kernel/pmac_time.c | 157 ++++++++ arch/ppc64/kernel/smp-tbsync.c | 179 +++++++++ include/asm-ppc64/bootx.h | 135 +++++++ include/asm-ppc64/btext.h | 29 ++ include/asm-ppc64/dbdma.h | 2 + include/asm-ppc64/keylargo.h | 2 + include/asm-ppc64/macio.h | 2 + include/asm-ppc64/of_device.h | 2 + include/asm-ppc64/offsets.h | 110 +++++ include/asm-ppc64/pmac_feature.h | 2 + include/asm-ppc64/pmac_low_i2c.h | 2 + include/asm-ppc64/uninorth.h | 2 + include/asm-ppc64/xmon.h | 17 + 27 files changed, 5632 insertions(+) create mode 100644 arch/ppc64/kernel/btext.c create mode 100644 arch/ppc64/kernel/cpu_setup_power4.S create mode 100644 arch/ppc64/kernel/idle_power4.S create mode 100644 arch/ppc64/kernel/of_device.c create mode 100644 arch/ppc64/kernel/open_pic_u3.c create mode 100644 arch/ppc64/kernel/pSeries_nvram.c create mode 100644 arch/ppc64/kernel/pci_dma_direct.c create mode 100644 arch/ppc64/kernel/pmac.h create mode 100644 arch/ppc64/kernel/pmac_feature.c create mode 100644 arch/ppc64/kernel/pmac_low_i2c.c create mode 100644 arch/ppc64/kernel/pmac_nvram.c create mode 100644 arch/ppc64/kernel/pmac_pci.c create mode 100644 arch/ppc64/kernel/pmac_setup.c create mode 100644 arch/ppc64/kernel/pmac_smp.c create mode 100644 arch/ppc64/kernel/pmac_time.c create mode 100644 arch/ppc64/kernel/smp-tbsync.c create mode 100644 include/asm-ppc64/bootx.h create mode 100644 include/asm-ppc64/btext.h create mode 100644 include/asm-ppc64/dbdma.h create mode 100644 include/asm-ppc64/keylargo.h create mode 100644 include/asm-ppc64/macio.h create mode 100644 include/asm-ppc64/of_device.h create mode 100644 include/asm-ppc64/offsets.h create mode 100644 include/asm-ppc64/pmac_feature.h create mode 100644 include/asm-ppc64/pmac_low_i2c.h create mode 100644 include/asm-ppc64/uninorth.h create mode 100644 include/asm-ppc64/xmon.h (limited to 'include') diff --git a/arch/ppc64/kernel/btext.c b/arch/ppc64/kernel/btext.c new file mode 100644 index 000000000000..6e07b255c507 --- /dev/null +++ b/arch/ppc64/kernel/btext.c @@ -0,0 +1,748 @@ +/* + * Procedures for drawing on the screen early on in the boot process. + * + * Benjamin Herrenschmidt + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef NO_SCROLL + +#ifndef NO_SCROLL +static void scrollscreen(void); +#endif + +static void draw_byte(unsigned char c, long locX, long locY); +static void draw_byte_32(unsigned char *bits, unsigned int *base, int rb); +static void draw_byte_16(unsigned char *bits, unsigned int *base, int rb); +static void draw_byte_8(unsigned char *bits, unsigned int *base, int rb); + +static int g_loc_X; +static int g_loc_Y; +static int g_max_loc_X; +static int g_max_loc_Y; + +unsigned long disp_BAT[2] __initdata = {0, 0}; + +#define cmapsz (16*256) + +static unsigned char vga_font[cmapsz]; + +int boot_text_mapped; +int force_printk_to_btext = 0; + +boot_infos_t disp_bi; + +extern char *klimit; + +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +void __init btext_setup_display(int width, int height, int depth, int pitch, + unsigned long address) +{ + unsigned long offset = reloc_offset(); + boot_infos_t* bi = PTRRELOC(&disp_bi); + + RELOC(g_loc_X) = 0; + RELOC(g_loc_Y) = 0; + RELOC(g_max_loc_X) = width / 8; + RELOC(g_max_loc_Y) = height / 16; + bi->logicalDisplayBase = (unsigned char *)address; + bi->dispDeviceBase = (unsigned char *)address; + bi->dispDeviceRowBytes = pitch; + bi->dispDeviceDepth = depth; + bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0; + bi->dispDeviceRect[2] = width; + bi->dispDeviceRect[3] = height; + RELOC(boot_text_mapped) = 1; +} + +/* Here's a small text engine to use during early boot + * or for debugging purposes + * + * todo: + * + * - build some kind of vgacon with it to enable early printk + * - move to a separate file + * - add a few video driver hooks to keep in sync with display + * changes. + */ + +void map_boot_text(void) +{ + unsigned long base, offset, size; + boot_infos_t *bi = &disp_bi; + unsigned char *vbase; + + /* By default, we are no longer mapped */ + boot_text_mapped = 0; + if (bi->dispDeviceBase == 0) + return; + base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL; + offset = ((unsigned long) bi->dispDeviceBase) - base; + size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset + + bi->dispDeviceRect[0]; + vbase = __ioremap(base, size, _PAGE_NO_CACHE); + if (vbase == 0) + return; + bi->logicalDisplayBase = vbase + offset; + boot_text_mapped = 1; +} + +/* Calc the base address of a given point (x,y) */ +static unsigned char * calc_base(boot_infos_t *bi, int x, int y) +{ + unsigned char *base; + + base = bi->logicalDisplayBase; + if (base == 0) + base = bi->dispDeviceBase; + base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3); + base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes; + return base; +} + +/* Adjust the display to a new resolution */ +void btext_update_display(unsigned long phys, int width, int height, + int depth, int pitch) +{ + boot_infos_t *bi = &disp_bi; + + if (bi->dispDeviceBase == 0) + return; + + /* check it's the same frame buffer (within 256MB) */ + if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000) + return; + + bi->dispDeviceBase = (__u8 *) phys; + bi->dispDeviceRect[0] = 0; + bi->dispDeviceRect[1] = 0; + bi->dispDeviceRect[2] = width; + bi->dispDeviceRect[3] = height; + bi->dispDeviceDepth = depth; + bi->dispDeviceRowBytes = pitch; + if (boot_text_mapped) { + iounmap(bi->logicalDisplayBase); + boot_text_mapped = 0; + } + map_boot_text(); + g_loc_X = 0; + g_loc_Y = 0; + g_max_loc_X = width / 8; + g_max_loc_Y = height / 16; +} + +void btext_clearscreen(void) +{ + unsigned long offset = reloc_offset(); + boot_infos_t* bi = PTRRELOC(&disp_bi); + unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); + unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 3; + int i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + { + unsigned long *ptr = base; + for(j=width; j; --j) + *(ptr++) = 0; + base += (bi->dispDeviceRowBytes >> 3); + } +} + +#ifndef NO_SCROLL +static void scrollscreen(void) +{ + unsigned long offset = reloc_offset(); + boot_infos_t* bi = PTRRELOC(&disp_bi); + unsigned long *src = (unsigned long *)calc_base(bi,0,16); + unsigned long *dst = (unsigned long *)calc_base(bi,0,0); + unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 3; + int i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++) + { + unsigned long *src_ptr = src; + unsigned long *dst_ptr = dst; + for(j=width; j; --j) + *(dst_ptr++) = *(src_ptr++); + src += (bi->dispDeviceRowBytes >> 3); + dst += (bi->dispDeviceRowBytes >> 3); + } + for (i=0; i<16; i++) + { + unsigned long *dst_ptr = dst; + for(j=width; j; --j) + *(dst_ptr++) = 0; + dst += (bi->dispDeviceRowBytes >> 3); + } +} +#endif /* ndef NO_SCROLL */ + +void btext_drawchar(char c) +{ + unsigned long offset = reloc_offset(); + int cline = 0; +#ifdef NO_SCROLL + int x; +#endif + if (!RELOC(boot_text_mapped)) + return; + + switch (c) { + case '\b': + if (RELOC(g_loc_X) > 0) + --RELOC(g_loc_X); + break; + case '\t': + RELOC(g_loc_X) = (RELOC(g_loc_X) & -8) + 8; + break; + case '\r': + RELOC(g_loc_X) = 0; + break; + case '\n': + RELOC(g_loc_X) = 0; + RELOC(g_loc_Y)++; + cline = 1; + break; + default: + draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y)); + } + if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) { + RELOC(g_loc_X) = 0; + RELOC(g_loc_Y)++; + cline = 1; + } +#ifndef NO_SCROLL + while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) { + scrollscreen(); + RELOC(g_loc_Y)--; + } +#else + /* wrap around from bottom to top of screen so we don't + waste time scrolling each line. -- paulus. */ + if (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) + RELOC(g_loc_Y) = 0; + if (cline) { + for (x = 0; x < RELOC(g_max_loc_X); ++x) + draw_byte(' ', x, RELOC(g_loc_Y)); + } +#endif +} + +void btext_drawstring(const char *c) +{ + unsigned long offset = reloc_offset(); + + if (!RELOC(boot_text_mapped)) + return; + while (*c) + btext_drawchar(*c++); +} + +void btext_drawhex(unsigned long v) +{ + unsigned long offset = reloc_offset(); + char *hex_table = RELOC("0123456789abcdef"); + + if (!RELOC(boot_text_mapped)) + return; + btext_drawchar(hex_table[(v >> 60) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 56) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 52) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 48) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 44) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 40) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 36) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 32) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]); + btext_drawchar(' '); +} + +static void draw_byte(unsigned char c, long locX, long locY) +{ + unsigned long offset = reloc_offset(); + boot_infos_t* bi = PTRRELOC(&disp_bi); + unsigned char *base = calc_base(bi, locX << 3, locY << 4); + unsigned char *font = PTRRELOC(&vga_font[((unsigned int)c) * 16]); + int rb = bi->dispDeviceRowBytes; + +#if 0 + switch(bi->dispDeviceDepth) { + case 24: + case 32: + draw_byte_32(font, (unsigned int *)base, rb); + break; + case 15: + case 16: + draw_byte_16(font, (unsigned int *)base, rb); + break; + case 8: + draw_byte_8(font, (unsigned int *)base, rb); + break; + } +#else + if(bi->dispDeviceDepth == 24 || + bi->dispDeviceDepth == 32) { + draw_byte_32(font, (unsigned int *)base, rb); + } else if(bi->dispDeviceDepth == 15 || + bi->dispDeviceDepth == 16) { + draw_byte_16(font, (unsigned int *)base, rb); + } else if(bi->dispDeviceDepth == 8) { + draw_byte_8(font, (unsigned int *)base, rb); + } +#endif +} + +static unsigned int expand_bits_8[16] = { + 0x00000000, + 0x000000ff, + 0x0000ff00, + 0x0000ffff, + 0x00ff0000, + 0x00ff00ff, + 0x00ffff00, + 0x00ffffff, + 0xff000000, + 0xff0000ff, + 0xff00ff00, + 0xff00ffff, + 0xffff0000, + 0xffff00ff, + 0xffffff00, + 0xffffffff +}; + +static unsigned int expand_bits_16[4] = { + 0x00000000, + 0x0000ffff, + 0xffff0000, + 0xffffffff +}; + + +static void draw_byte_32(unsigned char *font, unsigned int *base, int rb) +{ + int l, bits; + int fg = 0xFFFFFFFFUL; + int bg = 0x00000000UL; + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (-(bits >> 7) & fg) ^ bg; + base[1] = (-((bits >> 6) & 1) & fg) ^ bg; + base[2] = (-((bits >> 5) & 1) & fg) ^ bg; + base[3] = (-((bits >> 4) & 1) & fg) ^ bg; + base[4] = (-((bits >> 3) & 1) & fg) ^ bg; + base[5] = (-((bits >> 2) & 1) & fg) ^ bg; + base[6] = (-((bits >> 1) & 1) & fg) ^ bg; + base[7] = (-(bits & 1) & fg) ^ bg; + base = (unsigned int *) ((char *)base + rb); + } +} + +static void draw_byte_16(unsigned char *font, unsigned int *base, int rb) +{ + int l, bits; + int fg = 0xFFFFFFFFUL; + int bg = 0x00000000UL; + unsigned long offset = reloc_offset(); + unsigned int *eb = PTRRELOC((int *)expand_bits_16); + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (eb[bits >> 6] & fg) ^ bg; + base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg; + base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg; + base[3] = (eb[bits & 3] & fg) ^ bg; + base = (unsigned int *) ((char *)base + rb); + } +} + +static void draw_byte_8(unsigned char *font, unsigned int *base, int rb) +{ + int l, bits; + int fg = 0x0F0F0F0FUL; + int bg = 0x00000000UL; + unsigned long offset = reloc_offset(); + unsigned int *eb = PTRRELOC((int *)expand_bits_8); + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (eb[bits >> 4] & fg) ^ bg; + base[1] = (eb[bits & 0xf] & fg) ^ bg; + base = (unsigned int *) ((char *)base + rb); + } +} + +static unsigned char vga_font[cmapsz] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, +0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, +0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, +0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, +0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, +0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, +0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, +0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, +0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, +0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, +0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, +0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e, +0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, +0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, +0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, +0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, +0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, +0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, +0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, +0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, +0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, +0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, +0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, +0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, +0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, +0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, +0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, +0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, +0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, +0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, +0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, +0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, +0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, +0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, +0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, +0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c, +0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, +0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, +0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, +0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, +0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, +0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, +0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, +0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, +0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, +0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, +0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, +0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, +0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, +0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, +0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, +0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60, +0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, +0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, +0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, +0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, +0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, +0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, +0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, +0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, +0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, +0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, +0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, +0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, +0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, +0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, +0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, +0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, +0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, +0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, +0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, +0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, +0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, +0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, +0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, +0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, +0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, +0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, +0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, +0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, +0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, +0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, +0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, +0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, +0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, +0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, +0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, +0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, +0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, +0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, +0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, +0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, +0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, +0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, +0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44, +0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, +0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, +0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, +0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, +0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, +0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, +0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, +0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, +0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, +0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, +0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, +0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, +0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, +0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, +0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, +0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, +0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, +0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, +0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, +0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, +0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, +0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, +0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +}; diff --git a/arch/ppc64/kernel/cpu_setup_power4.S b/arch/ppc64/kernel/cpu_setup_power4.S new file mode 100644 index 000000000000..afb071bff12e --- /dev/null +++ b/arch/ppc64/kernel/cpu_setup_power4.S @@ -0,0 +1,179 @@ +/* + * This file contains low level CPU setup functions. + * Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +_GLOBAL(__power4_cpu_preinit) + /* + * On the PPC970, we have to turn off real-mode cache inhibit + * early, before we first turn the MMU off. + */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi r0,0x39 + bnelr + + li r0,0 + sync + mtspr SPRN_HID4,r0 + isync + sync + mtspr SPRN_HID5,r0 + isync + + mfspr r0,SPRN_HID1 + li r11,0x1200 /* enable i-fetch cacheability */ + sldi r11,r11,44 /* and prefetch */ + or r0,r0,r11 + mtspr SPRN_HID1,r0 + mtspr SPRN_HID1,r0 + isync + li r0,0 + sync + mtspr SPRN_HIOR,0 /* Clear interrupt prefix */ + isync + blr + +_GLOBAL(__setup_cpu_power4) + blr + +_GLOBAL(__setup_cpu_ppc970) + mfspr r0,SPRN_HID0 + li r11,5 /* clear DOZE and SLEEP */ + rldimi r0,r11,52,8 /* set NAP and DPM */ + mtspr SPRN_HID0,r0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + sync + isync + blr + +/* Definitions for the table use to save CPU states */ +#define CS_HID0 0 +#define CS_HID1 8 +#define CS_HID4 16 +#define CS_HID5 24 +#define CS_SIZE 32 + + .data + .balign L1_CACHE_BYTES,0 +cpu_state_storage: + .space CS_SIZE + .balign L1_CACHE_BYTES,0 + .text + +/* Called in normal context to backup CPU 0 state. This + * does not include cache settings. This function is also + * called for machine sleep. This does not include the MMU + * setup, BATs, etc... but rather the "special" registers + * like HID0, HID1, HID4, etc... + */ +_GLOBAL(__save_cpu_setup) + /* Some CR fields are volatile, we back it up all */ + mfcr r7 + + /* Get storage ptr */ + LOADADDR(r5,cpu_state_storage) + + /* We only deal with 970 for now */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi r0,0x39 + bne 1f + + /* Save HID0,1,4 and 5 */ + mfspr r3,SPRN_HID0 + std r3,CS_HID0(r5) + mfspr r3,SPRN_HID1 + std r3,CS_HID1(r5) + mfspr r3,SPRN_HID4 + std r3,CS_HID4(r5) + mfspr r3,SPRN_HID5 + std r3,CS_HID5(r5) + +1: + mtcr r7 + blr + +/* Called with no MMU context (typically MSR:IR/DR off) to + * restore CPU state as backed up by the previous + * function. This does not include cache setting + */ +_GLOBAL(__restore_cpu_setup) + /* Get storage ptr (FIXME when using anton reloc as we + * are running with translation disabled here + */ + LOADADDR(r5,cpu_state_storage) + + /* We only deal with 970 for now */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi r0,0x39 + bne 1f + + /* Clear interrupt prefix */ + li r0,0 + sync + mtspr SPRN_HIOR,0 + isync + + /* Restore HID0 */ + ld r3,CS_HID0(r5) + sync + isync + mtspr SPRN_HID0,r3 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + sync + isync + + /* Restore HID1 */ + ld r3,CS_HID1(r5) + sync + isync + mtspr SPRN_HID1,r3 + mtspr SPRN_HID1,r3 + sync + isync + + /* Restore HID4 */ + ld r3,CS_HID4(r5) + sync + isync + mtspr SPRN_HID4,r3 + sync + isync + + /* Restore HID5 */ + ld r3,CS_HID5(r5) + sync + isync + mtspr SPRN_HID5,r3 + sync + isync +1: + blr + diff --git a/arch/ppc64/kernel/idle_power4.S b/arch/ppc64/kernel/idle_power4.S new file mode 100644 index 000000000000..fb983069dfa5 --- /dev/null +++ b/arch/ppc64/kernel/idle_power4.S @@ -0,0 +1,79 @@ +/* + * This file contains the power_save function for 6xx & 7xxx CPUs + * rewritten in assembler + * + * Warning ! This code assumes that if your machine has a 750fx + * it will have PLL 1 set to low speed mode (used during NAP/DOZE). + * if this is not the case some additional changes will have to + * be done to check a runtime var (a bit like powersave-nap) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + + .text + +/* + * Here is the power_save_6xx function. This could eventually be + * split into several functions & changing the function pointer + * depending on the various features. + */ +_GLOBAL(power4_idle) +BEGIN_FTR_SECTION + blr +END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) + /* We must dynamically check for the NAP feature as it + * can be cleared by CPU init after the fixups are done + */ + LOADBASE(r3,cur_cpu_spec) + ld r4,cur_cpu_spec@l(r3) + ld r4,CPU_SPEC_FEATURES(r4) + andi. r0,r4,CPU_FTR_CAN_NAP + beqlr + /* Now check if user or arch enabled NAP mode */ + LOADBASE(r3,powersave_nap) + lwz r4,powersave_nap@l(r3) + cmpi 0,r4,0 + beqlr + + /* Clear MSR:EE */ + mfmsr r7 + li r4,0 + ori r4,r4,MSR_EE + andc r0,r7,r4 + mtmsrd r0 + + /* Check current_thread_info()->flags */ + clrrdi r4,r1,THREAD_SHIFT + ld r4,TI_FLAGS(r4) + andi. r0,r4,_TIF_NEED_RESCHED + beq 1f + mtmsrd r7 /* out of line this ? */ + blr +1: + /* Go to NAP now */ +BEGIN_FTR_SECTION + DSSALL + sync +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + oris r7,r7,MSR_POW@h + sync + isync + mtmsrd r7 + isync + sync + blr + diff --git a/arch/ppc64/kernel/of_device.c b/arch/ppc64/kernel/of_device.c new file mode 100644 index 000000000000..b27a75f1b986 --- /dev/null +++ b/arch/ppc64/kernel/of_device.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include +#include +#include +#include + +/** + * of_match_device - Tell if an of_device structure has a matching + * of_match structure + * @ids: array of of device match structures to search in + * @dev: the of device structure to match against + * + * Used by a driver to check whether an of_device present in the + * system is in its list of supported devices. + */ +const struct of_match * of_match_device(const struct of_match *matches, + const struct of_device *dev) +{ + if (!dev->node) + return NULL; + while (matches->name || matches->type || matches->compatible) { + int match = 1; + if (matches->name && matches->name != OF_ANY_MATCH) + match &= dev->node->name + && !strcmp(matches->name, dev->node->name); + if (matches->type && matches->type != OF_ANY_MATCH) + match &= dev->node->type + && !strcmp(matches->type, dev->node->type); + if (matches->compatible && matches->compatible != OF_ANY_MATCH) + match &= device_is_compatible(dev->node, + matches->compatible); + if (match) + return matches; + matches++; + } + return NULL; +} + +static int of_platform_bus_match(struct device *dev, struct device_driver *drv) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * of_drv = to_of_platform_driver(drv); + const struct of_match * matches = of_drv->match_table; + + if (!matches) + return 0; + + return of_match_device(matches, of_dev) != NULL; +} + +struct of_device *of_dev_get(struct of_device *dev) +{ + struct device *tmp; + + if (!dev) + return NULL; + tmp = get_device(&dev->dev); + if (tmp) + return to_of_device(tmp); + else + return NULL; +} + +void of_dev_put(struct of_device *dev) +{ + if (dev) + put_device(&dev->dev); +} + + +static int of_device_probe(struct device *dev) +{ + int error = -ENODEV; + struct of_platform_driver *drv; + struct of_device *of_dev; + const struct of_match *match; + + drv = to_of_platform_driver(dev->driver); + of_dev = to_of_device(dev); + + if (!drv->probe) + return error; + + of_dev_get(of_dev); + + match = of_match_device(drv->match_table, of_dev); + if (match) + error = drv->probe(of_dev, match); + if (error) + of_dev_put(of_dev); + + return error; +} + +static int of_device_remove(struct device *dev) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + + if (dev->driver && drv->remove) + drv->remove(of_dev); + return 0; +} + +static int of_device_suspend(struct device *dev, u32 state) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + int error = 0; + + if (dev->driver && drv->suspend) + error = drv->suspend(of_dev, state); + return error; +} + +static int of_device_resume(struct device * dev) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + int error = 0; + + if (dev->driver && drv->resume) + error = drv->resume(of_dev); + return error; +} + +struct bus_type of_platform_bus_type = { + .name = "of_platform", + .match = of_platform_bus_match, + .suspend = of_device_suspend, + .resume = of_device_resume, +}; + +static int __init of_bus_driver_init(void) +{ + return bus_register(&of_platform_bus_type); +} + +postcore_initcall(of_bus_driver_init); + +int of_register_driver(struct of_platform_driver *drv) +{ + int count = 0; + + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &of_platform_bus_type; + drv->driver.probe = of_device_probe; + drv->driver.remove = of_device_remove; + + /* register with core */ + count = driver_register(&drv->driver); + return count ? count : 1; +} + +void of_unregister_driver(struct of_platform_driver *drv) +{ + driver_unregister(&drv->driver); +} + + +static ssize_t dev_show_devspec(struct device *dev, char *buf) +{ + struct of_device *ofdev; + + ofdev = to_of_device(dev); + return sprintf(buf, "%s", ofdev->node->full_name); +} + +static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL); + +/** + * of_release_dev - free an of device structure when all users of it are finished. + * @dev: device that's been disconnected + * + * Will be called only by the device core when all users of this of device are + * done. + */ +void of_release_dev(struct device *dev) +{ + struct of_device *ofdev; + + ofdev = to_of_device(dev); + kfree(ofdev); +} + +int of_device_register(struct of_device *ofdev) +{ + int rc; + struct of_device **odprop; + + BUG_ON(ofdev->node == NULL); + + odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL); + if (!odprop) { + struct property *new_prop; + + new_prop = kmalloc(sizeof(struct property) + sizeof(struct of_device *), + GFP_KERNEL); + if (new_prop == NULL) + return -ENOMEM; + new_prop->name = "linux,device"; + new_prop->length = sizeof(sizeof(struct of_device *)); + new_prop->value = (unsigned char *)&new_prop[1]; + odprop = (struct of_device **)new_prop->value; + *odprop = NULL; + prom_add_property(ofdev->node, new_prop); + } + *odprop = ofdev; + + rc = device_register(&ofdev->dev); + if (rc) + return rc; + + device_create_file(&ofdev->dev, &dev_attr_devspec); + + return 0; +} + +void of_device_unregister(struct of_device *ofdev) +{ + struct of_device **odprop; + + device_remove_file(&ofdev->dev, &dev_attr_devspec); + + odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL); + if (odprop) + *odprop = NULL; + + device_unregister(&ofdev->dev); +} + +struct of_device* of_platform_device_create(struct device_node *np, const char *bus_id) +{ + struct of_device *dev; + u32 *reg; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + memset(dev, 0, sizeof(*dev)); + + dev->node = np; + dev->dma_mask = 0xffffffffUL; + dev->dev.dma_mask = &dev->dma_mask; + dev->dev.parent = NULL; + dev->dev.bus = &of_platform_bus_type; + dev->dev.release = of_release_dev; + + reg = (u32 *)get_property(np, "reg", NULL); + strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE); + + if (of_device_register(dev) != 0) { + kfree(dev); + return NULL; + } + + return dev; +} + +EXPORT_SYMBOL(of_match_device); +EXPORT_SYMBOL(of_platform_bus_type); +EXPORT_SYMBOL(of_register_driver); +EXPORT_SYMBOL(of_unregister_driver); +EXPORT_SYMBOL(of_device_register); +EXPORT_SYMBOL(of_device_unregister); +EXPORT_SYMBOL(of_dev_get); +EXPORT_SYMBOL(of_dev_put); +EXPORT_SYMBOL(of_platform_device_create); +EXPORT_SYMBOL(of_release_dev); diff --git a/arch/ppc64/kernel/open_pic_u3.c b/arch/ppc64/kernel/open_pic_u3.c new file mode 100644 index 000000000000..4f9832a3b07d --- /dev/null +++ b/arch/ppc64/kernel/open_pic_u3.c @@ -0,0 +1,348 @@ +/* + * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "open_pic.h" +#include "open_pic_defs.h" + +void* OpenPIC2_Addr; +static volatile struct OpenPIC *OpenPIC2 = NULL; + +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; + +static u_int NumSources; +static int NumISUs; +static int open_pic2_irq_offset; + +static OpenPIC_SourcePtr ISU2[OPENPIC_MAX_ISU]; + +unsigned int openpic2_vec_spurious; + +/* + * Accesses to the current processor's openpic registers + * U3 secondary openpic has only one output + */ +#define THIS_CPU Processor[0] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU + +#define GET_ISU(source) ISU2[(source) >> 4][(source) & 0xf] + +static inline u_int openpic2_read(volatile u_int *addr) +{ + u_int val; + + val = in_be32(addr); + return val; +} + +static inline void openpic2_write(volatile u_int *addr, u_int val) +{ + out_be32(addr, val); +} + +static inline u_int openpic2_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic2_read(addr); + return val & mask; +} + +static inline void openpic2_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic2_read(addr); + openpic2_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic2_clearfield(volatile u_int *addr, u_int mask) +{ + openpic2_writefield(addr, mask, 0); +} + +static inline void openpic2_setfield(volatile u_int *addr, u_int mask) +{ + openpic2_writefield(addr, mask, mask); +} + +static void openpic2_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + unsigned int loops = 100000; + + openpic2_setfield(addr, OPENPIC_MASK); + while (openpic2_read(addr) & OPENPIC_ACTIVITY) { + if (!loops--) { + printk(KERN_ERR "openpic2_safe_writefield timeout\n"); + break; + } + } + openpic2_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + + +static inline void openpic2_reset(void) +{ + openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); +} + +static void openpic2_disable_8259_pass_through(void) +{ + openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + * Find out the current interrupt + */ +static u_int openpic2_irq(void) +{ + u_int vec; + DECL_THIS_CPU; + CHECK_THIS_CPU; + vec = openpic2_readfield(&OpenPIC2->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} + +static void openpic2_eoi(void) +{ + DECL_THIS_CPU; + CHECK_THIS_CPU; + openpic2_write(&OpenPIC2->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic2_read(&OpenPIC2->THIS_CPU.EOI); +} + + +static inline u_int openpic2_get_priority(void) +{ + DECL_THIS_CPU; + CHECK_THIS_CPU; + return openpic2_readfield(&OpenPIC2->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} + +static void openpic2_set_priority(u_int pri) +{ + DECL_THIS_CPU; + CHECK_THIS_CPU; + openpic2_writefield(&OpenPIC2->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + * Get/set the spurious vector + */ +static inline u_int openpic2_get_spurious(void) +{ + return openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} + +static void openpic2_set_spurious(u_int vec) +{ + openpic2_writefield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +/* + * Enable/disable an external interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic2_enable_irq(u_int irq) +{ + unsigned int loops = 100000; + + openpic2_clearfield(&GET_ISU(irq - open_pic2_irq_offset).Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + if (!loops--) { + printk(KERN_ERR "openpic_enable_irq timeout\n"); + break; + } + + mb(); /* sync is probably useless here */ + } while(openpic2_readfield(&GET_ISU(irq - open_pic2_irq_offset).Vector_Priority, + OPENPIC_MASK)); +} + +static void openpic2_disable_irq(u_int irq) +{ + u32 vp; + unsigned int loops = 100000; + + openpic2_setfield(&GET_ISU(irq - open_pic2_irq_offset).Vector_Priority, + OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + if (!loops--) { + printk(KERN_ERR "openpic_disable_irq timeout\n"); + break; + } + + mb(); /* sync is probably useless here */ + vp = openpic2_readfield(&GET_ISU(irq - open_pic2_irq_offset).Vector_Priority, + OPENPIC_MASK | OPENPIC_ACTIVITY); + } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ +static void openpic2_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + openpic2_safe_writefield(&GET_ISU(irq).Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +static void openpic2_mapirq(u_int irq, u_int physmask) +{ + openpic2_write(&GET_ISU(irq).Destination, physmask); +} + +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +static inline void openpic2_set_sense(u_int irq, int sense) +{ + openpic2_safe_writefield(&GET_ISU(irq).Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + +static void openpic2_end_irq(unsigned int irq_nr) +{ + openpic2_eoi(); +} + +int openpic2_get_irq(struct pt_regs *regs) +{ + int irq = openpic2_irq(); + + if (irq == openpic2_vec_spurious) + return -1; + return irq + open_pic2_irq_offset; +} + +struct hw_interrupt_type open_pic2 = { + " OpenPIC2 ", + NULL, + NULL, + openpic2_enable_irq, + openpic2_disable_irq, + NULL, + openpic2_end_irq, +}; + +void __init openpic2_init(int offset) +{ + u_int t, i; + const char *version; + + if (!OpenPIC2_Addr) { + printk(KERN_INFO "No OpenPIC2 found !\n"); + return; + } + OpenPIC2 = (volatile struct OpenPIC *)OpenPIC2_Addr; + + ppc64_boot_msg(0x20, "OpenPic U3 Init"); + + t = openpic2_read(&OpenPIC2->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + printk(KERN_INFO "OpenPIC (U3) Version %s\n", version); + + open_pic2_irq_offset = offset; + + for (i=0; i<128; i+=0x10) { + ISU2[i>>4] = &((struct OpenPIC *)OpenPIC2_Addr)->Source[i]; + NumISUs++; + } + NumSources = NumISUs * 0x10; + openpic2_vec_spurious = NumSources; + + openpic2_set_priority(0xf); + + /* Init all external sources */ + for (i = 0; i < NumSources; i++) { + int pri, sense; + + /* the bootloader may have left it enabled (bad !) */ + openpic2_disable_irq(i+offset); + + pri = 8; + sense = (i < OpenPIC_NumInitSenses) ? OpenPIC_InitSenses[i]: 1; + if (sense) + irq_desc[i+offset].status = IRQ_LEVEL; + + /* Enabled, Priority 8 or 9 */ + openpic2_initirq(i, pri, i, !sense, sense); + /* Processor 0 */ + openpic2_mapirq(i, 0x1); + } + + /* Init descriptors */ + for (i = offset; i < NumSources + offset; i++) + irq_desc[i].handler = &open_pic2; + + /* Initialize the spurious interrupt */ + openpic2_set_spurious(openpic2_vec_spurious); + + openpic2_set_priority(0); + openpic2_disable_8259_pass_through(); + + ppc64_boot_msg(0x25, "OpenPic2 Done"); +} diff --git a/arch/ppc64/kernel/pSeries_nvram.c b/arch/ppc64/kernel/pSeries_nvram.c new file mode 100644 index 000000000000..ffeadf1c96b2 --- /dev/null +++ b/arch/ppc64/kernel/pSeries_nvram.c @@ -0,0 +1,150 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * /dev/nvram driver for PPC64 + * + * This perhaps should live in drivers/char + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int nvram_size; +static unsigned int nvram_fetch, nvram_store; +static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ +static spinlock_t nvram_lock = SPIN_LOCK_UNLOCKED; + + +static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) +{ + unsigned int i; + unsigned long len, done; + unsigned long flags; + char *p = buf; + + if (nvram_size == 0 || nvram_fetch) + return -ENODEV; + + if (*index >= nvram_size) + return 0; + + i = *index; + if (i + count > nvram_size) + count = nvram_size - i; + + spin_lock_irqsave(&nvram_lock, flags); + + for (; count != 0; count -= len) { + len = count; + if (len > NVRW_CNT) + len = NVRW_CNT; + + if ((rtas_call(nvram_fetch, 3, 2, &done, i, __pa(nvram_buf), + len) != 0) || len != done) { + spin_unlock_irqrestore(&nvram_lock, flags); + return -EIO; + } + + memcpy(p, nvram_buf, len); + + p += len; + i += len; + } + + spin_unlock_irqrestore(&nvram_lock, flags); + + *index = i; + return p - buf; +} + +static ssize_t pSeries_nvram_write(char *buf, size_t count, loff_t *index) +{ + unsigned int i; + unsigned long len, done; + unsigned long flags; + const char *p = buf; + + if (nvram_size == 0 || nvram_store) + return -ENODEV; + + if (*index >= nvram_size) + return 0; + + i = *index; + if (i + count > nvram_size) + count = nvram_size - i; + + spin_lock_irqsave(&nvram_lock, flags); + + for (; count != 0; count -= len) { + len = count; + if (len > NVRW_CNT) + len = NVRW_CNT; + + memcpy(nvram_buf, p, len); + + if ((rtas_call(nvram_store, 3, 2, &done, i, __pa(nvram_buf), + len) != 0) || len != done) { + spin_unlock_irqrestore(&nvram_lock, flags); + return -EIO; + } + + p += len; + i += len; + } + spin_unlock_irqrestore(&nvram_lock, flags); + + *index = i; + return p - buf; +} + +static ssize_t pSeries_nvram_get_size(void) +{ + return nvram_size ? nvram_size : -ENODEV; +} + +int __init pSeries_nvram_init(void) +{ + struct device_node *nvram; + unsigned int *nbytes_p, proplen; + + nvram = of_find_node_by_type(NULL, "nvram"); + if (nvram == NULL) + return -ENODEV; + + nbytes_p = (unsigned int *)get_property(nvram, "#bytes", &proplen); + if (nbytes_p == NULL || proplen != sizeof(unsigned int)) + return -EIO; + + nvram_size = *nbytes_p; + + nvram_fetch = rtas_token("nvram-fetch"); + nvram_store = rtas_token("nvram-store"); + printk(KERN_INFO "PPC64 nvram contains %d bytes\n", nvram_size); + of_node_put(nvram); + + ppc_md.nvram_read = pSeries_nvram_read; + ppc_md.nvram_write = pSeries_nvram_write; + ppc_md.nvram_size = pSeries_nvram_get_size; + + return 0; +} diff --git a/arch/ppc64/kernel/pci_dma_direct.c b/arch/ppc64/kernel/pci_dma_direct.c new file mode 100644 index 000000000000..d978b6523398 --- /dev/null +++ b/arch/ppc64/kernel/pci_dma_direct.c @@ -0,0 +1,88 @@ +/* + * Support for DMA from PCI devices to main memory on + * machines without an iommu or with directly addressable + * RAM (typically a pmac with 2Gb of RAM or less) + * + * Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +static void *pci_direct_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + + ret = (void *)__get_free_pages(GFP_ATOMIC, get_order(size)); + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_absolute((unsigned long)ret); + } + return ret; +} + +static void pci_direct_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} + +static dma_addr_t pci_direct_map_single(struct pci_dev *hwdev, void *ptr, + size_t size, int direction) +{ + return virt_to_absolute((unsigned long)ptr); +} + +static void pci_direct_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ +} + +static int pci_direct_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + int i; + + for (i = 0; i < nents; i++, sg++) { + sg->dma_address = page_to_phys(sg->page) + sg->offset; + sg->dma_length = sg->length; + } + + return nents; +} + +static void pci_direct_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ +} + +void __init pci_dma_init_direct(void) +{ + pci_dma_ops.pci_alloc_consistent = pci_direct_alloc_consistent; + pci_dma_ops.pci_free_consistent = pci_direct_free_consistent; + pci_dma_ops.pci_map_single = pci_direct_map_single; + pci_dma_ops.pci_unmap_single = pci_direct_unmap_single; + pci_dma_ops.pci_map_sg = pci_direct_map_sg; + pci_dma_ops.pci_unmap_sg = pci_direct_unmap_sg; +} diff --git a/arch/ppc64/kernel/pmac.h b/arch/ppc64/kernel/pmac.h new file mode 100644 index 000000000000..4d69b3abc9fe --- /dev/null +++ b/arch/ppc64/kernel/pmac.h @@ -0,0 +1,32 @@ +#ifndef __PMAC_H__ +#define __PMAC_H__ + +#include +#include + +/* + * Declaration for the various functions exported by the + * pmac_* files. Mostly for use by pmac_setup + */ + +extern void pmac_get_boot_time(struct rtc_time *tm); +extern void pmac_get_rtc_time(struct rtc_time *tm); +extern int pmac_set_rtc_time(struct rtc_time *tm); +extern void pmac_read_rtc_time(void); +extern void pmac_calibrate_decr(void); + +extern void pmac_pcibios_fixup(void); +extern void pmac_pci_init(void); +extern void pmac_setup_pci_dma(void); +extern void fixup_k2_sata(struct pci_dev* dev); +extern void pmac_check_ht_link(void); + +extern void pmac_setup_smp(void); + +extern unsigned long pmac_ide_get_base(int index); +extern void pmac_ide_init_hwif_ports(hw_regs_t *hw, + unsigned long data_port, unsigned long ctrl_port, int *irq); + +extern void pmac_nvram_init(void); + +#endif /* __PMAC_H__ */ diff --git a/arch/ppc64/kernel/pmac_feature.c b/arch/ppc64/kernel/pmac_feature.c new file mode 100644 index 000000000000..a47d2bfe3fda --- /dev/null +++ b/arch/ppc64/kernel/pmac_feature.c @@ -0,0 +1,654 @@ +/* + * arch/ppc/platforms/pmac_feature.c + * + * Copyright (C) 1996-2001 Paul Mackerras (paulus@cs.anu.edu.au) + * Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * TODO: + * + * - Replace mdelay with some schedule loop if possible + * - Shorten some obfuscated delays on some routines (like modem + * power) + * - Refcount some clocks (see darwin) + * - Split split split... + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG_FEATURE + +#ifdef DEBUG_FEATURE +#define DBG(fmt,...) printk(KERN_DEBUG fmt) +#else +#define DBG(fmt,...) +#endif + +/* + * We use a single global lock to protect accesses. Each driver has + * to take care of its own locking + */ +static spinlock_t feature_lock __pmacdata = SPIN_LOCK_UNLOCKED; + +#define LOCK(flags) spin_lock_irqsave(&feature_lock, flags); +#define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags); + + +/* + * Instance of some macio stuffs + */ +struct macio_chip macio_chips[MAX_MACIO_CHIPS] __pmacdata; + +struct macio_chip* __pmac +macio_find(struct device_node* child, int type) +{ + while(child) { + int i; + + for (i=0; i < MAX_MACIO_CHIPS && macio_chips[i].of_node; i++) + if (child == macio_chips[i].of_node && + (!type || macio_chips[i].type == type)) + return &macio_chips[i]; + child = child->parent; + } + return NULL; +} + +static const char* macio_names[] __pmacdata = +{ + "Unknown", + "Grand Central", + "OHare", + "OHareII", + "Heathrow", + "Gatwick", + "Paddington", + "Keylargo", + "Pangea", + "Intrepid", + "K2" +}; + + + +/* + * Uninorth reg. access. Note that Uni-N regs are big endian + */ + +#define UN_REG(r) (uninorth_base + ((r) >> 2)) +#define UN_IN(r) (in_be32(UN_REG(r))) +#define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) +#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) +#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) + +static struct device_node* uninorth_node __pmacdata; +static u32* uninorth_base __pmacdata; +static u32 uninorth_rev __pmacdata; +static void *u3_ht; + +extern struct pci_dev *k2_skiplist[2]; + +/* + * For each motherboard family, we have a table of functions pointers + * that handle the various features. + */ + +typedef long (*feature_call)(struct device_node* node, long param, long value); + +struct feature_table_entry { + unsigned int selector; + feature_call function; +}; + +struct pmac_mb_def +{ + const char* model_string; + const char* model_name; + int model_id; + struct feature_table_entry* features; + unsigned long board_flags; +}; +static struct pmac_mb_def pmac_mb __pmacdata; + +/* + * Here are the chip specific feature functions + */ + + +static long __pmac g5_read_gpio(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + + return MACIO_IN8(param); +} + + +static long __pmac g5_write_gpio(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + + MACIO_OUT8(param, (u8)(value & 0xff)); + return 0; +} + +static long __pmac g5_gmac_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + unsigned long flags; + struct pci_dev *pdev = NULL; + + if (node == NULL) + return -ENODEV; + + /* XXX FIXME: We should fix pci_device_from_OF_node here, and + * get to a real pci_dev or we'll get into trouble with PCI + * domains the day we get overlapping numbers (like if we ever + * decide to show the HT root. + * Note that we only get the slot when value is 0. This is called + * early during boot with value 1 to enable all devices, at which + * point, we don't yet have probed pci_find_slot, so it would fail + * to look for the slot at this point. + */ + if (!value) + pdev = pci_find_slot(node->busno, node->devfn); + + LOCK(flags); + if (value) { + MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); + mb(); + k2_skiplist[0] = NULL; + } else { + k2_skiplist[0] = pdev; + mb(); + MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); + } + + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac g5_fw_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + unsigned long flags; + struct pci_dev *pdev = NULL; + + /* XXX FIXME: We should fix pci_device_from_OF_node here, and + * get to a real pci_dev or we'll get into trouble with PCI + * domains the day we get overlapping numbers (like if we ever + * decide to show the HT root + * Note that we only get the slot when value is 0. This is called + * early during boot with value 1 to enable all devices, at which + * point, we don't yet have probed pci_find_slot, so it would fail + * to look for the slot at this point. + */ + if (node == NULL) + return -ENODEV; + + if (!value) + pdev = pci_find_slot(node->busno, node->devfn); + + LOCK(flags); + if (value) { + MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); + mb(); + k2_skiplist[1] = NULL; + } else { + k2_skiplist[0] = pdev; + mb(); + MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); + } + + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac g5_mpic_enable(struct device_node* node, long param, long value) +{ + unsigned long flags; + + if (node->parent == NULL || strcmp(node->parent->name, "u3")) + return 0; + + LOCK(flags); + UN_BIS(U3_TOGGLE_REG, U3_MPIC_RESET | U3_MPIC_OUTPUT_ENABLE); + UNLOCK(flags); + + return 0; +} + +#ifdef CONFIG_SMP +static long __pmac g5_reset_cpu(struct device_node* node, long param, long value) +{ + unsigned int reset_io = 0; + unsigned long flags; + struct macio_chip* macio; + struct device_node* np; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo2) + return -ENODEV; + + np = find_path_device("/cpus"); + if (np == NULL) + return -ENODEV; + for (np = np->child; np != NULL; np = np->sibling) { + u32* num = (u32 *)get_property(np, "reg", NULL); + u32* rst = (u32 *)get_property(np, "soft-reset", NULL); + if (num == NULL || rst == NULL) + continue; + if (param == *num) { + reset_io = *rst; + break; + } + } + if (np == NULL || reset_io == 0) + return -ENODEV; + + LOCK(flags); + MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(reset_io); + udelay(1); + MACIO_OUT8(reset_io, 0); + (void)MACIO_IN8(reset_io); + UNLOCK(flags); + + return 0; +} +#endif /* CONFIG_SMP */ + +/* + * This can be called from pmac_smp so isn't static + * + * This takes the second CPU off the bus on dual CPU machines + * running UP + */ +void __pmac g5_phy_disable_cpu1(void) +{ + UN_OUT(U3_API_PHY_CONFIG_1, 0); +} + +static long __pmac generic_get_mb_info(struct device_node* node, long param, long value) +{ + switch(param) { + case PMAC_MB_INFO_MODEL: + return pmac_mb.model_id; + case PMAC_MB_INFO_FLAGS: + return pmac_mb.board_flags; + case PMAC_MB_INFO_NAME: + /* hack hack hack... but should work */ + *((const char **)value) = pmac_mb.model_name; + return 0; + } + return -EINVAL; +} + + +/* + * Table definitions + */ + +/* Used on any machine + */ +static struct feature_table_entry any_features[] __pmacdata = { + { PMAC_FTR_GET_MB_INFO, generic_get_mb_info }, + { 0, NULL } +}; + +/* G5 features + */ +static struct feature_table_entry g5_features[] __pmacdata = { + { PMAC_FTR_GMAC_ENABLE, g5_gmac_enable }, + { PMAC_FTR_1394_ENABLE, g5_fw_enable }, + { PMAC_FTR_ENABLE_MPIC, g5_mpic_enable }, + { PMAC_FTR_READ_GPIO, g5_read_gpio }, + { PMAC_FTR_WRITE_GPIO, g5_write_gpio }, +#ifdef CONFIG_SMP + { PMAC_FTR_RESET_CPU, g5_reset_cpu }, +#endif /* CONFIG_SMP */ + { 0, NULL } +}; + +static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { + { "PowerMac7,2", "PowerMac G5", + PMAC_TYPE_POWERMAC_G5, g5_features, + 0, + }, +}; + +/* + * The toplevel feature_call callback + */ +long __pmac pmac_do_feature_call(unsigned int selector, ...) +{ + struct device_node* node; + long param, value; + int i; + feature_call func = NULL; + va_list args; + + if (pmac_mb.features) + for (i=0; pmac_mb.features[i].function; i++) + if (pmac_mb.features[i].selector == selector) { + func = pmac_mb.features[i].function; + break; + } + if (!func) + for (i=0; any_features[i].function; i++) + if (any_features[i].selector == selector) { + func = any_features[i].function; + break; + } + if (!func) + return -ENODEV; + + va_start(args, selector); + node = (struct device_node*)va_arg(args, void*); + param = va_arg(args, long); + value = va_arg(args, long); + va_end(args); + + return func(node, param, value); +} + +static int __init probe_motherboard(void) +{ + int i; + struct macio_chip* macio = &macio_chips[0]; + const char* model = NULL; + struct device_node *dt; + + /* Lookup known motherboard type in device-tree. First try an + * exact match on the "model" property, then try a "compatible" + * match is none is found. + */ + dt = find_devices("device-tree"); + if (dt != NULL) + model = (const char *) get_property(dt, "model", NULL); + for(i=0; model && i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) { + if (strcmp(model, pmac_mb_defs[i].model_string) == 0) { + pmac_mb = pmac_mb_defs[i]; + goto found; + } + } + for(i=0; i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) { + if (machine_is_compatible(pmac_mb_defs[i].model_string)) { + pmac_mb = pmac_mb_defs[i]; + goto found; + } + } + + /* Fallback to selection depending on mac-io chip type */ + switch(macio->type) { + case macio_keylargo2: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_K2; + pmac_mb.model_name = "Unknown K2-based"; + pmac_mb.features = g5_features; + + default: + return -ENODEV; + } +found: + /* Check for "mobile" machine */ + if (model && (strncmp(model, "PowerBook", 9) == 0 + || strncmp(model, "iBook", 5) == 0)) + pmac_mb.board_flags |= PMAC_MB_MOBILE; + + + printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name); + return 0; +} + +/* Initialize the Core99 UniNorth host bridge and memory controller + */ +static void __init probe_uninorth(void) +{ + uninorth_node = of_find_node_by_name(NULL, "u3"); + if (uninorth_node && uninorth_node->n_addrs > 0) { + /* Small hack until I figure out if parsing in prom.c is correct. I should + * get rid of those pre-parsed junk anyway + */ + unsigned long address = uninorth_node->addrs[0].address; + uninorth_base = ioremap(address, 0x40000); + uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); + u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); + } else + uninorth_node = NULL; + + if (!uninorth_node) + return; + + printk(KERN_INFO "Found U3 memory controller & host bridge, revision: %d\n", + uninorth_rev); + printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base); + +} + +static void __init probe_one_macio(const char* name, const char* compat, int type) +{ + struct device_node* node; + int i; + volatile u32* base; + u32* revp; + + node = find_devices(name); + if (!node || !node->n_addrs) + return; + if (compat) + do { + if (device_is_compatible(node, compat)) + break; + node = node->next; + } while (node); + if (!node) + return; + for(i=0; i= MAX_MACIO_CHIPS) { + printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n"); + printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name); + return; + } + base = (volatile u32*)ioremap(node->addrs[0].address, node->addrs[0].size); + if (!base) { + printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n"); + return; + } + if (type == macio_keylargo) { + u32* did = (u32 *)get_property(node, "device-id", NULL); + if (*did == 0x00000025) + type = macio_pangea; + if (*did == 0x0000003e) + type = macio_intrepid; + } + macio_chips[i].of_node = node; + macio_chips[i].type = type; + macio_chips[i].base = base; + macio_chips[i].flags = MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON; + macio_chips[i].name = macio_names[type]; + revp = (u32 *)get_property(node, "revision-id", NULL); + if (revp) + macio_chips[i].rev = *revp; + printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n", + macio_names[type], macio_chips[i].rev, macio_chips[i].base); +} + +static int __init +probe_macios(void) +{ + probe_one_macio("mac-io", "K2-Keylargo", macio_keylargo2); + + macio_chips[0].lbus.index = 0; + macio_chips[1].lbus.index = 1; + + return (macio_chips[0].of_node == NULL) ? -ENODEV : 0; +} + +static void __init +set_initial_features(void) +{ + struct device_node *np; + + if (macio_chips[0].type == macio_keylargo2) { +#ifndef CONFIG_SMP + /* On SMP machines running UP, we have the second CPU eating + * bus cycles. We need to take it off the bus. This is done + * from pmac_smp for SMP kernels running on one CPU + */ + np = of_find_node_by_type(NULL, "cpu"); + if (np != NULL) + np = of_find_node_by_type(np, "cpu"); + if (np != NULL) { + g5_phy_disable_cpu1(); + of_node_put(np); + } +#endif /* CONFIG_SMP */ + /* Enable GMAC for now for PCI probing. It will be disabled + * later on after PCI probe + */ + np = of_find_node_by_name(NULL, "ethernet"); + while(np) { + if (device_is_compatible(np, "K2-GMAC")) + g5_gmac_enable(np, 0, 1); + np = of_find_node_by_name(np, "ethernet"); + } + + /* Enable FW before PCI probe. Will be disabled later on + * Note: We should have a batter way to check that we are + * dealing with uninorth internal cell and not a PCI cell + * on the external PCI. The code below works though. + */ + np = of_find_node_by_name(NULL, "firewire"); + while(np) { + if (device_is_compatible(np, "pci106b,5811")) { + macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; + g5_fw_enable(np, 0, 1); + } + np = of_find_node_by_name(np, "firewire"); + } + } +} + +void __init +pmac_feature_init(void) +{ + /* Detect the UniNorth memory controller */ + probe_uninorth(); + + /* Probe mac-io controllers */ + if (probe_macios()) { + printk(KERN_WARNING "No mac-io chip found\n"); + return; + } + + /* Setup low-level i2c stuffs */ + pmac_init_low_i2c(); + + /* Probe machine type */ + if (probe_motherboard()) + printk(KERN_WARNING "Unknown PowerMac !\n"); + + /* Set some initial features (turn off some chips that will + * be later turned on) + */ + set_initial_features(); +} + +int __init pmac_feature_late_init(void) +{ +#if 0 + struct device_node* np; + + /* Request some resources late */ + if (uninorth_node) + request_OF_resource(uninorth_node, 0, NULL); + np = find_devices("hammerhead"); + if (np) + request_OF_resource(np, 0, NULL); + np = find_devices("interrupt-controller"); + if (np) + request_OF_resource(np, 0, NULL); +#endif + return 0; +} + +device_initcall(pmac_feature_late_init); + + +static void dump_HT_speeds(char *name, u32 cfg, u32 frq) +{ + int freqs[16] = { 200,300,400,500,600,800,1000,0,0,0,0,0,0,0,0,0 }; + int bits[8] = { 8,16,0,32,2,4,0,0 }; + int freq = (frq >> 8) & 0xf; + + if (freqs[freq] == 0) + printk("%s: Unknown HT link frequency %x\n", name, freq); + else + printk("%s: %d MHz on main link, (%d in / %d out) bits width\n", + name, freqs[freq], + bits[(cfg >> 28) & 0x7], bits[(cfg >> 24) & 0x7]); +} + +void __init pmac_check_ht_link(void) +{ +#if 0 /* Disabled for now */ + u32 ufreq, freq, ucfg, cfg; + struct device_node *pcix_node; + u8 px_bus, px_devfn; + struct pci_controller *px_hose; + + (void)in_be32(u3_ht + U3_HT_LINK_COMMAND); + ucfg = cfg = in_be32(u3_ht + U3_HT_LINK_CONFIG); + ufreq = freq = in_be32(u3_ht + U3_HT_LINK_FREQ); + dump_HT_speeds("U3 HyperTransport", cfg, freq); + + pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); + if (pcix_node == NULL) { + printk("No PCI-X bridge found\n"); + return; + } + px_hose = pcix_node->phb; + px_bus = pcix_node->busno; + px_devfn = pcix_node->devfn; + + early_read_config_dword(px_hose, px_bus, px_devfn, 0xc4, &cfg); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xcc, &freq); + dump_HT_speeds("PCI-X HT Uplink", cfg, freq); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xc8, &cfg); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xd0, &freq); + dump_HT_speeds("PCI-X HT Downlink", cfg, freq); +#endif +} diff --git a/arch/ppc64/kernel/pmac_low_i2c.c b/arch/ppc64/kernel/pmac_low_i2c.c new file mode 100644 index 000000000000..d07579f2b8b9 --- /dev/null +++ b/arch/ppc64/kernel/pmac_low_i2c.c @@ -0,0 +1,513 @@ +/* + * arch/ppc/platforms/pmac_low_i2c.c + * + * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This file contains some low-level i2c access routines that + * need to be used by various bits of the PowerMac platform code + * at times where the real asynchronous & interrupt driven driver + * cannot be used. The API borrows some semantics from the darwin + * driver in order to ease the implementation of the platform + * properties parser + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_LOW_I2C_HOST 4 + +#if 1 +#define DBG(x...) do {\ + printk(KERN_DEBUG "KW:" x); \ + } while(0) +#else +#define DBGG(x...) +#endif + +struct low_i2c_host; + +typedef int (*low_i2c_func_t)(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len); + +struct low_i2c_host +{ + struct device_node *np; /* OF device node */ + struct semaphore mutex; /* Access mutex for use by i2c-keywest */ + low_i2c_func_t func; /* Access function */ + int is_open : 1; /* Poor man's access control */ + int mode; /* Current mode */ + int channel; /* Current channel */ + int num_channels; /* Number of channels */ + unsigned long base; /* For keywest-i2c, base address */ + int bsteps; /* And register stepping */ + int speed; /* And speed */ +}; + +static struct low_i2c_host low_i2c_hosts[MAX_LOW_I2C_HOST]; + +/* No locking is necessary on allocation, we are running way before + * anything can race with us + */ +static struct low_i2c_host *find_low_i2c_host(struct device_node *np) +{ + int i; + + for (i = 0; i < MAX_LOW_I2C_HOST; i++) + if (low_i2c_hosts[i].np == np) + return &low_i2c_hosts[i]; + return NULL; +} + +/* + * + * i2c-keywest implementation (UniNorth, U2, U3, Keylargo's) + * + */ + +/* + * Keywest i2c definitions borrowed from drivers/i2c/i2c-keywest.h, + * should be moved somewhere in include/asm-ppc/ + */ +/* Register indices */ +typedef enum { + reg_mode = 0, + reg_control, + reg_status, + reg_isr, + reg_ier, + reg_addr, + reg_subaddr, + reg_data +} reg_t; + + +/* Mode register */ +#define KW_I2C_MODE_100KHZ 0x00 +#define KW_I2C_MODE_50KHZ 0x01 +#define KW_I2C_MODE_25KHZ 0x02 +#define KW_I2C_MODE_DUMB 0x00 +#define KW_I2C_MODE_STANDARD 0x04 +#define KW_I2C_MODE_STANDARDSUB 0x08 +#define KW_I2C_MODE_COMBINED 0x0C +#define KW_I2C_MODE_MODE_MASK 0x0C +#define KW_I2C_MODE_CHAN_MASK 0xF0 + +/* Control register */ +#define KW_I2C_CTL_AAK 0x01 +#define KW_I2C_CTL_XADDR 0x02 +#define KW_I2C_CTL_STOP 0x04 +#define KW_I2C_CTL_START 0x08 + +/* Status register */ +#define KW_I2C_STAT_BUSY 0x01 +#define KW_I2C_STAT_LAST_AAK 0x02 +#define KW_I2C_STAT_LAST_RW 0x04 +#define KW_I2C_STAT_SDA 0x08 +#define KW_I2C_STAT_SCL 0x10 + +/* IER & ISR registers */ +#define KW_I2C_IRQ_DATA 0x01 +#define KW_I2C_IRQ_ADDR 0x02 +#define KW_I2C_IRQ_STOP 0x04 +#define KW_I2C_IRQ_START 0x08 +#define KW_I2C_IRQ_MASK 0x0F + +/* State machine states */ +enum { + state_idle, + state_addr, + state_read, + state_write, + state_stop, + state_dead +}; + +#define WRONG_STATE(name) do {\ + printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ + name, __kw_state_names[state], isr); \ + } while(0) + +static const char *__kw_state_names[] = { + "state_idle", + "state_addr", + "state_read", + "state_write", + "state_stop", + "state_dead" +}; + +static inline u8 __kw_read_reg(struct low_i2c_host *host, reg_t reg) +{ + return in_8(((volatile u8 *)host->base) + + (((unsigned)reg) << host->bsteps)); +} + +static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val) +{ + out_8(((volatile u8 *)host->base) + + (((unsigned)reg) << host->bsteps), val); + (void)__kw_read_reg(host, reg_subaddr); +} + +#define kw_write_reg(reg, val) __kw_write_reg(host, reg, val) +#define kw_read_reg(reg) __kw_read_reg(host, reg) + + +/* Don't schedule, the g5 fan controller is too + * timing sensitive + */ +static u8 kw_wait_interrupt(struct low_i2c_host* host) +{ + int i; + u8 isr; + + for (i = 0; i < 200000; i++) { + isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK; + if (isr != 0) + return isr; + udelay(1); + } + return isr; +} + +static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr) +{ + u8 ack; + + if (isr == 0) { + if (state != state_stop) { + DBG("KW: Timeout !\n"); + *rc = -EIO; + goto stop; + } + if (state == state_stop) { + ack = kw_read_reg(reg_status); + if (!(ack & KW_I2C_STAT_BUSY)) { + state = state_idle; + kw_write_reg(reg_ier, 0x00); + } + } + return state; + } + + if (isr & KW_I2C_IRQ_ADDR) { + ack = kw_read_reg(reg_status); + if (state != state_addr) { + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + WRONG_STATE("KW_I2C_IRQ_ADDR"); + *rc = -EIO; + goto stop; + } + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + *rc = -ENODEV; + DBG("KW: NAK on address\n"); + return state_stop; + } else { + if (rw) { + state = state_read; + if (*len > 1) + kw_write_reg(reg_control, KW_I2C_CTL_AAK); + } else { + state = state_write; + kw_write_reg(reg_data, **data); + (*data)++; (*len)--; + } + } + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + } + + if (isr & KW_I2C_IRQ_DATA) { + if (state == state_read) { + **data = kw_read_reg(reg_data); + (*data)++; (*len)--; + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + if ((*len) == 0) + state = state_stop; + else if ((*len) == 1) + kw_write_reg(reg_control, 0); + } else if (state == state_write) { + ack = kw_read_reg(reg_status); + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + DBG("KW: nack on data write\n"); + *rc = -EIO; + goto stop; + } else if (*len) { + kw_write_reg(reg_data, **data); + (*data)++; (*len)--; + } else { + kw_write_reg(reg_control, KW_I2C_CTL_STOP); + state = state_stop; + *rc = 0; + } + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + } else { + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + WRONG_STATE("KW_I2C_IRQ_DATA"); + if (state != state_stop) { + *rc = -EIO; + goto stop; + } + } + } + + if (isr & KW_I2C_IRQ_STOP) { + kw_write_reg(reg_isr, KW_I2C_IRQ_STOP); + if (state != state_stop) { + WRONG_STATE("KW_I2C_IRQ_STOP"); + *rc = -EIO; + } + return state_idle; + } + + if (isr & KW_I2C_IRQ_START) + kw_write_reg(reg_isr, KW_I2C_IRQ_START); + + return state; + + stop: + kw_write_reg(reg_control, KW_I2C_CTL_STOP); + return state_stop; +} + +static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len) +{ + u8 mode_reg = host->speed; + int state = state_addr; + int rc = 0; + + /* Setup mode & subaddress if any */ + switch(host->mode) { + case pmac_low_i2c_mode_dumb: + printk(KERN_ERR "low_i2c: Dumb mode not supported !\n"); + return -EINVAL; + case pmac_low_i2c_mode_std: + mode_reg |= KW_I2C_MODE_STANDARD; + break; + case pmac_low_i2c_mode_stdsub: + mode_reg |= KW_I2C_MODE_STANDARDSUB; + kw_write_reg(reg_subaddr, subaddr); + break; + case pmac_low_i2c_mode_combined: + mode_reg |= KW_I2C_MODE_COMBINED; + kw_write_reg(reg_subaddr, subaddr); + break; + } + + /* Setup channel & clear pending irqs */ + kw_write_reg(reg_isr, kw_read_reg(reg_isr)); + kw_write_reg(reg_mode, mode_reg | (host->channel << 4)); + kw_write_reg(reg_status, 0); + + /* Set up address and r/w bit */ + kw_write_reg(reg_addr, addr); + + /* Start sending address & disable interrupt*/ + kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/); + kw_write_reg(reg_control, KW_I2C_CTL_XADDR); + + /* State machine, to turn into an interrupt handler */ + while(state != state_idle) { + u8 isr = kw_wait_interrupt(host); + state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr); + } + + return rc; +} + +static void keywest_low_i2c_add(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(NULL); + unsigned long *psteps, *prate, steps, aoffset = 0; + struct device_node *parent; + + if (host == NULL) { + printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", + np->full_name); + return; + } + memset(host, 0, sizeof(*host)); + + init_MUTEX(&host->mutex); + host->np = of_node_get(np); + psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL); + steps = psteps ? (*psteps) : 0x10; + for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) + steps >>= 1; + parent = of_get_parent(np); + host->num_channels = 1; + if (parent && parent->name[0] == 'u') { + host->num_channels = 2; + aoffset = 3; + } + /* Select interface rate */ + host->speed = KW_I2C_MODE_100KHZ; + prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL); + if (prate) switch(*prate) { + case 100: + host->speed = KW_I2C_MODE_100KHZ; + break; + case 50: + host->speed = KW_I2C_MODE_50KHZ; + break; + case 25: + host->speed = KW_I2C_MODE_25KHZ; + break; + } + host->mode = pmac_low_i2c_mode_std; + host->base = (unsigned long)ioremap(np->addrs[0].address + aoffset, + np->addrs[0].size); + host->func = keywest_low_i2c_func; +} + +/* + * + * PMU implementation + * + */ + + +#ifdef CONFIG_ADB_PMU + +static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len) +{ + // TODO + return -ENODEV; +} + +static void pmu_low_i2c_add(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(NULL); + + if (host == NULL) { + printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", + np->full_name); + return; + } + memset(host, 0, sizeof(*host)); + + init_MUTEX(&host->mutex); + host->np = of_node_get(np); + host->num_channels = 3; + host->mode = pmac_low_i2c_mode_std; + host->func = pmu_low_i2c_func; +} + +#endif /* CONFIG_ADB_PMU */ + +void __init pmac_init_low_i2c(void) +{ + struct device_node *np; + + /* Probe keywest-i2c busses */ + np = of_find_compatible_node(NULL, "i2c", "keywest-i2c"); + while(np) { + keywest_low_i2c_add(np); + np = of_find_compatible_node(np, "i2c", "keywest-i2c"); + } + +#ifdef CONFIG_ADB_PMU + /* Probe PMU busses */ + np = of_find_node_by_name(NULL, "via-pmu"); + if (np) + pmu_low_i2c_add(np); +#endif /* CONFIG_ADB_PMU */ + + /* TODO: Add CUDA support as well */ +} + +int pmac_low_i2c_lock(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + down(&host->mutex); + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_lock); + +int pmac_low_i2c_unlock(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + up(&host->mutex); + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_unlock); + + +int pmac_low_i2c_open(struct device_node *np, int channel) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + + if (channel >= host->num_channels) + return -EINVAL; + + down(&host->mutex); + host->is_open = 1; + host->channel = channel; + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_open); + +int pmac_low_i2c_close(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + + host->is_open = 0; + up(&host->mutex); + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_close); + +int pmac_low_i2c_setmode(struct device_node *np, int mode) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + WARN_ON(!host->is_open); + host->mode = mode; + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_setmode); + +int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + WARN_ON(!host->is_open); + + return host->func(host, addrdir, subaddr, data, len); +} +EXPORT_SYMBOL(pmac_low_i2c_xfer); + diff --git a/arch/ppc64/kernel/pmac_nvram.c b/arch/ppc64/kernel/pmac_nvram.c new file mode 100644 index 000000000000..5ab6cd401973 --- /dev/null +++ b/arch/ppc64/kernel/pmac_nvram.c @@ -0,0 +1,495 @@ +/* + * arch/ppc/platforms/pmac_nvram.c + * + * Copyright (C) 2002 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Todo: - add support for the OF persistent properties + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ + +#define CORE99_SIGNATURE 0x5a +#define CORE99_ADLER_START 0x14 + +/* On Core99, nvram is either a sharp, a micron or an AMD flash */ +#define SM_FLASH_STATUS_DONE 0x80 +#define SM_FLASH_STATUS_ERR 0x38 + +#define SM_FLASH_CMD_ERASE_CONFIRM 0xd0 +#define SM_FLASH_CMD_ERASE_SETUP 0x20 +#define SM_FLASH_CMD_RESET 0xff +#define SM_FLASH_CMD_WRITE_SETUP 0x40 +#define SM_FLASH_CMD_CLEAR_STATUS 0x50 +#define SM_FLASH_CMD_READ_STATUS 0x70 + +/* CHRP NVRAM header */ +struct chrp_header { + u8 signature; + u8 cksum; + u16 len; + char name[12]; + u8 data[0]; +}; + +struct core99_header { + struct chrp_header hdr; + u32 adler; + u32 generation; + u32 reserved[2]; +}; + +/* + * Read and write the non-volatile RAM on PowerMacs and CHRP machines. + */ +static volatile unsigned char *nvram_data; +static int core99_bank = 0; +// XXX Turn that into a sem +static spinlock_t nv_lock = SPIN_LOCK_UNLOCKED; + +extern int system_running; + +static int (*core99_write_bank)(int bank, u8* datas); +static int (*core99_erase_bank)(int bank); + +static char *nvram_image __pmacdata; + + +static ssize_t __pmac core99_nvram_read(char *buf, size_t count, loff_t *index) +{ + int i; + + if (nvram_image == NULL) + return -ENODEV; + if (*index > NVRAM_SIZE) + return 0; + + i = *index; + if (i + count > NVRAM_SIZE) + count = NVRAM_SIZE - i; + + memcpy(buf, &nvram_image[i], count); + *index = i + count; + return count; +} + +static ssize_t __pmac core99_nvram_write(char *buf, size_t count, loff_t *index) +{ + int i; + + if (nvram_image == NULL) + return -ENODEV; + if (*index > NVRAM_SIZE) + return 0; + + i = *index; + if (i + count > NVRAM_SIZE) + count = NVRAM_SIZE - i; + + memcpy(&nvram_image[i], buf, count); + *index = i + count; + return count; +} + +static ssize_t __pmac core99_nvram_size(void) +{ + if (nvram_image == NULL) + return -ENODEV; + return NVRAM_SIZE; +} + +static u8 __pmac chrp_checksum(struct chrp_header* hdr) +{ + u8 *ptr; + u16 sum = hdr->signature; + for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++) + sum += *ptr; + while (sum > 0xFF) + sum = (sum & 0xFF) + (sum>>8); + return sum; +} + +static u32 __pmac core99_calc_adler(u8 *buffer) +{ + int cnt; + u32 low, high; + + buffer += CORE99_ADLER_START; + low = 1; + high = 0; + for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) { + if ((cnt % 5000) == 0) { + high %= 65521UL; + high %= 65521UL; + } + low += buffer[cnt]; + high += low; + } + low %= 65521UL; + high %= 65521UL; + + return (high << 16) | low; +} + +static u32 __pmac core99_check(u8* datas) +{ + struct core99_header* hdr99 = (struct core99_header*)datas; + + if (hdr99->hdr.signature != CORE99_SIGNATURE) { + DBG("Invalid signature\n"); + return 0; + } + if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { + DBG("Invalid checksum\n"); + return 0; + } + if (hdr99->adler != core99_calc_adler(datas)) { + DBG("Invalid adler\n"); + return 0; + } + return hdr99->generation; +} + +static int __pmac sm_erase_bank(int bank) +{ + int stat, i; + unsigned long timeout; + + u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + + DBG("nvram: Sharp/Micron Erasing bank %d...\n", bank); + + out_8(base, SM_FLASH_CMD_ERASE_SETUP); + out_8(base, SM_FLASH_CMD_ERASE_CONFIRM); + timeout = 0; + do { + if (++timeout > 1000000) { + printk(KERN_ERR "nvram: Sharp/Miron flash erase timeout !\n"); + break; + } + out_8(base, SM_FLASH_CMD_READ_STATUS); + stat = in_8(base); + } while (!(stat & SM_FLASH_STATUS_DONE)); + + out_8(base, SM_FLASH_CMD_CLEAR_STATUS); + out_8(base, SM_FLASH_CMD_RESET); + + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: Sharp/Micron flash write timeout !\n"); + break; + } + out_8(base, SM_FLASH_CMD_READ_STATUS); + stat = in_8(base); + } while (!(stat & SM_FLASH_STATUS_DONE)); + if (!(stat & SM_FLASH_STATUS_DONE)) + break; + } + out_8(base, SM_FLASH_CMD_CLEAR_STATUS); + out_8(base, SM_FLASH_CMD_RESET); + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: AMD flash erase timeout !\n"); + break; + } + stat = in_8(base) ^ in_8(base); + } while (stat != 0); + + /* Reset */ + out_8(base, 0xf0); + udelay(1); + + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: AMD flash write timeout !\n"); + break; + } + stat = in_8(base) ^ in_8(base); + } while (stat != 0); + if (stat != 0) + break; + } + + /* Reset */ + out_8(base, 0xf0); + udelay(1); + + for (i=0; igeneration++; + hdr99->hdr.signature = CORE99_SIGNATURE; + hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); + hdr99->adler = core99_calc_adler(nvram_image); + core99_bank = core99_bank ? 0 : 1; + if (core99_erase_bank) + if (core99_erase_bank(core99_bank)) { + printk("nvram: Error erasing bank %d\n", core99_bank); + goto bail; + } + if (core99_write_bank) + if (core99_write_bank(core99_bank, nvram_image)) + printk("nvram: Error writing bank %d\n", core99_bank); + bail: + spin_unlock_irqrestore(&nv_lock, flags); + + return 0; +} + +int __init pmac_nvram_init(void) +{ + struct device_node *dp; + u32 gen_bank0, gen_bank1; + int i; + + dp = find_devices("nvram"); + if (dp == NULL) { + printk(KERN_ERR "Can't find NVRAM device\n"); + return -ENODEV; + } + if (!device_is_compatible(dp, "nvram,flash")) { + printk(KERN_ERR "Incompatible type of NVRAM\n"); + return -ENXIO; + } + + nvram_image = alloc_bootmem(NVRAM_SIZE); + if (nvram_image == NULL) { + printk(KERN_ERR "nvram: can't allocate ram image\n"); + return -ENOMEM; + } + nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); + + DBG("nvram: Checking bank 0...\n"); + + gen_bank0 = core99_check((u8 *)nvram_data); + gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); + core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; + + DBG("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); + DBG("nvram: Active bank is: %d\n", core99_bank); + + for (i=0; iindex; +} + +u8 __pmac pmac_xpram_read(int xpaddr) +{ + int offset = pmac_get_partition(pmac_nvram_XPRAM); + loff_t index; + u8 buf; + ssize_t count; + + if (offset < 0 || xpaddr < 0 || xpaddr > 0x100) + return 0xff; + index = offset + xpaddr; + + count = ppc_md.nvram_read(&buf, 1, &index); + if (count != 1) + return 0xff; + return buf; +} + +void __pmac pmac_xpram_write(int xpaddr, u8 data) +{ + int offset = pmac_get_partition(pmac_nvram_XPRAM); + loff_t index; + u8 buf; + + if (offset < 0 || xpaddr < 0 || xpaddr > 0x100) + return; + index = offset + xpaddr; + buf = data; + + ppc_md.nvram_write(&buf, 1, &index); +} + +EXPORT_SYMBOL(pmac_get_partition); +EXPORT_SYMBOL(pmac_xpram_read); +EXPORT_SYMBOL(pmac_xpram_write); diff --git a/arch/ppc64/kernel/pmac_pci.c b/arch/ppc64/kernel/pmac_pci.c new file mode 100644 index 000000000000..b1e332573e6f --- /dev/null +++ b/arch/ppc64/kernel/pmac_pci.c @@ -0,0 +1,755 @@ +/* + * Support for PCI bridges found on Power Macintoshes. + * At present the "bandit" and "chaos" bridges are supported. + * Fortunately you access configuration space in the same + * way with either bridge. + * + * Copyright (C) 2003 Benjamin Herrenschmuidt (benh@kernel.crashing.org) + * Copyright (C) 1997 Paul Mackerras (paulus@samba.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pci.h" +#include "pmac.h" + +#define DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +extern int pci_probe_only; +extern int pci_read_irq_line(struct pci_dev *pci_dev); + +/* XXX Could be per-controller, but I don't think we risk anything by + * assuming we won't have both UniNorth and Bandit */ +static int has_uninorth; +static struct pci_controller *u3_agp; +u8 pci_cache_line_size; +struct pci_dev *k2_skiplist[2]; + +static int __init fixup_one_level_bus_range(struct device_node *node, int higher) +{ + for (; node != 0;node = node->sibling) { + int * bus_range; + unsigned int *class_code; + int len; + + /* For PCI<->PCI bridges or CardBus bridges, we go down */ + class_code = (unsigned int *) get_property(node, "class-code", 0); + if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && + (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) + continue; + bus_range = (int *) get_property(node, "bus-range", &len); + if (bus_range != NULL && len > 2 * sizeof(int)) { + if (bus_range[1] > higher) + higher = bus_range[1]; + } + higher = fixup_one_level_bus_range(node->child, higher); + } + return higher; +} + +/* This routine fixes the "bus-range" property of all bridges in the + * system since they tend to have their "last" member wrong on macs + * + * Note that the bus numbers manipulated here are OF bus numbers, they + * are not Linux bus numbers. + */ +static void __init fixup_bus_range(struct device_node *bridge) +{ + int * bus_range; + int len; + + /* Lookup the "bus-range" property for the hose */ + bus_range = (int *) get_property(bridge, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + bridge->full_name); + return; + } + bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); +} + +/* + * Apple MacRISC (U3, UniNorth, Bandit, Chaos) PCI controllers. + * + * The "Bandit" version is present in all early PCI PowerMacs, + * and up to the first ones using Grackle. Some machines may + * have 2 bandit controllers (2 PCI busses). + * + * "Chaos" is used in some "Bandit"-type machines as a bridge + * for the separate display bus. It is accessed the same + * way as bandit, but cannot be probed for devices. It therefore + * has its own config access functions. + * + * The "UniNorth" version is present in all Core99 machines + * (iBook, G4, new IMacs, and all the recent Apple machines). + * It contains 3 controllers in one ASIC. + * + * The U3 is the bridge used on G5 machines. It contains on + * AGP bus which is dealt with the old UniNorth access routines + * and an HyperTransport bus which uses its own set of access + * functions. + */ + +#define MACRISC_CFA0(devfn, off) \ + ((1 << (unsigned long)PCI_SLOT(dev_fn)) \ + | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \ + | (((unsigned long)(off)) & 0xFCUL)) + +#define MACRISC_CFA1(bus, devfn, off) \ + ((((unsigned long)(bus)) << 16) \ + |(((unsigned long)(devfn)) << 8) \ + |(((unsigned long)(off)) & 0xFCUL) \ + |1UL) + +static unsigned long __pmac macrisc_cfg_access(struct pci_controller* hose, + u8 bus, u8 dev_fn, u8 offset) +{ + unsigned int caddr; + + if (bus == hose->first_busno) { + if (dev_fn < (11 << 3)) + return 0; + caddr = MACRISC_CFA0(dev_fn, offset); + } else + caddr = MACRISC_CFA1(bus, dev_fn, offset); + + /* Uninorth will return garbage if we don't read back the value ! */ + do { + out_le32(hose->cfg_addr, caddr); + } while (in_le32(hose->cfg_addr) != caddr); + + offset &= has_uninorth ? 0x07 : 0x03; + return ((unsigned long)hose->cfg_data) + offset; +} + +static int __pmac macrisc_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct pci_controller *hose; + struct device_node *busdn; + unsigned long addr; + int i; + + if (bus->self) + busdn = pci_device_to_OF_node(bus->self); + else + busdn = bus->sysdata; /* must be a phb */ + if (busdn == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + hose = busdn->phb; + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * When a device in K2 is powered down, we die on config + * cycle accesses. Fix that here. + */ + for (i=0; i<2; i++) + if (k2_skiplist[i] && k2_skiplist[i]->bus == bus && + k2_skiplist[i]->devfn == devfn) { + switch (len) { + case 1: + *val = 0xff; break; + case 2: + *val = 0xffff; break; + default: + *val = 0xfffffffful; break; + } + return PCIBIOS_SUCCESSFUL; + } + + addr = macrisc_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + *val = in_8((u8 *)addr); + break; + case 2: + *val = in_le16((u16 *)addr); + break; + default: + *val = in_le32((u32 *)addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int __pmac macrisc_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose; + struct device_node *busdn; + unsigned long addr; + int i; + + if (bus->self) + busdn = pci_device_to_OF_node(bus->self); + else + busdn = bus->sysdata; /* must be a phb */ + if (busdn == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + hose = busdn->phb; + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * When a device in K2 is powered down, we die on config + * cycle accesses. Fix that here. + */ + for (i=0; i<2; i++) + if (k2_skiplist[i] && k2_skiplist[i]->bus == bus && + k2_skiplist[i]->devfn == devfn) + return PCIBIOS_SUCCESSFUL; + + addr = macrisc_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + out_8((u8 *)addr, val); + (void) in_8((u8 *)addr); + break; + case 2: + out_le16((u16 *)addr, val); + (void) in_le16((u16 *)addr); + break; + default: + out_le32((u32 *)addr, val); + (void) in_le32((u32 *)addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops macrisc_pci_ops = +{ + macrisc_read_config, + macrisc_write_config +}; + +/* + * These versions of U3 HyperTransport config space access ops do not + * implement self-view of the HT host yet + */ + +#define U3_HT_CFA0(devfn, off) \ + ((((unsigned long)devfn) << 8) | offset) +#define U3_HT_CFA1(bus, devfn, off) \ + (U3_HT_CFA0(devfn, off) \ + + (((unsigned long)bus) << 16) \ + + 0x01000000UL) + +static unsigned long __pmac u3_ht_cfg_access(struct pci_controller* hose, + u8 bus, u8 devfn, u8 offset) +{ + if (bus == hose->first_busno) { + /* For now, we don't self probe U3 HT bridge */ + if (PCI_FUNC(devfn) != 0 || PCI_SLOT(devfn) > 7 || + PCI_SLOT(devfn) < 1) + return 0; + return ((unsigned long)hose->cfg_data) + U3_HT_CFA0(devfn, offset); + } else + return ((unsigned long)hose->cfg_data) + U3_HT_CFA1(bus, devfn, offset); +} + +static int __pmac u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct pci_controller *hose; + struct device_node *busdn; + unsigned long addr; + + if (bus->self) + busdn = pci_device_to_OF_node(bus->self); + else + busdn = bus->sysdata; /* must be a phb */ + if (busdn == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + hose = busdn->phb; + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + *val = in_8((u8 *)addr); + break; + case 2: + *val = in_le16((u16 *)addr); + break; + default: + *val = in_le32((u32 *)addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int __pmac u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose; + struct device_node *busdn; + unsigned long addr; + + if (bus->self) + busdn = pci_device_to_OF_node(bus->self); + else + busdn = bus->sysdata; /* must be a phb */ + if (busdn == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + hose = busdn->phb; + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + out_8((u8 *)addr, val); + (void) in_8((u8 *)addr); + break; + case 2: + out_le16((u16 *)addr, val); + (void) in_le16((u16 *)addr); + break; + default: + out_le32((u32 *)addr, val); + (void) in_le32((u32 *)addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops u3_ht_pci_ops = +{ + u3_ht_read_config, + u3_ht_write_config +}; + +static void __init setup_u3_agp(struct pci_controller* hose) +{ + /* On G5, we move AGP up to high bus number so we don't need + * to reassign bus numbers for HT. If we ever have P2P bridges + * on AGP, we'll have to move pci_assign_all_busses to the + * pci_controller structure so we enable it for AGP and not for + * HT childs. + * We hard code the address because of the different size of + * the reg address cell, we shall fix that by killing struct + * reg_property and using some accessor functions instead + */ + hose->first_busno = 0xf0; + hose->last_busno = 0xff; + has_uninorth = 1; + hose->ops = ¯isc_pci_ops; + hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); + hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); + + u3_agp = hose; +} + +static void __init setup_u3_ht(struct pci_controller* hose) +{ + struct device_node *np = (struct device_node *)hose->arch_data; + int i, cur; + + hose->ops = &u3_ht_pci_ops; + + /* We hard code the address because of the different size of + * the reg address cell, we shall fix that by killing struct + * reg_property and using some accessor functions instead + */ + hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000, 0x02000000); + + /* + * /ht node doesn't expose a "ranges" property, so we "remove" regions that + * have been allocated to AGP. So far, this version of the code doesn't assign + * any of the 0xfxxxxxxx "fine" memory regions to /ht. + * We need to fix that sooner or later by either parsing all child "ranges" + * properties or figuring out the U3 address space decoding logic and + * then read it's configuration register (if any). + */ + hose->io_base_phys = 0xf4000000 + 0x00400000; + hose->io_base_virt = ioremap(hose->io_base_phys, 0x00400000); + isa_io_base = pci_io_base = (unsigned long) hose->io_base_virt; + hose->io_resource.name = np->full_name; + hose->io_resource.start = 0; + hose->io_resource.end = 0x003fffff; + hose->io_resource.flags = IORESOURCE_IO; + hose->pci_mem_offset = 0; + hose->first_busno = 0; + hose->last_busno = 0xef; + hose->mem_resources[0].name = np->full_name; + hose->mem_resources[0].start = 0x80000000; + hose->mem_resources[0].end = 0xefffffff; + hose->mem_resources[0].flags = IORESOURCE_MEM; + + if (u3_agp == NULL) { + DBG("U3 has no AGP, using full resource range\n"); + return; + } + + /* We "remove" the AGP resources from the resources allocated to HT, that + * is we create "holes". However, that code does assumptions that so far + * happen to be true (cross fingers...), typically that resources in the + * AGP node are properly ordered + */ + cur = 0; + for (i=0; i<3; i++) { + struct resource *res = &u3_agp->mem_resources[i]; + if (res->flags != IORESOURCE_MEM) + continue; + /* We don't care about "fine" resources */ + if (res->start >= 0xf0000000) + continue; + /* Check if it's just a matter of "shrinking" us in one direction */ + if (hose->mem_resources[cur].start == res->start) { + DBG("U3/HT: shrink start of %d, %08lx -> %08lx\n", + cur, hose->mem_resources[cur].start, res->end + 1); + hose->mem_resources[cur].start = res->end + 1; + continue; + } + if (hose->mem_resources[cur].end == res->end) { + DBG("U3/HT: shrink end of %d, %08lx -> %08lx\n", + cur, hose->mem_resources[cur].end, res->start - 1); + hose->mem_resources[cur].end = res->start - 1; + continue; + } + /* No, it's not the case, we need a hole */ + if (cur == 2) { + /* not enough resources for a hole, we drop part of the range */ + printk(KERN_WARNING "Running out of resources for /ht host !\n"); + hose->mem_resources[cur].end = res->start - 1; + continue; + } + cur++; + DBG("U3/HT: hole, %d end at %08lx, %d start at %08lx\n", + cur-1, res->start - 1, cur, res->end + 1); + hose->mem_resources[cur].name = np->full_name; + hose->mem_resources[cur].flags = IORESOURCE_MEM; + hose->mem_resources[cur].start = res->end + 1; + hose->mem_resources[cur].end = hose->mem_resources[cur-1].end; + hose->mem_resources[cur-1].end = res->start - 1; + } +} + +static void __init pmac_process_bridge_OF_ranges(struct pci_controller *hose, + struct device_node *dev, int primary) +{ + static unsigned int static_lc_ranges[2024]; + unsigned int *dt_ranges, *lc_ranges, *ranges, *prev; + unsigned int size; + int rlen = 0, orig_rlen; + int memno = 0; + struct resource *res; + int np, na = prom_n_addr_cells(dev); + + np = na + 5; + + /* First we try to merge ranges to fix a problem with some pmacs + * that can have more than 3 ranges, fortunately using contiguous + * addresses -- BenH + */ + dt_ranges = (unsigned int *) get_property(dev, "ranges", &rlen); + if (!dt_ranges) + return; + /* lc_ranges = (unsigned int *) alloc_bootmem(rlen);*/ + lc_ranges = static_lc_ranges; + if (!lc_ranges) + return; /* what can we do here ? */ + memcpy(lc_ranges, dt_ranges, rlen); + orig_rlen = rlen; + + /* Let's work on a copy of the "ranges" property instead of damaging + * the device-tree image in memory + */ + ranges = lc_ranges; + prev = NULL; + while ((rlen -= np * sizeof(unsigned int)) >= 0) { + if (prev) { + if (prev[0] == ranges[0] && prev[1] == ranges[1] && + (prev[2] + prev[na+4]) == ranges[2] && + (prev[na+2] + prev[na+4]) == ranges[na+2]) { + prev[na+4] += ranges[na+4]; + ranges[0] = 0; + ranges += np; + continue; + } + } + prev = ranges; + ranges += np; + } + + /* + * The ranges property is laid out as an array of elements, + * each of which comprises: + * cells 0 - 2: a PCI address + * cells 3 or 3+4: a CPU physical address + * (size depending on dev->n_addr_cells) + * cells 4+5 or 5+6: the size of the range + */ + ranges = lc_ranges; + rlen = orig_rlen; + while (ranges && (rlen -= np * sizeof(unsigned int)) >= 0) { + res = NULL; + size = ranges[na+4]; + switch (ranges[0] >> 24) { + case 1: /* I/O space */ + if (ranges[2] != 0) + break; + hose->io_base_phys = ranges[na+2]; + /* limit I/O space to 16MB */ + if (size > 0x01000000) + size = 0x01000000; + hose->io_base_virt = ioremap(ranges[na+2], size); + if (primary) + isa_io_base = (unsigned long) hose->io_base_virt; + res = &hose->io_resource; + res->flags = IORESOURCE_IO; + res->start = ranges[2]; + break; + case 2: /* memory space */ + memno = 0; + if (ranges[1] == 0 && ranges[2] == 0 + && ranges[na+4] <= (16 << 20)) { + /* 1st 16MB, i.e. ISA memory area */ +#if 0 + if (primary) + isa_mem_base = ranges[na+2]; +#endif + memno = 1; + } + while (memno < 3 && hose->mem_resources[memno].flags) + ++memno; + if (memno == 0) + hose->pci_mem_offset = ranges[na+2] - ranges[2]; + if (memno < 3) { + res = &hose->mem_resources[memno]; + res->flags = IORESOURCE_MEM; + res->start = ranges[na+2]; + } + break; + } + if (res != NULL) { + res->name = dev->full_name; + res->end = res->start + size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + ranges += np; + } +} + +/* + * We assume that if we have a G3 powermac, we have one bridge called + * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, + * if we have one or more bandit or chaos bridges, we don't have a MPC106. + */ +static int __init add_bridge(struct device_node *dev) +{ + int len; + struct pci_controller *hose; + char* disp_name; + int *bus_range; + int primary = 1; + struct property *of_prop; + + DBG("Adding PCI host bridge %s\n", dev->full_name); + + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n", + dev->full_name); + } + + hose = pci_alloc_pci_controller(phb_type_apple); + if (!hose) + return -ENOMEM; + hose->arch_data = dev; + hose->first_busno = bus_range ? bus_range[0] : 0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + of_prop = (struct property *)alloc_bootmem(sizeof(struct property) + + sizeof(hose->global_number)); + if (of_prop) { + memset(of_prop, 0, sizeof(struct property)); + of_prop->name = "linux,pci-domain"; + of_prop->length = sizeof(hose->global_number); + of_prop->value = (unsigned char *)&of_prop[1]; + memcpy(of_prop->value, &hose->global_number, sizeof(hose->global_number)); + prom_add_property(dev, of_prop); + } + + disp_name = NULL; + if (device_is_compatible(dev, "u3-agp")) { + setup_u3_agp(hose); + disp_name = "U3-AGP"; + primary = 0; + } else if (device_is_compatible(dev, "u3-ht")) { + setup_u3_ht(hose); + disp_name = "U3-HT"; + primary = 1; + } + printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number: %d->%d\n", + disp_name, hose->first_busno, hose->last_busno); + + /* Interpret the "ranges" property */ + /* This also maps the I/O region and sets isa_io/mem_base */ + pmac_process_bridge_OF_ranges(hose, dev, primary); + + /* Fixup "bus-range" OF property */ + fixup_bus_range(dev); + + return 0; +} + + +void __init pmac_pcibios_fixup(void) +{ + struct pci_dev *dev = NULL; + + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) + pci_read_irq_line(dev); + + pci_fix_bus_sysdata(); +} + +static void __init pmac_fixup_phb_resources(void) +{ + struct pci_controller *hose; + + for (hose = hose_head; hose; hose = hose->next) { + unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base; + hose->io_resource.start += offset; + hose->io_resource.end += offset; + printk(KERN_INFO "PCI Host %d, io start: %lx; io end: %lx\n", + hose->global_number, + hose->io_resource.start, hose->io_resource.end); + } +} + +void __init pmac_pci_init(void) +{ + struct device_node *np, *root; + struct device_node *ht = NULL; + + /* Probe root PCI hosts, that is on U3 the AGP host and the + * HyperTransport host. That one is actually "kept" around + * and actually added last as it's resource management relies + * on the AGP resources to have been setup first + */ + root = of_find_node_by_path("/"); + if (root == NULL) { + printk(KERN_CRIT "pmac_find_bridges: can't find root of device tree\n"); + return; + } + for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) { + if (np->name == NULL) + continue; + if (strcmp(np->name, "pci") == 0) { + if (add_bridge(np) == 0) + of_node_get(np); + } + if (strcmp(np->name, "ht") == 0) { + of_node_get(np); + ht = np; + } + } + of_node_put(root); + + /* Now setup the HyperTransport host if we found any + */ + if (ht && add_bridge(ht) != 0) + of_node_put(ht); + + /* Fixup the IO resources on our host bridges as the common code + * does it only for childs of the host bridges + */ + pmac_fixup_phb_resources(); + + /* Setup the linkage between OF nodes and PHBs */ + pci_devs_phb_init(); + + pmac_check_ht_link(); + + /* Tell pci.c to use the common resource allocation mecanism */ + pci_probe_only = 0; + + /* HT don't do more than 64 bytes transfers. FIXME: Deal with + * the exception of U3/AGP (hook into pci_set_mwi) + */ + pci_cache_line_size = 16; /* 64 bytes */ +} + +/* + * Disable second function on K2-SATA, it's broken + * and disable IO BARs on first one + */ +void fixup_k2_sata(struct pci_dev* dev) +{ + int i; + u16 cmd; + + if (PCI_FUNC(dev->devfn) > 0) { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + pci_write_config_word(dev, PCI_COMMAND, cmd); + for (i = 0; i < 6; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + } + } else { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_IO; + pci_write_config_word(dev, PCI_COMMAND, cmd); + for (i = 0; i < 5; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + } + } +} diff --git a/arch/ppc64/kernel/pmac_setup.c b/arch/ppc64/kernel/pmac_setup.c new file mode 100644 index 000000000000..4c33810dc82c --- /dev/null +++ b/arch/ppc64/kernel/pmac_setup.c @@ -0,0 +1,504 @@ +/* + * arch/ppc/platforms/setup.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * + * Derived from "arch/alpha/kernel/setup.c" + * Copyright (C) 1995 Linus Torvalds + * + * Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmac.h" + +extern char saved_command_line[]; +static int current_root_goodness = -1; +#define DEFAULT_ROOT_DEVICE Root_SDA1 /* sda1 - slightly silly choice */ + +extern int powersave_nap; +int sccdbg; + +extern void udbg_init_scc(struct device_node *np); + +#ifdef CONFIG_BOOTX_TEXT +void pmac_progress(char *s, unsigned short hex); +#endif + +void __pmac pmac_show_cpuinfo(struct seq_file *m) +{ + struct device_node *np; + char *pp; + int plen; + char* mbname; + int mbmodel = pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, + PMAC_MB_INFO_MODEL, 0); + unsigned int mbflags = pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, + PMAC_MB_INFO_FLAGS, 0); + + if (pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_NAME, + (long)&mbname) != 0) + mbname = "Unknown"; + + /* find motherboard type */ + seq_printf(m, "machine\t\t: "); + np = find_devices("device-tree"); + if (np != NULL) { + pp = (char *) get_property(np, "model", NULL); + if (pp != NULL) + seq_printf(m, "%s\n", pp); + else + seq_printf(m, "PowerMac\n"); + pp = (char *) get_property(np, "compatible", &plen); + if (pp != NULL) { + seq_printf(m, "motherboard\t:"); + while (plen > 0) { + int l = strlen(pp) + 1; + seq_printf(m, " %s", pp); + plen -= l; + pp += l; + } + seq_printf(m, "\n"); + } + } else + seq_printf(m, "PowerMac\n"); + + /* print parsed model */ + seq_printf(m, "detected as\t: %d (%s)\n", mbmodel, mbname); + seq_printf(m, "pmac flags\t: %08x\n", mbflags); + seq_printf(m, "memory\t\t: %luMB\n", lmb_phys_mem_size() >> 20); + + /* Checks "l2cr-value" property in the registry */ + np = find_devices("cpus"); + if (np == 0) + np = find_type_devices("cpu"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + seq_printf(m, "l2cr override\t: 0x%x\n", *l2cr); + } + } + + /* Indicate newworld */ + seq_printf(m, "pmac-generation\t: NewWorld\n"); +} + + +void __init pmac_setup_arch(void) +{ + struct device_node *cpu; + int *fp; + unsigned long pvr; + + pvr = PVR_VER(mfspr(PVR)); + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) { + if (pvr == 4 || pvr >= 8) + /* 604, G3, G4 etc. */ + loops_per_jiffy = *fp / HZ; + else + /* 601, 603, etc. */ + loops_per_jiffy = *fp / (2*HZ); + } else + loops_per_jiffy = 50000000 / HZ; + } + + /* We can NAP */ + powersave_nap = 1; + + /* Initialize the PMU */ + find_via_pmu(); + + /* Init NVRAM access */ + pmac_nvram_init(); + + /* Setup SMP callback */ +#ifdef CONFIG_SMP + pmac_setup_smp(); +#endif + /* Setup the PCI DMA to "direct" for now, until we have proper + * DART support and can deal with more than 2Gb of RAM + */ + pci_dma_init_direct(); + + /* Lookup PCI hosts */ + pmac_pci_init(); + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif +} + +extern char *bootpath; +extern char *bootdevice; +void *boot_host; +int boot_target; +int boot_part; +extern dev_t boot_dev; + +#ifdef CONFIG_SCSI +void __init note_scsi_host(struct device_node *node, void *host) +{ + int l; + char *p; + + l = strlen(node->full_name); + if (bootpath != NULL && bootdevice != NULL + && strncmp(node->full_name, bootdevice, l) == 0 + && (bootdevice[l] == '/' || bootdevice[l] == 0)) { + boot_host = host; + /* + * There's a bug in OF 1.0.5. (Why am I not surprised.) + * If you pass a path like scsi/sd@1:0 to canon, it returns + * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 + * That is, the scsi target number doesn't get preserved. + * So we pick the target number out of bootpath and use that. + */ + p = strstr(bootpath, "/sd@"); + if (p != NULL) { + p += 4; + boot_target = simple_strtoul(p, NULL, 10); + p = strchr(p, ':'); + if (p != NULL) + boot_part = simple_strtoul(p + 1, NULL, 10); + } + } +} +#endif + +#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) +static dev_t __init find_ide_boot(void) +{ + char *p; + int n; + dev_t __init pmac_find_ide_boot(char *bootdevice, int n); + + if (bootdevice == NULL) + return 0; + p = strrchr(bootdevice, '/'); + if (p == NULL) + return 0; + n = p - bootdevice; + + return pmac_find_ide_boot(bootdevice, n); +} +#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */ + +void __init find_boot_device(void) +{ +#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) + boot_dev = find_ide_boot(); +#endif +} + +static int initializing = 1; + +static int pmac_late_init(void) +{ + initializing = 0; + return 0; +} + +late_initcall(pmac_late_init); + +/* can't be __init - can be called whenever a disk is first accessed */ +void __pmac note_bootable_part(dev_t dev, int part, int goodness) +{ + static int found_boot = 0; + char *p; + + if (!initializing) + return; + if ((goodness <= current_root_goodness) && + ROOT_DEV != DEFAULT_ROOT_DEVICE) + return; + p = strstr(saved_command_line, "root="); + if (p != NULL && (p == saved_command_line || p[-1] == ' ')) + return; + + if (!found_boot) { + find_boot_device(); + found_boot = 1; + } + if (!boot_dev || dev == boot_dev) { + ROOT_DEV = dev + part; + boot_dev = 0; + current_root_goodness = goodness; + } +} + +void __pmac pmac_restart(char *cmd) +{ + pmu_restart(); +} + +void __pmac pmac_power_off(void) +{ + pmu_shutdown(); +} + +void __pmac pmac_halt(void) +{ + pmac_power_off(); +} + +#ifdef CONFIG_BOOTX_TEXT +static int dummy_getc_poll(void) +{ + return -1; +} + +static unsigned char dummy_getc(void) +{ + return 0; +} + +static void btext_putc(unsigned char c) +{ + btext_drawchar(c); +} +#endif /* CONFIG_BOOTX_TEXT */ + +/* + * Early initialization. + * Relocation is on but do not reference unbolted pages + * Also, device-tree hasn't been "finished", so don't muck with + * it too much + */ +void __init pmac_init_early(void) +{ + hpte_init_pSeries(); + +#ifdef CONFIG_BOOTX_TEXT + ppc_md.udbg_putc = btext_putc; + ppc_md.udbg_getc = dummy_getc; + ppc_md.udbg_getc_poll = dummy_getc_poll; +#endif /* CONFIG_BOOTX_TEXT */ +} + +extern void* OpenPIC_Addr; +extern void* OpenPIC2_Addr; +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; +extern void openpic_init(int main_pic, int offset, unsigned char* chrp_ack, + int programmer_switch_irq); +extern void openpic2_init(int offset); +extern int openpic_get_irq(struct pt_regs *regs); +extern int openpic2_get_irq(struct pt_regs *regs); + +static int pmac_cascade_irq = -1; + +static irqreturn_t pmac_u3_do_cascade(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq; + + for (;;) { + irq = openpic2_get_irq(regs); + if (irq == -1) + break; + ppc_irq_dispatch_handler(regs, irq); + } + return IRQ_HANDLED; +} + +static __init void pmac_init_IRQ(void) +{ + struct device_node *irqctrler = NULL; + struct device_node *irqctrler2 = NULL; + struct device_node *np = NULL; + + /* We first try to detect Apple's new Core99 chipset, since mac-io + * is quite different on those machines and contains an IBM MPIC2. + */ + while ((np = of_find_node_by_type(np, "open-pic")) != NULL) { + struct device_node *parent = of_get_parent(np); + if (parent && !strcmp(parent->name, "u3")) + irqctrler2 = of_node_get(np); + else + irqctrler = of_node_get(np); + of_node_put(parent); + } + if (irqctrler != NULL && irqctrler->n_addrs > 0) { + unsigned char senses[128]; + + printk(KERN_INFO "PowerMac using OpenPIC irq controller at 0x%08x\n", + (unsigned int)irqctrler->addrs[0].address); + + prom_get_irq_senses(senses, 0, 128); + OpenPIC_InitSenses = senses; + OpenPIC_NumInitSenses = 128; + OpenPIC_Addr = ioremap(irqctrler->addrs[0].address, + irqctrler->addrs[0].size); + openpic_init(1, 0, NULL, -1); + + if (irqctrler2 != NULL && irqctrler2->n_intrs > 0 && + irqctrler2->n_addrs > 0) { + printk(KERN_INFO "Slave OpenPIC at 0x%08x hooked on IRQ %d\n", + (u32)irqctrler2->addrs[0].address, + irqctrler2->intrs[0].line); + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler2, 0, 0); + OpenPIC2_Addr = ioremap(irqctrler2->addrs[0].address, + irqctrler2->addrs[0].size); + prom_get_irq_senses(senses, 128, 128 + 128); + OpenPIC_InitSenses = senses; + OpenPIC_NumInitSenses = 128; + openpic2_init(128); + pmac_cascade_irq = irqctrler2->intrs[0].line; + } + } + of_node_put(irqctrler); + of_node_put(irqctrler2); +} + +/* We cannot do request_irq too early ... Right now, we get the + * cascade as a core_initcall, which should be fine for our needs + */ +static int __init pmac_irq_cascade_init(void) +{ + if (request_irq(pmac_cascade_irq, pmac_u3_do_cascade, 0, + "U3->K2 Cascade", NULL)) + printk(KERN_ERR "Unable to get OpenPIC IRQ for cascade\n"); + return 0; +} + +core_initcall(pmac_irq_cascade_init); + +void __init pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* Probe motherboard chipset */ + pmac_feature_init(); + + /* Init SCC */ + if (strstr(cmd_line, "sccdbg")) { + sccdbg = 1; + udbg_init_scc(NULL); + } + + /* Fill up the machine description */ + ppc_md.setup_arch = pmac_setup_arch; + ppc_md.get_cpuinfo = pmac_show_cpuinfo; + + ppc_md.init_IRQ = pmac_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.pcibios_fixup = pmac_pcibios_fixup; + + ppc_md.restart = pmac_restart; + ppc_md.power_off = pmac_power_off; + ppc_md.halt = pmac_halt; + + ppc_md.get_boot_time = pmac_get_boot_time; + ppc_md.set_rtc_time = pmac_set_rtc_time; + ppc_md.get_rtc_time = pmac_get_rtc_time; + ppc_md.calibrate_decr = pmac_calibrate_decr; + + ppc_md.feature_call = pmac_do_feature_call; + + +#ifdef CONFIG_BOOTX_TEXT + ppc_md.progress = pmac_progress; +#endif /* CONFIG_BOOTX_TEXT */ + + if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0); + +} + +#ifdef CONFIG_BOOTX_TEXT +void __init pmac_progress(char *s, unsigned short hex) +{ + if (sccdbg) { + udbg_puts(s); + udbg_putc('\n'); + } + else if (boot_text_mapped) { + btext_drawstring(s); + btext_drawchar('\n'); + } +} +#endif /* CONFIG_BOOTX_TEXT */ + +static int __init pmac_declare_of_platform_devices(void) +{ + struct device_node *np; + + np = find_devices("u3"); + if (np) { + for (np = np->child; np != NULL; np = np->sibling) + if (strncmp(np->name, "i2c", 3) == 0) { + of_platform_device_create(np, "u3-i2c"); + break; + } + } + + return 0; +} + +device_initcall(pmac_declare_of_platform_devices); diff --git a/arch/ppc64/kernel/pmac_smp.c b/arch/ppc64/kernel/pmac_smp.c new file mode 100644 index 000000000000..4e21460baf3f --- /dev/null +++ b/arch/ppc64/kernel/pmac_smp.c @@ -0,0 +1,174 @@ +/* + * SMP support for power macintosh. + * + * We support both the old "powersurge" SMP architecture + * and the current Core99 (G4 PowerMac) machines. + * + * Note that we don't support the very first rev. of + * Apple/DayStar 2 CPUs board, the one with the funky + * watchdog. Hopefully, none of these should be there except + * maybe internally to Apple. I should probably still add some + * code to detect this card though and disable SMP. --BenH. + * + * Support Macintosh G4 SMP by Troy Benjegerdes (hozer@drgw.net) + * and Ben Herrenschmidt . + * + * Support for DayStar quad CPU cards + * Copyright (C) XLR8, Inc. 1994-2000 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "open_pic.h" + +extern void pmac_secondary_start_1(void); +extern void pmac_secondary_start_2(void); +extern void pmac_secondary_start_3(void); + +extern void smp_openpic_message_pass(int target, int msg, unsigned long data, int wait); + +extern struct smp_ops_t *smp_ops; + +static int __init smp_core99_probe(void) +{ + struct device_node *cpus; + int ncpus = 1; + + /* Maybe use systemconfiguration here ? */ + if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345); + cpus = find_type_devices("cpu"); + if (cpus == NULL) + return 0; + + while ((cpus = cpus->next) != NULL) + ++ncpus; + + printk(KERN_INFO "PowerMac SMP probe found %d cpus\n", ncpus); + + if (ncpus > 1) + openpic_request_IPIs(); + + return ncpus; +} + +static void __init smp_core99_kick_cpu(int nr) +{ + int save_vector; + unsigned long new_vector; + unsigned long flags; + volatile unsigned int *vector + = ((volatile unsigned int *)(KERNELBASE+0x100)); + + if (nr < 1 || nr > 3) + return; + if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346); + + local_irq_save(flags); + local_irq_disable(); + + /* Save reset vector */ + save_vector = *vector; + + /* Setup fake reset vector that does + * b .pmac_secondary_start - KERNELBASE + */ + switch(nr) { + case 1: + new_vector = (unsigned long)pmac_secondary_start_1; + break; + case 2: + new_vector = (unsigned long)pmac_secondary_start_2; + break; + case 3: + new_vector = (unsigned long)pmac_secondary_start_3; + break; + } + *vector = 0x48000002 + (new_vector - KERNELBASE); + + /* flush data cache and inval instruction cache */ + flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); + + /* Put some life in our friend */ + pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0); + paca[nr].xProcStart = 1; + + /* FIXME: We wait a bit for the CPU to take the exception, I should + * instead wait for the entry code to set something for me. Well, + * ideally, all that crap will be done in prom.c and the CPU left + * in a RAM-based wait loop like CHRP. + */ + mdelay(1); + + /* Restore our exception vector */ + *vector = save_vector; + flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); + + local_irq_restore(flags); + if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347); +} + +static void __init smp_core99_setup_cpu(int cpu_nr) +{ + /* Setup openpic */ + do_openpic_setup_cpu(); + + if (cpu_nr == 0) { + extern void g5_phy_disable_cpu1(void); + + /* If we didn't start the second CPU, we must take + * it off the bus + */ + if (num_online_cpus() < 2) + g5_phy_disable_cpu1(); + if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349); + } +} + +extern void smp_generic_give_timebase(void); +extern void smp_generic_take_timebase(void); + +struct smp_ops_t core99_smp_ops __pmacdata = { + .message_pass = smp_openpic_message_pass, + .probe = smp_core99_probe, + .kick_cpu = smp_core99_kick_cpu, + .setup_cpu = smp_core99_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, +}; + +void __init pmac_setup_smp(void) +{ + smp_ops = &core99_smp_ops; +} diff --git a/arch/ppc64/kernel/pmac_time.c b/arch/ppc64/kernel/pmac_time.c new file mode 100644 index 000000000000..0681da70e81d --- /dev/null +++ b/arch/ppc64/kernel/pmac_time.c @@ -0,0 +1,157 @@ +/* + * Support for periodic interrupts (100 per second) and for getting + * the current time from the RTC on Power Macintoshes. + * + * We use the decrementer register for our periodic interrupts. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +extern void setup_default_decr(void); + +/* Apparently the RTC stores seconds since 1 Jan 1904 */ +#define RTC_OFFSET 2082844800 + +/* + * Calibrate the decrementer frequency with the VIA timer 1. + */ +#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */ + +extern struct timezone sys_tz; +extern void to_tm(int tim, struct rtc_time * tm); + +void __pmac pmac_get_rtc_time(struct rtc_time *tm) +{ + struct adb_request req; + unsigned int now; + + /* Get the time from the RTC */ + if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) + return; + while (!req.complete) + pmu_poll(); + if (req.reply_len != 4) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + now = (req.reply[0] << 24) + (req.reply[1] << 16) + + (req.reply[2] << 8) + req.reply[3]; + DBG("get: %u -> %u\n", (int)now, (int)(now - RTC_OFFSET)); + now -= RTC_OFFSET; + + to_tm(now, tm); + tm->tm_year -= 1900; + tm->tm_mon -= 1; + + DBG("-> tm_mday: %d, tm_mon: %d, tm_year: %d, %d:%02d:%02d\n", + tm->tm_mday, tm->tm_mon, tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +int __pmac pmac_set_rtc_time(struct rtc_time *tm) +{ + struct adb_request req; + unsigned int nowtime; + + DBG("set: tm_mday: %d, tm_mon: %d, tm_year: %d, %d:%02d:%02d\n", + tm->tm_mday, tm->tm_mon, tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + nowtime = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + DBG("-> %u -> %u\n", (int)nowtime, (int)(nowtime + RTC_OFFSET)); + nowtime += RTC_OFFSET; + + if (pmu_request(&req, NULL, 5, PMU_SET_RTC, + nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0) + return 0; + while (!req.complete) + pmu_poll(); + if (req.reply_len != 0) + printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n", + req.reply_len); + return 1; +} + +void __init pmac_get_boot_time(struct rtc_time *tm) +{ + pmac_get_rtc_time(tm); + +#ifdef disabled__CONFIG_NVRAM + s32 delta = 0; + int dst; + + delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16; + delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8; + delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb); + if (delta & 0x00800000UL) + delta |= 0xFF000000UL; + dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0); + printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60, + dst ? "on" : "off"); +#endif +} + +/* + * Query the OF and get the decr frequency. + * This was taken from the pmac time_init() when merging the prep/pmac + * time functions. + */ +void __init pmac_calibrate_decr(void) +{ + struct device_node *cpu; + unsigned int freq, *fp; + struct div_result divres; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + cpu = find_type_devices("cpu"); + if (cpu == 0) + panic("can't find cpu node in time_init"); + fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL); + if (fp == 0) + panic("can't get cpu timebase frequency"); + freq = *fp; + printk("time_init: decrementer frequency = %u.%.6u MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; + tb_ticks_per_usec = freq / 1000000; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres ); + tb_to_xs = divres.result_low; + + setup_default_decr(); +} + diff --git a/arch/ppc64/kernel/smp-tbsync.c b/arch/ppc64/kernel/smp-tbsync.c new file mode 100644 index 000000000000..7d8ec9996b3e --- /dev/null +++ b/arch/ppc64/kernel/smp-tbsync.c @@ -0,0 +1,179 @@ +/* + * Smp timebase synchronization for ppc. + * + * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_ITER 300 + +enum { + kExit=0, kSetAndTest, kTest +}; + +static struct { + volatile long tb; + volatile long mark; + volatile int cmd; + volatile int handshake; + int filler[3]; + + volatile int ack; + int filler2[7]; + + volatile int race_result; +} *tbsync; + +static volatile int running; + +static void __devinit +enter_contest( long mark, long add ) +{ + while( (long)(mftb() - mark) < 0 ) + tbsync->race_result = add; +} + +void __devinit +smp_generic_take_timebase( void ) +{ + int cmd; + long tb; + + local_irq_disable(); + while( !running ) + ; + rmb(); + + for( ;; ) { + tbsync->ack = 1; + while( !tbsync->handshake ) + ; + rmb(); + + cmd = tbsync->cmd; + tb = tbsync->tb; + tbsync->ack = 0; + if( cmd == kExit ) + return; + + if( cmd == kSetAndTest ) { + while( tbsync->handshake ) + ; + asm volatile ("mttbl %0" :: "r" (tb & 0xfffffffful) ); + asm volatile ("mttbu %0" :: "r" (tb >> 32) ); + } else { + while( tbsync->handshake ) + ; + } + enter_contest( tbsync->mark, -1 ); + } + local_irq_enable(); +} + +static int __devinit +start_contest( int cmd, long offset, long num ) +{ + int i, score=0; + long tb, mark; + + tbsync->cmd = cmd; + + local_irq_disable(); + for( i=-3; itb = tb + offset; + tbsync->mark = mark = tb + 400; + + wmb(); + + tbsync->handshake = 1; + while( tbsync->ack ) + ; + + while( (long)(mftb() - tb) <= 0 ) + ; + tbsync->handshake = 0; + enter_contest( mark, 1 ); + + while( !tbsync->ack ) + ; + + if ((tbsync->tb ^ (long)mftb()) & 0x8000000000000000ul) + continue; + if( i++ > 0 ) + score += tbsync->race_result; + } + local_irq_enable(); + return score; +} + +void __devinit +smp_generic_give_timebase( void ) +{ + int i, score, score2, old, min=0, max=5000, offset=1000; + + printk("Synchronizing timebase\n"); + + /* if this fails then this kernel won't work anyway... */ + tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL ); + memset( tbsync, 0, sizeof(*tbsync) ); + mb(); + running = 1; + + while( !tbsync->ack ) + ; + + printk("Got ack\n"); + + /* binary search */ + for( old=-1 ; old != offset ; offset=(min+max)/2 ) { + score = start_contest( kSetAndTest, offset, NUM_ITER ); + + printk("score %d, offset %d\n", score, offset ); + + if( score > 0 ) + max = offset; + else + min = offset; + old = offset; + } + score = start_contest( kSetAndTest, min, NUM_ITER ); + score2 = start_contest( kSetAndTest, max, NUM_ITER ); + + printk( "Min %d (score %d), Max %d (score %d)\n", min, score, max, score2 ); + score = abs( score ); + score2 = abs( score2 ); + offset = (score < score2) ? min : max; + + /* guard against inaccurate mttb */ + for( i=0; i<10; i++ ) { + start_contest( kSetAndTest, offset, NUM_ITER/10 ); + + if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 ) + score2 = -score2; + if( score2 <= score || score2 < 20 ) + break; + } + printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER ); + + /* exiting */ + tbsync->cmd = kExit; + wmb(); + tbsync->handshake = 1; + while( tbsync->ack ) + ; + tbsync->handshake = 0; + kfree( tbsync ); + tbsync = NULL; + running = 0; +} diff --git a/include/asm-ppc64/bootx.h b/include/asm-ppc64/bootx.h new file mode 100644 index 000000000000..b0c51b45d7a2 --- /dev/null +++ b/include/asm-ppc64/bootx.h @@ -0,0 +1,135 @@ +/* + * This file describes the structure passed from the BootX application + * (for MacOS) when it is used to boot Linux. + * + * Written by Benjamin Herrenschmidt. + */ + + +#ifndef __ASM_BOOTX_H__ +#define __ASM_BOOTX_H__ + +#ifdef macintosh +#include +#include "linux_type_defs.h" +#endif + +#ifdef macintosh +/* All this requires PowerPC alignment */ +#pragma options align=power +#endif + +/* On kernel entry: + * + * r3 = 0x426f6f58 ('BooX') + * r4 = pointer to boot_infos + * r5 = NULL + * + * Data and instruction translation disabled, interrupts + * disabled, kernel loaded at physical 0x00000000 on PCI + * machines (will be different on NuBus). + */ + +#define BOOT_INFO_VERSION 5 +#define BOOT_INFO_COMPATIBLE_VERSION 1 + +/* Bit in the architecture flag mask. More to be defined in + future versions. Note that either BOOT_ARCH_PCI or + BOOT_ARCH_NUBUS is set. The other BOOT_ARCH_NUBUS_xxx are + set additionally when BOOT_ARCH_NUBUS is set. + */ +#define BOOT_ARCH_PCI 0x00000001UL +#define BOOT_ARCH_NUBUS 0x00000002UL +#define BOOT_ARCH_NUBUS_PDM 0x00000010UL +#define BOOT_ARCH_NUBUS_PERFORMA 0x00000020UL +#define BOOT_ARCH_NUBUS_POWERBOOK 0x00000040UL + +/* Maximum number of ranges in phys memory map */ +#define MAX_MEM_MAP_SIZE 26 + +/* This is the format of an element in the physical memory map. Note that + the map is optional and current BootX will only build it for pre-PCI + machines */ +typedef struct boot_info_map_entry +{ + __u32 physAddr; /* Physical starting address */ + __u32 size; /* Size in bytes */ +} boot_info_map_entry_t; + + +/* Here are the boot informations that are passed to the bootstrap + * Note that the kernel arguments and the device tree are appended + * at the end of this structure. */ +typedef struct boot_infos +{ + /* Version of this structure */ + __u32 version; + /* backward compatible down to version: */ + __u32 compatible_version; + + /* NEW (vers. 2) this holds the current _logical_ base addr of + the frame buffer (for use by early boot message) */ + __u8* logicalDisplayBase; + + /* NEW (vers. 4) Apple's machine identification */ + __u32 machineID; + + /* NEW (vers. 4) Detected hw architecture */ + __u32 architecture; + + /* The device tree (internal addresses relative to the beginning of the tree, + * device tree offset relative to the beginning of this structure). + * On pre-PCI macintosh (BOOT_ARCH_PCI bit set to 0 in architecture), this + * field is 0. + */ + __u32 deviceTreeOffset; /* Device tree offset */ + __u32 deviceTreeSize; /* Size of the device tree */ + + /* Some infos about the current MacOS display */ + __u32 dispDeviceRect[4]; /* left,top,right,bottom */ + __u32 dispDeviceDepth; /* (8, 16 or 32) */ + __u8* dispDeviceBase; /* base address (physical) */ + __u32 dispDeviceRowBytes; /* rowbytes (in bytes) */ + __u32 dispDeviceColorsOffset; /* Colormap (8 bits only) or 0 (*) */ + /* Optional offset in the registry to the current + * MacOS display. (Can be 0 when not detected) */ + __u32 dispDeviceRegEntryOffset; + + /* Optional pointer to boot ramdisk (offset from this structure) */ + __u32 ramDisk; + __u32 ramDiskSize; /* size of ramdisk image */ + + /* Kernel command line arguments (offset from this structure) */ + __u32 kernelParamsOffset; + + /* ALL BELOW NEW (vers. 4) */ + + /* This defines the physical memory. Valid with BOOT_ARCH_NUBUS flag + (non-PCI) only. On PCI, memory is contiguous and it's size is in the + device-tree. */ + boot_info_map_entry_t + physMemoryMap[MAX_MEM_MAP_SIZE]; /* Where the phys memory is */ + __u32 physMemoryMapSize; /* How many entries in map */ + + + /* The framebuffer size (optional, currently 0) */ + __u32 frameBufferSize; /* Represents a max size, can be 0. */ + + /* NEW (vers. 5) */ + + /* Total params size (args + colormap + device tree + ramdisk) */ + __u32 totalParamsSize; + +} boot_infos_t; + +/* (*) The format of the colormap is 256 * 3 * 2 bytes. Each color index is represented + * by 3 short words containing a 16 bits (unsigned) color component. + * Later versions may contain the gamma table for direct-color devices here. + */ +#define BOOTX_COLORTABLE_SIZE (256UL*3UL*2UL) + +#ifdef macintosh +#pragma options align=reset +#endif + +#endif diff --git a/include/asm-ppc64/btext.h b/include/asm-ppc64/btext.h new file mode 100644 index 000000000000..ba6b5b3ae98a --- /dev/null +++ b/include/asm-ppc64/btext.h @@ -0,0 +1,29 @@ +/* + * Definitions for using the procedures in btext.c. + * + * Benjamin Herrenschmidt + */ +#ifndef __PPC_BTEXT_H +#define __PPC_BTEXT_H +#ifdef __KERNEL__ + +#include + +extern void btext_clearscreen(void); +extern void btext_flushscreen(void); + +extern boot_infos_t disp_bi; +extern int boot_text_mapped; + +void btext_setup_display(int width, int height, int depth, int pitch, + unsigned long address); +void map_boot_text(void); +void btext_update_display(unsigned long phys, int width, int height, + int depth, int pitch); + +void btext_drawchar(char c); +void btext_drawstring(const char *str); +void btext_drawhex(unsigned long v); + +#endif /* __KERNEL__ */ +#endif /* __PPC_BTEXT_H */ diff --git a/include/asm-ppc64/dbdma.h b/include/asm-ppc64/dbdma.h new file mode 100644 index 000000000000..f2d5d5dc3377 --- /dev/null +++ b/include/asm-ppc64/dbdma.h @@ -0,0 +1,2 @@ +#include + diff --git a/include/asm-ppc64/keylargo.h b/include/asm-ppc64/keylargo.h new file mode 100644 index 000000000000..4d78e3d0314c --- /dev/null +++ b/include/asm-ppc64/keylargo.h @@ -0,0 +1,2 @@ +#include + diff --git a/include/asm-ppc64/macio.h b/include/asm-ppc64/macio.h new file mode 100644 index 000000000000..a3028b364f70 --- /dev/null +++ b/include/asm-ppc64/macio.h @@ -0,0 +1,2 @@ +#include + diff --git a/include/asm-ppc64/of_device.h b/include/asm-ppc64/of_device.h new file mode 100644 index 000000000000..7bc136e22590 --- /dev/null +++ b/include/asm-ppc64/of_device.h @@ -0,0 +1,2 @@ +#include + diff --git a/include/asm-ppc64/offsets.h b/include/asm-ppc64/offsets.h new file mode 100644 index 000000000000..6dc66c6c2244 --- /dev/null +++ b/include/asm-ppc64/offsets.h @@ -0,0 +1,110 @@ +#ifndef __ASM_OFFSETS_H__ +#define __ASM_OFFSETS_H__ +/* + * DO NOT MODIFY. + * + * This file was generated by arch/ppc64/Makefile + * + */ + +#define THREAD_SHIFT 14 /* THREAD_SHIFT */ +#define THREAD_SIZE 16384 /* THREAD_SIZE */ +#define TI_FLAGS 16 /* offsetof(struct thread_info, flags) */ +#define THREAD 1328 /* offsetof(struct task_struct, thread) */ +#define PT_REGS 8 /* offsetof(struct thread_struct, regs) */ +#define THREAD_FPEXC_MODE 288 /* offsetof(struct thread_struct, fpexc_mode) */ +#define THREAD_FPR0 24 /* offsetof(struct thread_struct, fpr[0]) */ +#define THREAD_FPSCR 280 /* offsetof(struct thread_struct, fpscr) */ +#define KSP 0 /* offsetof(struct thread_struct, ksp) */ +#define THREAD_VR0 320 /* offsetof(struct thread_struct, vr[0]) */ +#define THREAD_VRSAVE 848 /* offsetof(struct thread_struct, vrsave) */ +#define THREAD_VSCR 832 /* offsetof(struct thread_struct, vscr) */ +#define THREAD_USED_VR 856 /* offsetof(struct thread_struct, used_vr) */ +#define MM 184 /* offsetof(struct task_struct, mm) */ +#define PACA 24 /* offsetof(struct naca_struct, paca) */ +#define DCACHEL1LINESIZE 100 /* offsetof(struct systemcfg, dCacheL1LineSize) */ +#define DCACHEL1LOGLINESIZE 96 /* offsetof(struct naca_struct, dCacheL1LogLineSize) */ +#define DCACHEL1LINESPERPAGE 100 /* offsetof(struct naca_struct, dCacheL1LinesPerPage) */ +#define ICACHEL1LINESIZE 108 /* offsetof(struct systemcfg, iCacheL1LineSize) */ +#define ICACHEL1LOGLINESIZE 104 /* offsetof(struct naca_struct, iCacheL1LogLineSize) */ +#define ICACHEL1LINESPERPAGE 108 /* offsetof(struct naca_struct, iCacheL1LinesPerPage) */ +#define SLBSIZE 72 /* offsetof(struct naca_struct, slb_size) */ +#define PLATFORM 24 /* offsetof(struct systemcfg, platform) */ +#define PACA_SIZE 8192 /* sizeof(struct paca_struct) */ +#define PACAPACAINDEX 24 /* offsetof(struct paca_struct, xPacaIndex) */ +#define PACAPROCSTART 256 /* offsetof(struct paca_struct, xProcStart) */ +#define PACAKSAVE 32 /* offsetof(struct paca_struct, xKsave) */ +#define PACACURRENT 16 /* offsetof(struct paca_struct, xCurrent) */ +#define PACASAVEDMSR 2144 /* offsetof(struct paca_struct, xSavedMsr) */ +#define PACASTABREAL 56 /* offsetof(struct paca_struct, xStab_data.real) */ +#define PACASTABVIRT 64 /* offsetof(struct paca_struct, xStab_data.virt) */ +#define PACASTABRR 72 /* offsetof(struct paca_struct, xStab_data.next_round_robin) */ +#define PACAR1 2136 /* offsetof(struct paca_struct, xR1) */ +#define PACALPQUEUE 40 /* offsetof(struct paca_struct, lpQueuePtr) */ +#define PACATOC 48 /* offsetof(struct paca_struct, xTOC) */ +#define PACAEXCSP 80 /* offsetof(struct paca_struct, exception_sp) */ +#define PACAPROCENABLED 88 /* offsetof(struct paca_struct, xProcEnabled) */ +#define PACADEFAULTDECR 28 /* offsetof(struct paca_struct, default_decr) */ +#define PACAPROFENABLED 89 /* offsetof(struct paca_struct, prof_enabled) */ +#define PACAPROFLEN 200 /* offsetof(struct paca_struct, prof_len) */ +#define PACAPROFSHIFT 180 /* offsetof(struct paca_struct, prof_shift) */ +#define PACAPROFBUFFER 184 /* offsetof(struct paca_struct, prof_buffer) */ +#define PACAPROFSTEXT 192 /* offsetof(struct paca_struct, prof_stext) */ +#define PACALPPACA 384 /* offsetof(struct paca_struct, xLpPaca) */ +#define LPPACA 384 /* offsetof(struct paca_struct, xLpPaca) */ +#define PACAREGSAV 1024 /* offsetof(struct paca_struct, xRegSav) */ +#define PACAEXC 3840 /* offsetof(struct paca_struct, exception_stack) */ +#define PACAGUARD 4096 /* offsetof(struct paca_struct, guard) */ +#define LPPACASRR0 144 /* offsetof(struct ItLpPaca, xSavedSrr0) */ +#define LPPACASRR1 152 /* offsetof(struct ItLpPaca, xSavedSrr1) */ +#define LPPACAANYINT 128 /* offsetof(struct ItLpPaca, xIntDword.xAnyInt) */ +#define LPPACADECRINT 132 /* offsetof(struct ItLpPaca, xIntDword.xFields.xDecrInt) */ +#define LPQCUREVENTPTR 16 /* offsetof(struct ItLpQueue, xSlicCurEventPtr) */ +#define LPQOVERFLOW 0 /* offsetof(struct ItLpQueue, xPlicOverflowIntPending) */ +#define LPEVENTFLAGS 0 /* offsetof(struct HvLpEvent, xFlags) */ +#define PROMENTRY 0 /* offsetof(struct prom_t, entry) */ +#define RTASBASE 8 /* offsetof(struct rtas_t, base) */ +#define RTASENTRY 0 /* offsetof(struct rtas_t, entry) */ +#define RTASSIZE 16 /* offsetof(struct rtas_t, size) */ +#define STACK_FRAME_OVERHEAD 112 /* STACK_FRAME_OVERHEAD */ +#define SWITCH_FRAME_SIZE 464 /* STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) */ +#define INT_FRAME_SIZE 752 /* STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 288 */ +#define PROM_FRAME_SIZE 480 /* STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16 */ +#define RTAS_FRAME_SIZE 480 /* STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16 */ +#define GPR0 112 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0]) */ +#define GPR1 120 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1]) */ +#define GPR2 128 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[2]) */ +#define GPR3 136 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[3]) */ +#define GPR4 144 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[4]) */ +#define GPR5 152 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[5]) */ +#define GPR6 160 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[6]) */ +#define GPR7 168 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[7]) */ +#define GPR8 176 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[8]) */ +#define GPR9 184 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[9]) */ +#define GPR20 272 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[20]) */ +#define GPR21 280 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[21]) */ +#define GPR22 288 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[22]) */ +#define GPR23 296 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[23]) */ +#define _NIP 368 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, nip) */ +#define _MSR 376 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, msr) */ +#define _CTR 392 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ctr) */ +#define _LINK 400 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, link) */ +#define _CCR 416 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ccr) */ +#define _XER 408 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer) */ +#define _DAR 440 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar) */ +#define _DSISR 448 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr) */ +#define ORIG_GPR3 384 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3) */ +#define RESULT 456 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result) */ +#define TRAP 432 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap) */ +#define SOFTE 424 /* STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, softe) */ +#define _SRR0 464 /* STACK_FRAME_OVERHEAD+sizeof(struct pt_regs) */ +#define _SRR1 472 /* STACK_FRAME_OVERHEAD+sizeof(struct pt_regs)+8 */ +#define CLONE_VM 256 /* CLONE_VM */ +#define CLONE_UNTRACED 8388608 /* CLONE_UNTRACED */ +#define CPU_SPEC_ENTRY_SIZE 56 /* sizeof(struct cpu_spec) */ +#define CPU_SPEC_PVR_MASK 0 /* offsetof(struct cpu_spec, pvr_mask) */ +#define CPU_SPEC_PVR_VALUE 4 /* offsetof(struct cpu_spec, pvr_value) */ +#define CPU_SPEC_FEATURES 16 /* offsetof(struct cpu_spec, cpu_features) */ +#define CPU_SPEC_SETUP 40 /* offsetof(struct cpu_spec, cpu_setup) */ + +#endif diff --git a/include/asm-ppc64/pmac_feature.h b/include/asm-ppc64/pmac_feature.h new file mode 100644 index 000000000000..e07e36c4cbb2 --- /dev/null +++ b/include/asm-ppc64/pmac_feature.h @@ -0,0 +1,2 @@ +#include + diff --git a/include/asm-ppc64/pmac_low_i2c.h b/include/asm-ppc64/pmac_low_i2c.h new file mode 100644 index 000000000000..7bcfc72c5c8a --- /dev/null +++ b/include/asm-ppc64/pmac_low_i2c.h @@ -0,0 +1,2 @@ +#include + diff --git a/include/asm-ppc64/uninorth.h b/include/asm-ppc64/uninorth.h new file mode 100644 index 000000000000..7ad7059f2c80 --- /dev/null +++ b/include/asm-ppc64/uninorth.h @@ -0,0 +1,2 @@ +#include + diff --git a/include/asm-ppc64/xmon.h b/include/asm-ppc64/xmon.h new file mode 100644 index 000000000000..042b83e6680d --- /dev/null +++ b/include/asm-ppc64/xmon.h @@ -0,0 +1,17 @@ +#ifndef __PPC_XMON_H +#define __PPC_XMON_H +#ifdef __KERNEL__ + +struct pt_regs; + +extern void xmon(struct pt_regs *excp); +extern void xmon_printf(const char *fmt, ...); +extern void xmon_map_scc(void); +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); + +#endif +#endif -- cgit v1.2.3 From a06a1a1b1da7d4ad212ae28623621363d1b97253 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 12:57:25 +1100 Subject: ppc64: Add some definitions relative to the G5 CPU and POWERMAC platform --- arch/ppc64/kernel/smp.c | 7 ------- include/asm-ppc64/processor.h | 20 +++++++++++++++++++- include/asm-ppc64/sections.h | 12 ++++++++++++ include/asm-ppc64/systemcfg.h | 6 ++++++ 4 files changed, 37 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/ppc64/kernel/smp.c b/arch/ppc64/kernel/smp.c index 2b2d286fec65..101441d0adc7 100644 --- a/arch/ppc64/kernel/smp.c +++ b/arch/ppc64/kernel/smp.c @@ -76,13 +76,6 @@ extern long register_vpa(unsigned long flags, unsigned long proc, #define smp_message_pass(t,m,d,w) smp_ops->message_pass((t),(m),(d),(w)) -static inline void set_tb(unsigned int upper, unsigned int lower) -{ - mttbl(0); - mttbu(upper); - mttbl(lower); -} - #ifdef CONFIG_PPC_ISERIES static unsigned long iSeries_smp_message[NR_CPUS]; diff --git a/include/asm-ppc64/processor.h b/include/asm-ppc64/processor.h index ffb684f7ab71..1bde268f4d85 100644 --- a/include/asm-ppc64/processor.h +++ b/include/asm-ppc64/processor.h @@ -216,8 +216,11 @@ #define HID0_BHTE (1<<2) /* Branch History Table Enable */ #define HID0_BTCD (1<<1) /* Branch target cache disable */ #define SPRN_MSRDORM 0x3F1 /* Hardware Implementation Register 1 */ +#define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */ #define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ #define SPRN_NIADORM 0x3F3 /* Hardware Implementation Register 2 */ +#define SPRN_HID4 0x3F4 /* 970 HID4 */ +#define SPRN_HID5 0x3F6 /* 970 HID5 */ #define SPRN_TSC 0x3FD /* Thread switch control */ #define SPRN_TST 0x3FC /* Thread switch timeout */ #define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */ @@ -263,6 +266,7 @@ #define SPRN_TBRU 0x10D /* Time Base Read Upper Register (user, R/O) */ #define SPRN_TBWL 0x11C /* Time Base Lower Register (super, W/O) */ #define SPRN_TBWU 0x11D /* Time Base Write Upper Register (super, W/O) */ +#define SPRN_HIOR 0x137 /* 970 Hypervisor interrupt offset */ #define SPRN_TCR 0x3DA /* Timer Control Register */ #define TCR_WP(x) (((x)&0x3)<<30) /* WDT Period */ #define WP_2_17 0 /* 2^17 clocks */ @@ -373,6 +377,7 @@ #define PV_ICESTAR 0x0036 #define PV_SSTAR 0x0037 #define PV_POWER4p 0x0038 +#define PV_GPUL 0x0039 #define PV_POWER5 0x003A #define PV_630 0x0040 #define PV_630p 0x0041 @@ -382,7 +387,12 @@ #define PLATFORM_PSERIES_LPAR 0x0101 #define PLATFORM_ISERIES_LPAR 0x0201 #define PLATFORM_LPAR 0x0001 - +#define PLATFORM_POWERMAC 0x0400 + +/* Compatibility with drivers coming from PPC32 world */ +#define _machine (systemcfg->platform) +#define _MACH_Pmac PLATFORM_POWERMAC + /* * List of interrupt controllers. */ @@ -457,6 +467,14 @@ GLUE(.,name): asm volatile("mfasr %0" : "=r" (rval)); rval;}) #ifndef __ASSEMBLY__ + +static inline void set_tb(unsigned int upper, unsigned int lower) +{ + mttbl(0); + mttbu(upper); + mttbl(lower); +} + extern unsigned long *_get_SP(void); extern int have_of; diff --git a/include/asm-ppc64/sections.h b/include/asm-ppc64/sections.h index b74722491931..95bac41d79b8 100644 --- a/include/asm-ppc64/sections.h +++ b/include/asm-ppc64/sections.h @@ -5,4 +5,16 @@ extern char _end[]; #include +#define __pmac +#define __pmacdata + +#define __prep +#define __prepdata + +#define __chrp +#define __chrpdata + +#define __openfirmware +#define __openfirmwaredata + #endif diff --git a/include/asm-ppc64/systemcfg.h b/include/asm-ppc64/systemcfg.h index b78c5a2828ca..72b5adba9325 100644 --- a/include/asm-ppc64/systemcfg.h +++ b/include/asm-ppc64/systemcfg.h @@ -72,6 +72,7 @@ extern struct systemcfg *systemcfg; #define PV_ICESTAR 0x0036 #define PV_SSTAR 0x0037 #define PV_POWER4p 0x0038 +#define PV_GPUL 0x0039 #define PV_630 0x0040 #define PV_630p 0x0041 @@ -79,6 +80,11 @@ extern struct systemcfg *systemcfg; #define PLATFORM_PSERIES 0x0100 #define PLATFORM_PSERIES_LPAR 0x0101 #define PLATFORM_ISERIES_LPAR 0x0201 +#define PLATFORM_POWERMAC 0x0400 + +/* Compatibility with drivers coming from PPC32 world */ +#define _machine (systemcfg->platform) +#define _MACH_Pmac PLATFORM_POWERMAC static inline volatile struct systemcfg *systemcfg_init(void) -- cgit v1.2.3 From 94ab33485516530384ea6e2edab98cef23075132 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 13:08:25 +1100 Subject: ppc64: Add support for PowerMacs in the OF client interface code (prom.c) Also move some of the init cruft into separate function to make things slightly more readable. We sill need to significantly cleanup that file, but that will come later... Properly export the OF device-tree accessors to modules --- arch/ppc64/kernel/prom.c | 454 +++++++++++++++++++++++++++++++++++++++-------- include/asm-ppc64/lmb.h | 5 +- include/asm-ppc64/prom.h | 7 + 3 files changed, 386 insertions(+), 80 deletions(-) (limited to 'include') diff --git a/arch/ppc64/kernel/prom.c b/arch/ppc64/kernel/prom.c index 8c68908d97e2..437b6a8b0790 100644 --- a/arch/ppc64/kernel/prom.c +++ b/arch/ppc64/kernel/prom.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include #include "open_pic.h" @@ -115,24 +117,15 @@ typedef unsigned long interpret_func(struct device_node *, unsigned long, static interpret_func interpret_pci_props; static interpret_func interpret_isa_props; static interpret_func interpret_root_props; +static interpret_func interpret_dbdma_props; +static interpret_func interpret_macio_props; #ifndef FB_MAX /* avoid pulling in all of the fb stuff */ #define FB_MAX 8 #endif - -struct prom_t prom = { - 0, /* entry */ - 0, /* chosen */ - 0, /* cpu */ - 0, /* stdout */ - 0, /* disp_node */ - {0,0,0,{0},NULL}, /* args */ - 0, /* version */ - 32, /* encode_phys_size */ - 0 /* bi_rec pointer */ -}; - +/* prom structure */ +struct prom_t prom; char *prom_display_paths[FB_MAX] __initdata = { 0, }; unsigned int prom_num_displays = 0; @@ -140,7 +133,6 @@ char *of_stdout_device = 0; extern struct rtas_t rtas; extern unsigned long klimit; -extern unsigned long embedded_sysmap_end; extern struct lmb lmb; #define MAX_PHB 16 * 3 // 16 Towers * 3 PHBs/tower @@ -343,9 +335,14 @@ prom_initialize_naca(unsigned long mem) RELOC("d-cache-size"), &size, sizeof(size)); - call_prom(RELOC("getprop"), 4, 1, node, - RELOC("d-cache-line-size"), - &lsize, sizeof(lsize)); + if (_systemcfg->platform == PLATFORM_POWERMAC) + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("d-cache-block-size"), + &lsize, sizeof(lsize)); + else + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("d-cache-line-size"), + &lsize, sizeof(lsize)); _systemcfg->dCacheL1Size = size; _systemcfg->dCacheL1LineSize = lsize; @@ -356,9 +353,14 @@ prom_initialize_naca(unsigned long mem) RELOC("i-cache-size"), &size, sizeof(size)); - call_prom(RELOC("getprop"), 4, 1, node, - RELOC("i-cache-line-size"), - &lsize, sizeof(lsize)); + if (_systemcfg->platform == PLATFORM_POWERMAC) + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("i-cache-block-size"), + &lsize, sizeof(lsize)); + else + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("i-cache-line-size"), + &lsize, sizeof(lsize)); _systemcfg->iCacheL1Size = size; _systemcfg->iCacheL1LineSize = lsize; @@ -379,6 +381,8 @@ prom_initialize_naca(unsigned long mem) struct isa_reg_property reg; union pci_range ranges; + if (_systemcfg->platform == PLATFORM_POWERMAC) + continue; type[0] = 0; call_prom(RELOC("getprop"), 4, 1, node, RELOC("ibm,aix-loc"), type, sizeof(type)); @@ -409,24 +413,27 @@ prom_initialize_naca(unsigned long mem) } } - _naca->interrupt_controller = IC_INVALID; - for (node = 0; prom_next_node(&node); ) { - type[0] = 0; - call_prom(RELOC("getprop"), 4, 1, node, RELOC("name"), - type, sizeof(type)); - if (strcmp(type, RELOC("interrupt-controller"))) { - continue; - } - call_prom(RELOC("getprop"), 4, 1, node, RELOC("compatible"), - type, sizeof(type)); - if (strstr(type, RELOC("open-pic"))) { - _naca->interrupt_controller = IC_OPEN_PIC; - } else if (strstr(type, RELOC("ppc-xicp"))) { - _naca->interrupt_controller = IC_PPC_XIC; - } else { - prom_print(RELOC("prom: failed to recognize interrupt-controller\n")); + if (_systemcfg->platform == PLATFORM_POWERMAC) + _naca->interrupt_controller = IC_OPEN_PIC; + else { + _naca->interrupt_controller = IC_INVALID; + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("name"), + type, sizeof(type)); + if (strcmp(type, RELOC("interrupt-controller"))) + continue; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("compatible"), + type, sizeof(type)); + if (strstr(type, RELOC("open-pic"))) + _naca->interrupt_controller = IC_OPEN_PIC; + else if (strstr(type, RELOC("ppc-xicp"))) + _naca->interrupt_controller = IC_PPC_XIC; + else + prom_print(RELOC("prom: failed to recognize" + " interrupt-controller\n")); + break; } - break; } if (_naca->interrupt_controller == IC_INVALID) { @@ -440,7 +447,8 @@ prom_initialize_naca(unsigned long mem) _systemcfg->physicalMemorySize = lmb_phys_mem_size(); - if (_systemcfg->platform == PLATFORM_PSERIES) { + if (_systemcfg->platform == PLATFORM_PSERIES || + _systemcfg->platform == PLATFORM_POWERMAC) { unsigned long rnd_mem_size, pteg_count; /* round mem_size up to next power of 2 */ @@ -519,12 +527,19 @@ prom_initialize_lmb(unsigned long mem) char type[64]; unsigned long i, offset = reloc_offset(); struct prom_t *_prom = PTRRELOC(&prom); + struct systemcfg *_systemcfg = RELOC(systemcfg); union lmb_reg_property reg; unsigned long lmb_base, lmb_size; unsigned long num_regs, bytes_per_reg = (_prom->encode_phys_size*2)/8; lmb_init(); + /* XXX Quick HACK. Proper fix is to drop those structures and properly use + * #address-cells. PowerMac has #size-cell set to 1 and #address-cells to 2 + */ + if (_systemcfg->platform == PLATFORM_POWERMAC) + bytes_per_reg = 12; + for (node = 0; prom_next_node(&node); ) { type[0] = 0; call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), @@ -537,7 +552,15 @@ prom_initialize_lmb(unsigned long mem) ®, sizeof(reg)) / bytes_per_reg; for (i=0; i < num_regs ;i++) { - if (_prom->encode_phys_size == 32) { + if (_systemcfg->platform == PLATFORM_POWERMAC) { + lmb_base = ((unsigned long)reg.addrPM[i].address_hi) << 32; + lmb_base |= (unsigned long)reg.addrPM[i].address_lo; + lmb_size = reg.addrPM[i].size; + if (lmb_base > 0x80000000ull) { + prom_print(RELOC("Skipping memory above 2Gb for now, not yet supported\n")); + continue; + } + } else if (_prom->encode_phys_size == 32) { lmb_base = reg.addr32[i].address; lmb_size = reg.addr32[i].size; } else { @@ -922,19 +945,47 @@ prom_hold_cpus(unsigned long mem) struct paca_struct *_xPaca = PTRRELOC(&paca[0]); struct prom_t *_prom = PTRRELOC(&prom); + /* On pmac, we just fill out the various global bitmasks and + * arrays indicating our CPUs are here, they are actually started + * later on from pmac_smp + */ + if (_systemcfg->platform == PLATFORM_POWERMAC) { + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + reg = -1; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)); + _xPaca[cpuid].xHwProcNum = reg; + +#ifdef CONFIG_SMP + cpu_set(cpuid, RELOC(cpu_available_map)); + cpu_set(cpuid, RELOC(cpu_possible_map)); + cpu_set(cpuid, RELOC(cpu_present_at_boot)); + if (reg == 0) + cpu_set(cpuid, RELOC(cpu_online_map)); +#endif /* CONFIG_SMP */ + cpuid++; + } + return; + } + /* Initially, we must have one active CPU. */ _systemcfg->processorCount = 1; #ifdef DEBUG_PROM prom_print(RELOC("prom_hold_cpus: start...\n")); prom_print(RELOC(" 1) spinloop = 0x")); - prom_print_hex(spinloop); + prom_print_hex((unsigned long)spinloop); prom_print_nl(); prom_print(RELOC(" 1) *spinloop = 0x")); prom_print_hex(*spinloop); prom_print_nl(); prom_print(RELOC(" 1) acknowledge = 0x")); - prom_print_hex(acknowledge); + prom_print_hex((unsigned long)acknowledge); prom_print_nl(); prom_print(RELOC(" 1) *acknowledge = 0x")); prom_print_hex(*acknowledge); @@ -1212,6 +1263,144 @@ smt_setup(void) _naca->smt_state = my_smt_enabled; } + +#ifdef CONFIG_BOOTX_TEXT + +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +static void __init setup_disp_fake_bi(ihandle dp) +{ + int width = 640, height = 480, depth = 8, pitch; + unsigned address; + struct pci_reg_property addrs[8]; + int i, naddrs; + char name[64]; + unsigned long offset = reloc_offset(); + char *getprop = RELOC("getprop"); + + prom_print(RELOC("Initializing fake screen: ")); + + memset(name, 0, sizeof(name)); + call_prom(getprop, 4, 1, dp, RELOC("name"), name, sizeof(name)); + name[sizeof(name)-1] = 0; + prom_print(name); + prom_print(RELOC("\n")); + call_prom(getprop, 4, 1, dp, RELOC("width"), &width, sizeof(width)); + call_prom(getprop, 4, 1, dp, RELOC("height"), &height, sizeof(height)); + call_prom(getprop, 4, 1, dp, RELOC("depth"), &depth, sizeof(depth)); + pitch = width * ((depth + 7) / 8); + call_prom(getprop, 4, 1, dp, RELOC("linebytes"), + &pitch, sizeof(pitch)); + if (pitch == 1) + pitch = 0x1000; /* for strange IBM display */ + address = 0; + + prom_print(RELOC("width ")); + prom_print_hex(width); + prom_print(RELOC(" height ")); + prom_print_hex(height); + prom_print(RELOC(" depth ")); + prom_print_hex(depth); + prom_print(RELOC(" linebytes ")); + prom_print_hex(pitch); + prom_print(RELOC("\n")); + + + call_prom(getprop, 4, 1, dp, RELOC("address"), + &address, sizeof(address)); + if (address == 0) { + /* look for an assigned address with a size of >= 1MB */ + naddrs = (int) call_prom(getprop, 4, 1, dp, + RELOC("assigned-addresses"), + addrs, sizeof(addrs)); + naddrs /= sizeof(struct pci_reg_property); + for (i = 0; i < naddrs; ++i) { + if (addrs[i].size_lo >= (1 << 20)) { + address = addrs[i].addr.a_lo; + /* use the BE aperture if possible */ + if (addrs[i].size_lo >= (16 << 20)) + address += (8 << 20); + break; + } + } + if (address == 0) { + prom_print(RELOC("Failed to get address of frame buffer\n")); + return; + } + } + btext_setup_display(width, height, depth, pitch, address); + prom_print(RELOC("Addr of fb: ")); + prom_print_hex(address); + prom_print_nl(); + RELOC(boot_text_mapped) = 0; +} +#endif /* CONFIG_BOOTX_TEXT */ + +static void __init prom_init_client_services(unsigned long pp) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + + /* Get a handle to the prom entry point before anything else */ + _prom->entry = pp; + + /* Init default value for phys size */ + _prom->encode_phys_size = 32; + + /* get a handle for the stdout device */ + _prom->chosen = (ihandle)call_prom(RELOC("finddevice"), 1, 1, + RELOC("/chosen")); + if ((long)_prom->chosen <= 0) + prom_panic(RELOC("cannot find chosen")); /* msg won't be printed :( */ + + /* get device tree root */ + _prom->root = (ihandle)call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + if ((long)_prom->root <= 0) + prom_panic(RELOC("cannot find device tree root")); /* msg won't be printed :( */ +} + +static void __init prom_init_stdout(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + u32 val; + + if ((long)call_prom(RELOC("getprop"), 4, 1, _prom->chosen, + RELOC("stdout"), &val, + sizeof(val)) <= 0) + prom_panic(RELOC("cannot find stdout")); + + _prom->stdout = (ihandle)(unsigned long)val; +} + +static int __init prom_find_machine_type(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + char compat[256]; + int len, i = 0; + + len = (int)(long)call_prom(RELOC("getprop"), 4, 1, _prom->root, + RELOC("compatible"), + compat, sizeof(compat)-1); + if (len > 0) { + compat[len] = 0; + while (i < len) { + char *p = &compat[i]; + int sl = strlen(p); + if (sl == 0) + break; + if (strstr(p, RELOC("Power Macintosh")) || + strstr(p, RELOC("MacRISC4"))) + return PLATFORM_POWERMAC; + i += sl + 1; + } + } + /* Default to pSeries */ + return PLATFORM_PSERIES; +} + /* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. @@ -1222,7 +1411,7 @@ prom_init(unsigned long r3, unsigned long r4, unsigned long pp, unsigned long r6, unsigned long r7) { unsigned long mem; - ihandle prom_root, prom_cpu; + ihandle prom_cpu; phandle cpu_pkg; unsigned long offset = reloc_offset(); long l; @@ -1241,39 +1430,27 @@ prom_init(unsigned long r3, unsigned long r4, unsigned long pp, RELOC(systemcfg) = _systemcfg = (struct systemcfg *)(SYSTEMCFG_VIRT_ADDR - offset); RELOC(naca) = (struct naca_struct *)(NACA_VIRT_ADDR - offset); - /* Default machine type. */ - _systemcfg->platform = PLATFORM_PSERIES; + /* Init interface to Open Firmware and pickup bi-recs */ + prom_init_client_services(pp); -#if 0 - /* Reset klimit to take into account the embedded system map */ - if (RELOC(embedded_sysmap_end)) - RELOC(klimit) = __va(PAGE_ALIGN(RELOC(embedded_sysmap_end))); -#endif + /* Init prom stdout device */ + prom_init_stdout(); - /* Get a handle to the prom entry point before anything else */ - _prom->entry = pp; + /* check out if we have bi_recs */ _prom->bi_recs = prom_bi_rec_verify((struct bi_record *)r6); - if ( _prom->bi_recs != NULL ) { - RELOC(klimit) = PTRUNRELOC((unsigned long)_prom->bi_recs + _prom->bi_recs->data[1]); - } + if ( _prom->bi_recs != NULL ) + RELOC(klimit) = PTRUNRELOC((unsigned long)_prom->bi_recs + + _prom->bi_recs->data[1]); - /* First get a handle for the stdout device */ - _prom->chosen = (ihandle)call_prom(RELOC("finddevice"), 1, 1, - RELOC("/chosen")); + /* Default machine type. */ + _systemcfg->platform = prom_find_machine_type(); - if ((long)_prom->chosen <= 0) - prom_panic(RELOC("cannot find chosen")); /* msg won't be printed :( */ /* On pSeries, copy the CPU hold code */ if (_systemcfg->platform == PLATFORM_PSERIES) copy_and_flush(0, KERNELBASE - offset, 0x100, 0); - if ((long)call_prom(RELOC("getprop"), 4, 1, _prom->chosen, - RELOC("stdout"), &getprop_rval, - sizeof(getprop_rval)) <= 0) - prom_panic(RELOC("cannot find stdout")); - - _prom->stdout = (ihandle)(unsigned long)getprop_rval; + /* Start storing things at klimit */ mem = RELOC(klimit) - offset; /* Get the full OF pathname of the stdout device */ @@ -1284,13 +1461,10 @@ prom_init(unsigned long r3, unsigned long r4, unsigned long pp, mem += strlen(p) + 1; getprop_rval = 1; - prom_root = (ihandle)call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); - if (prom_root != (ihandle)-1) { - call_prom(RELOC("getprop"), 4, 1, - prom_root, RELOC("#size-cells"), - &getprop_rval, sizeof(getprop_rval)); - } - _prom->encode_phys_size = (getprop_rval==1) ? 32 : 64; + call_prom(RELOC("getprop"), 4, 1, + _prom->root, RELOC("#size-cells"), + &getprop_rval, sizeof(getprop_rval)); + _prom->encode_phys_size = (getprop_rval == 1) ? 32 : 64; /* Determine which cpu is actually running right _now_ */ if ((long)call_prom(RELOC("getprop"), 4, 1, _prom->chosen, @@ -1329,13 +1503,24 @@ prom_init(unsigned long r3, unsigned long r4, unsigned long pp, mem = DOUBLEWORD_ALIGN(mem + strlen(d) + 1); } + RELOC(cmd_line[0]) = 0; + if ((long)_prom->chosen > 0) { + call_prom(RELOC("getprop"), 4, 1, _prom->chosen, + RELOC("bootargs"), p, sizeof(cmd_line)); + if (p != NULL && p[0] != 0) + strncpy(RELOC(cmd_line), p, sizeof(cmd_line)); + } + RELOC(cmd_line[sizeof(cmd_line) - 1]) = 0; + + mem = prom_initialize_lmb(mem); mem = prom_bi_rec_reserve(mem); mem = check_display(mem); - prom_instantiate_rtas(); + if (_systemcfg->platform != PLATFORM_POWERMAC) + prom_instantiate_rtas(); /* Initialize some system info into the Naca early... */ mem = prom_initialize_naca(mem); @@ -1346,7 +1531,7 @@ prom_init(unsigned long r3, unsigned long r4, unsigned long pp, * following, regardless of whether we have an SMP * kernel or not. */ - prom_hold_cpus(mem); + prom_hold_cpus(mem); #ifdef DEBUG_PROM prom_print(RELOC("copying OF device tree...\n")); @@ -1360,6 +1545,13 @@ prom_init(unsigned long r3, unsigned long r4, unsigned long pp, if (_systemcfg->platform == PLATFORM_PSERIES) prom_initialize_tce_table(); +#ifdef CONFIG_BOOTX_TEXT + if(_prom->disp_node) { + prom_print(RELOC("Setting up bi display...\n")); + setup_disp_fake_bi(_prom->disp_node); + } +#endif /* CONFIG_BOOTX_TEXT */ + prom_print(RELOC("Calling quiesce ...\n")); call_prom(RELOC("quiesce"), 0, 0); phys = KERNELBASE - offset; @@ -1476,6 +1668,10 @@ check_display(unsigned long mem) RELOC(prom_display_paths[i]) = PTRUNRELOC(path); if (RELOC(prom_num_displays) >= FB_MAX) break; + /* XXX Temporary workaround: only open the first display so we don't + * lose debug output + */ + break; } return DOUBLEWORD_ALIGN(mem); } @@ -1689,9 +1885,9 @@ finish_node(struct device_node *np, unsigned long mem_start, np->type = get_property(np, "device_type", 0); /* get the device addresses and interrupts */ - if (ifunc != NULL) { - mem_start = ifunc(np, mem_start, naddrc, nsizec); - } + if (ifunc != NULL) + mem_start = ifunc(np, mem_start, naddrc, nsizec); + mem_start = finish_node_interrupts(np, mem_start); /* Look for #address-cells and #size-cells properties. */ @@ -1705,7 +1901,6 @@ finish_node(struct device_node *np, unsigned long mem_start, /* the f50 sets the name to 'display' and 'compatible' to what we * expect for the name -- Cort */ - ifunc = NULL; if (!strcmp(np->name, "display")) np->name = get_property(np, "compatible", 0); @@ -1715,8 +1910,19 @@ finish_node(struct device_node *np, unsigned long mem_start, ifunc = NULL; else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) ifunc = interpret_pci_props; + else if (!strcmp(np->type, "dbdma")) + ifunc = interpret_dbdma_props; + else if (!strcmp(np->type, "mac-io") || ifunc == interpret_macio_props) + ifunc = interpret_macio_props; else if (!strcmp(np->type, "isa")) ifunc = interpret_isa_props; + else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3")) + ifunc = interpret_root_props; + else if (!((ifunc == interpret_dbdma_props + || ifunc == interpret_macio_props) + && (!strcmp(np->type, "escc") + || !strcmp(np->type, "media-bay")))) + ifunc = NULL; for (child = np->child; child != NULL; child = child->sibling) mem_start = finish_node(child, mem_start, ifunc, @@ -1893,6 +2099,12 @@ finish_node_interrupts(struct device_node *np, unsigned long mem_start) if (n <= 0) continue; np->intrs[i].line = openpic_to_irq(virt_irq_create_mapping(irq[0])); + /* We offset irq numbers for the u3 MPIC by 128 in PowerMac */ + if (systemcfg->platform == PLATFORM_POWERMAC && ic && ic->parent) { + char *name = get_property(ic->parent, "name", NULL); + if (name && !strcmp(name, "u3")) + np->intrs[i].line += 128; + } if (n > 1) np->intrs[i].sense = irq[1]; if (n > 2) { @@ -1964,6 +2176,78 @@ interpret_pci_props(struct device_node *np, unsigned long mem_start, return mem_start; } +static unsigned long __init +interpret_dbdma_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct reg_property32 *rp; + struct address_range *adr; + unsigned long base_address; + int i, l; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; + } + } + + rp = (struct reg_property32 *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property32)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property32)) >= 0) { + adr[i].space = 2; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + return mem_start; +} + +static unsigned long __init +interpret_macio_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct reg_property32 *rp; + struct address_range *adr; + unsigned long base_address; + int i, l; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; + } + } + + rp = (struct reg_property32 *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property32)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property32)) >= 0) { + adr[i].space = 2; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + return mem_start; +} + static unsigned long __init interpret_isa_props(struct device_node *np, unsigned long mem_start, int naddrc, int nsizec) @@ -2212,6 +2496,7 @@ struct device_node *of_find_node_by_name(struct device_node *from, read_unlock(&devtree_lock); return np; } +EXPORT_SYMBOL(of_find_node_by_name); /** * of_find_node_by_type - Find a node by its "device_type" property @@ -2275,6 +2560,7 @@ struct device_node *of_find_compatible_node(struct device_node *from, read_unlock(&devtree_lock); return np; } +EXPORT_SYMBOL(of_find_compatible_node); /** * of_find_node_by_path - Find a node matching a full OF path @@ -2295,6 +2581,7 @@ struct device_node *of_find_node_by_path(const char *path) read_unlock(&devtree_lock); return np; } +EXPORT_SYMBOL(of_find_node_by_path); /** * of_find_all_nodes - Get next node in global list @@ -2318,6 +2605,7 @@ struct device_node *of_find_all_nodes(struct device_node *prev) read_unlock(&devtree_lock); return np; } +EXPORT_SYMBOL(of_find_all_nodes); /** * of_get_parent - Get a node's parent if any @@ -2338,6 +2626,7 @@ struct device_node *of_get_parent(const struct device_node *node) read_unlock(&devtree_lock); return np; } +EXPORT_SYMBOL(of_get_parent); /** * of_get_next_child - Iterate a node childs @@ -2362,6 +2651,7 @@ struct device_node *of_get_next_child(const struct device_node *node, read_unlock(&devtree_lock); return next; } +EXPORT_SYMBOL(of_get_next_child); /** * of_node_get - Increment refcount of a node @@ -2378,6 +2668,7 @@ struct device_node *of_node_get(struct device_node *node) } return NULL; } +EXPORT_SYMBOL(of_node_get); /** * of_node_put - Decrement refcount of a node @@ -2401,6 +2692,7 @@ void of_node_put(struct device_node *node) else atomic_dec(&node->_users); } +EXPORT_SYMBOL(of_node_put); /** * of_node_cleanup - release a dynamically allocated node @@ -2658,6 +2950,12 @@ static int of_finish_dynamic_node(struct device_node *node) goto out; } + /* We don't support that function on PowerMac, at least + * not yet + */ + if (systemcfg->platform == PLATFORM_POWERMAC) + return -ENODEV; + /* do the work of interpret_pci_props */ if (parent->type && !strcmp(parent->type, "pci")) { struct address_range *adr; diff --git a/include/asm-ppc64/lmb.h b/include/asm-ppc64/lmb.h index af8edcacd3c9..aecc2f36c6b4 100644 --- a/include/asm-ppc64/lmb.h +++ b/include/asm-ppc64/lmb.h @@ -21,8 +21,9 @@ extern unsigned long reloc_offset(void); #define MAX_LMB_REGIONS 64 union lmb_reg_property { - struct reg_property32 addr32[MAX_LMB_REGIONS]; - struct reg_property64 addr64[MAX_LMB_REGIONS]; + struct reg_property32 addr32[MAX_LMB_REGIONS]; + struct reg_property64 addr64[MAX_LMB_REGIONS]; + struct reg_property_pmac addrPM[MAX_LMB_REGIONS]; }; #define LMB_MEMORY_AREA 1 diff --git a/include/asm-ppc64/prom.h b/include/asm-ppc64/prom.h index 988f5a272be4..f54c47efde42 100644 --- a/include/asm-ppc64/prom.h +++ b/include/asm-ppc64/prom.h @@ -108,6 +108,12 @@ struct reg_property64 { unsigned long size; }; +struct reg_property_pmac { + unsigned int address_hi; + unsigned int address_lo; + unsigned int size; +}; + struct translation_property { unsigned long virt; unsigned long size; @@ -207,6 +213,7 @@ struct prom_args { struct prom_t { unsigned long entry; + ihandle root; ihandle chosen; int cpu; ihandle stdout; -- cgit v1.2.3 From d93aef751a6b0e1a962044538d3b4d5fc0b991be Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 13:10:38 +1100 Subject: ppc32: Separate definitions for known vs unknown PowerMac G5 models --- arch/ppc/platforms/pmac_feature.c | 2 +- include/asm-ppc/pmac_feature.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/ppc/platforms/pmac_feature.c b/arch/ppc/platforms/pmac_feature.c index cde0f17a0b2e..2b9fbb67ed5a 100644 --- a/arch/ppc/platforms/pmac_feature.c +++ b/arch/ppc/platforms/pmac_feature.c @@ -2239,7 +2239,7 @@ probe_motherboard(void) break; #else /* CONFIG_POWER4 */ case macio_keylargo2: - pmac_mb.model_id = PMAC_TYPE_POWERMAC_G5; + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_K2; pmac_mb.model_name = "Unknown G5"; pmac_mb.features = g5_features; break; diff --git a/include/asm-ppc/pmac_feature.h b/include/asm-ppc/pmac_feature.h index 83cc2ea442c4..11065e368382 100644 --- a/include/asm-ppc/pmac_feature.h +++ b/include/asm-ppc/pmac_feature.h @@ -115,6 +115,7 @@ /* MacRISC4 / G5 machines */ #define PMAC_TYPE_POWERMAC_G5 0x150 /* First tower */ +#define PMAC_TYPE_UNKNOWN_K2 0x19f /* Any other K2 based */ /* * Motherboard flags -- cgit v1.2.3 From aeadc4a594124d4053161fc66672a0e62fb62c69 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 13:14:11 +1100 Subject: ppc64: Update the nvram driver to deal with PowerMac G5 This involves making the actual read/write routines be indirect through ppc_md, and adding the various nvram partition types used on a PowerMac. --- arch/ppc64/kernel/chrp_setup.c | 7 +- arch/ppc64/kernel/nvram.c | 535 ++++++++++++++++++++--------------------- arch/ppc64/kernel/setup.c | 7 + include/asm-ppc64/machdep.h | 3 + include/asm-ppc64/nvram.h | 57 ++++- 5 files changed, 328 insertions(+), 281 deletions(-) (limited to 'include') diff --git a/arch/ppc64/kernel/chrp_setup.c b/arch/ppc64/kernel/chrp_setup.c index e3b91cdf2b08..2a0df2e5ba7f 100644 --- a/arch/ppc64/kernel/chrp_setup.c +++ b/arch/ppc64/kernel/chrp_setup.c @@ -178,6 +178,10 @@ chrp_setup_arch(void) #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; #endif + +#ifdef CONFIG_PPC_PSERIES + pSeries_nvram_init(); +#endif } void __init @@ -272,9 +276,6 @@ chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, ppc_md.progress = chrp_progress; - ppc_md.nvram_read = pSeries_nvram_read; - ppc_md.nvram_write = pSeries_nvram_write; - /* Build up the firmware_features bitmask field * using contents of device-tree/ibm,hypertas-functions. * Ultimately this functionality may be moved into prom.c prom_init(). diff --git a/arch/ppc64/kernel/nvram.c b/arch/ppc64/kernel/nvram.c index 07ddf0abc8f9..8f1bdf4f32eb 100644 --- a/arch/ppc64/kernel/nvram.c +++ b/arch/ppc64/kernel/nvram.c @@ -9,6 +9,10 @@ * /dev/nvram driver for PPC64 * * This perhaps should live in drivers/char + * + * TODO: Split the /dev/nvram part (that one can use + * drivers/char/generic_nvram.c) from the arch & partition + * parsing code. */ #include @@ -34,16 +38,10 @@ static int nvram_scan_partitions(void); static int nvram_setup_partition(void); static int nvram_create_os_partition(void); static int nvram_remove_os_partition(void); -static unsigned char nvram_checksum(struct nvram_header *p); -static int nvram_write_header(struct nvram_partition * part); -static unsigned int nvram_size; -static unsigned int nvram_fetch, nvram_store; -static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ static struct nvram_partition * nvram_part; static long nvram_error_log_index = -1; static long nvram_error_log_size = 0; -static spinlock_t nvram_lock = SPIN_LOCK_UNLOCKED; volatile int no_more_logging = 1; /* Until we initialize everything, * make sure we don't try logging @@ -58,12 +56,18 @@ struct err_log_info { static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin) { + int size; + + if (ppc_md.nvram_size == NULL) + return -ENODEV; + size = ppc_md.nvram_size(); + switch (origin) { case 1: offset += file->f_pos; break; case 2: - offset += nvram_size; + offset += size; break; } if (offset < 0) @@ -78,13 +82,18 @@ static ssize_t dev_nvram_read(struct file *file, char *buf, { ssize_t len; char *tmp_buffer; + int size; + + if (ppc_md.nvram_size == NULL) + return -ENODEV; + size = ppc_md.nvram_size(); if (verify_area(VERIFY_WRITE, buf, count)) return -EFAULT; - if (*ppos >= nvram_size) + if (*ppos >= size) return 0; - if (count > nvram_size) - count = nvram_size; + if (count > size) + count = size; tmp_buffer = (char *) kmalloc(count, GFP_KERNEL); if (!tmp_buffer) { @@ -113,13 +122,18 @@ static ssize_t dev_nvram_write(struct file *file, const char *buf, { ssize_t len; char * tmp_buffer; + int size; + + if (ppc_md.nvram_size == NULL) + return -ENODEV; + size = ppc_md.nvram_size(); if (verify_area(VERIFY_READ, buf, count)) return -EFAULT; - if (*ppos >= nvram_size) + if (*ppos >= size) return 0; - if (count > nvram_size) - count = nvram_size; + if (count > size) + count = size; tmp_buffer = (char *) kmalloc(count, GFP_KERNEL); if (!tmp_buffer) { @@ -145,6 +159,28 @@ static ssize_t dev_nvram_write(struct file *file, const char *buf, static int dev_nvram_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + switch(cmd) { +#ifdef CONFIG_PPC_PMAC + case OBSOLETE_PMAC_NVRAM_GET_OFFSET: + printk(KERN_WARNING "nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n"); + case IOC_NVRAM_GET_OFFSET: { + int part, offset; + + if (systemcfg->platform != PLATFORM_POWERMAC) + return -EINVAL; + if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0) + return -EFAULT; + if (part < pmac_nvram_OF || part > pmac_nvram_NR) + return -EINVAL; + offset = pmac_get_partition(part); + if (offset < 0) + return offset; + if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0) + return -EFAULT; + return 0; + } +#endif /* CONFIG_PPC_PMAC */ + } return -EINVAL; } @@ -162,259 +198,75 @@ static struct miscdevice nvram_dev = { &nvram_fops }; -ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) -{ - unsigned int i; - unsigned long len, done; - unsigned long flags; - char *p = buf; - - if (*index >= nvram_size) - return 0; - - i = *index; - if (i + count > nvram_size) - count = nvram_size - i; - spin_lock_irqsave(&nvram_lock, flags); - for (; count != 0; count -= len) { - len = count; - if (len > NVRW_CNT) - len = NVRW_CNT; - - if ((rtas_call(nvram_fetch, 3, 2, &done, i, __pa(nvram_buf), - len) != 0) || len != done) { - spin_unlock_irqrestore(&nvram_lock, flags); - return -EIO; - } - - memcpy(p, nvram_buf, len); - - p += len; - i += len; - } - - spin_unlock_irqrestore(&nvram_lock, flags); +static void nvram_print_partitions(char * label) +{ + struct list_head * p; + struct nvram_partition * tmp_part; - *index = i; - return p - buf; + printk(KERN_WARNING "--------%s---------\n", label); + printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n"); + list_for_each(p, &nvram_part->partition) { + tmp_part = list_entry(p, struct nvram_partition, partition); + printk(KERN_WARNING "%d \t%02x\t%02x\t%d\t%s\n", + tmp_part->index, tmp_part->header.signature, + tmp_part->header.checksum, tmp_part->header.length, + tmp_part->header.name); + } } -ssize_t pSeries_nvram_write(char *buf, size_t count, loff_t *index) -{ - unsigned int i; - unsigned long len, done; - unsigned long flags; - const char *p = buf; - - if (*index >= nvram_size) - return 0; - - i = *index; - if (i + count > nvram_size) - count = nvram_size - i; - - spin_lock_irqsave(&nvram_lock, flags); - - for (; count != 0; count -= len) { - len = count; - if (len > NVRW_CNT) - len = NVRW_CNT; - - memcpy(nvram_buf, p, len); - if ((rtas_call(nvram_store, 3, 2, &done, i, __pa(nvram_buf), - len) != 0) || len != done) { - spin_unlock_irqrestore(&nvram_lock, flags); - return -EIO; - } - - p += len; - i += len; - } - spin_unlock_irqrestore(&nvram_lock, flags); - - *index = i; - return p - buf; -} - -int __init nvram_init(void) +static int nvram_write_header(struct nvram_partition * part) { - struct device_node *nvram; - unsigned int *nbytes_p, proplen; - int error; + loff_t tmp_index; int rc; - if ((nvram = of_find_node_by_type(NULL, "nvram")) != NULL) { - nbytes_p = (unsigned int *)get_property(nvram, "#bytes", &proplen); - if (nbytes_p && proplen == sizeof(unsigned int)) { - nvram_size = *nbytes_p; - } else { - return -EIO; - } - } - nvram_fetch = rtas_token("nvram-fetch"); - nvram_store = rtas_token("nvram-store"); - printk(KERN_INFO "PPC64 nvram contains %d bytes\n", nvram_size); - of_node_put(nvram); - - rc = misc_register(&nvram_dev); - - /* If we don't know how big NVRAM is then we shouldn't touch - the nvram partitions */ - if (nvram == NULL) { - return rc; - } - - /* initialize our anchor for the nvram partition list */ - nvram_part = (struct nvram_partition *) kmalloc(sizeof(struct nvram_partition), GFP_KERNEL); - if (!nvram_part) { - printk(KERN_ERR "nvram_init: Failed kmalloc\n"); - return -ENOMEM; - } - INIT_LIST_HEAD(&nvram_part->partition); - - /* Get all the NVRAM partitions */ - error = nvram_scan_partitions(); - if (error) { - printk(KERN_ERR "nvram_init: Failed nvram_scan_partitions\n"); - return error; - } - - if(nvram_setup_partition()) - printk(KERN_WARNING "nvram_init: Could not find nvram partition" - " for nvram buffered error logging.\n"); - -#ifdef DEBUG_NVRAM - nvram_print_partitions("NVRAM Partitions"); -#endif + tmp_index = part->index; + rc = ppc_md.nvram_write((char *)&part->header, NVRAM_HEADER_LEN, &tmp_index); - return rc; + return rc; } -void __exit nvram_cleanup(void) -{ - misc_deregister( &nvram_dev ); -} -static int nvram_scan_partitions(void) +static unsigned char nvram_checksum(struct nvram_header *p) { - loff_t cur_index = 0; - struct nvram_header phead; - struct nvram_partition * tmp_part; - unsigned char c_sum; - char * header; - long size; - - header = (char *) kmalloc(NVRAM_HEADER_LEN, GFP_KERNEL); - if (!header) { - printk(KERN_ERR "nvram_scan_partitions: Failed kmalloc\n"); - return -ENOMEM; - } - - while (cur_index < nvram_size) { - - size = ppc_md.nvram_read(header, NVRAM_HEADER_LEN, &cur_index); - if (size != NVRAM_HEADER_LEN) { - printk(KERN_ERR "nvram_scan_partitions: Error parsing " - "nvram partitions\n"); - kfree(header); - return size; - } - - cur_index -= NVRAM_HEADER_LEN; /* nvram_read will advance us */ - - memcpy(&phead, header, NVRAM_HEADER_LEN); - - c_sum = nvram_checksum(&phead); - if (c_sum != phead.checksum) - printk(KERN_WARNING "WARNING: nvram partition checksum " - "was %02x, should be %02x!\n", phead.checksum, c_sum); - - tmp_part = (struct nvram_partition *) - kmalloc(sizeof(struct nvram_partition), GFP_KERNEL); - if (!tmp_part) { - printk(KERN_ERR "nvram_scan_partitions: kmalloc failed\n"); - kfree(header); - return -ENOMEM; - } - - memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN); - tmp_part->index = cur_index; - list_add_tail(&tmp_part->partition, &nvram_part->partition); - - cur_index += phead.length * NVRAM_BLOCK_LEN; - } + unsigned int c_sum, c_sum2; + unsigned short *sp = (unsigned short *)p->name; /* assume 6 shorts */ + c_sum = p->signature + p->length + sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5]; - kfree(header); - return 0; + /* The sum may have spilled into the 3rd byte. Fold it back. */ + c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff; + /* The sum cannot exceed 2 bytes. Fold it into a checksum */ + c_sum2 = (c_sum >> 8) + (c_sum << 8); + c_sum = ((c_sum + c_sum2) >> 8) & 0xff; + return c_sum; } -/* nvram_setup_partition - * - * This will setup the partition we need for buffering the - * error logs and cleanup partitions if needed. - * - * The general strategy is the following: - * 1.) If there is ppc64,linux partition large enough then use it. - * 2.) If there is not a ppc64,linux partition large enough, search - * for a free partition that is large enough. - * 3.) If there is not a free partition large enough remove - * _all_ OS partitions and consolidate the space. - * 4.) Will first try getting a chunk that will satisfy the maximum - * error log size (NVRAM_MAX_REQ). - * 5.) If the max chunk cannot be allocated then try finding a chunk - * that will satisfy the minum needed (NVRAM_MIN_REQ). + +/* + * Find an nvram partition, sig can be 0 for any + * partition or name can be NULL for any name, else + * tries to match both */ -static int nvram_setup_partition(void) +struct nvram_partition *nvram_find_partition(int sig, const char *name) { - struct list_head * p; struct nvram_partition * part; - int rc; + struct list_head * p; - /* see if we have an OS partition that meets our needs. - will try getting the max we need. If not we'll delete - partitions and try again. */ list_for_each(p, &nvram_part->partition) { part = list_entry(p, struct nvram_partition, partition); - if (part->header.signature != NVRAM_SIG_OS) - continue; - if (strcmp(part->header.name, "ppc64,linux")) + if (sig && part->header.signature != sig) continue; - - if (part->header.length >= NVRAM_MIN_REQ) { - /* found our partition */ - nvram_error_log_index = part->index + NVRAM_HEADER_LEN; - nvram_error_log_size = ((part->header.length - 1) * - NVRAM_BLOCK_LEN) - sizeof(struct err_log_info); - return 0; - } - } - - /* try creating a partition with the free space we have */ - rc = nvram_create_os_partition(); - if (!rc) { - return 0; - } - - /* need to free up some space */ - rc = nvram_remove_os_partition(); - if (rc) { - return rc; - } - - /* create a partition in this new space */ - rc = nvram_create_os_partition(); - if (rc) { - printk(KERN_ERR "nvram_create_os_partition: Could not find a " - "NVRAM partition large enough\n"); - return rc; + if (name && 0 != strncmp(name, part->header.name, 12)) + continue; + return part; } - - return 0; + return NULL; } +EXPORT_SYMBOL(nvram_find_partition); + static int nvram_remove_os_partition(void) { @@ -572,22 +424,185 @@ static int nvram_create_os_partition(void) } -void nvram_print_partitions(char * label) +/* nvram_setup_partition + * + * This will setup the partition we need for buffering the + * error logs and cleanup partitions if needed. + * + * The general strategy is the following: + * 1.) If there is ppc64,linux partition large enough then use it. + * 2.) If there is not a ppc64,linux partition large enough, search + * for a free partition that is large enough. + * 3.) If there is not a free partition large enough remove + * _all_ OS partitions and consolidate the space. + * 4.) Will first try getting a chunk that will satisfy the maximum + * error log size (NVRAM_MAX_REQ). + * 5.) If the max chunk cannot be allocated then try finding a chunk + * that will satisfy the minum needed (NVRAM_MIN_REQ). + */ +static int nvram_setup_partition(void) { struct list_head * p; + struct nvram_partition * part; + int rc; + + /* For now, we don't do any of this on pmac, until I + * have figured out if it's worth killing some unused stuffs + * in our nvram, as Apple defined partitions use pretty much + * all of the space + */ + if (systemcfg->platform == PLATFORM_POWERMAC) + return -ENOSPC; + + /* see if we have an OS partition that meets our needs. + will try getting the max we need. If not we'll delete + partitions and try again. */ + list_for_each(p, &nvram_part->partition) { + part = list_entry(p, struct nvram_partition, partition); + if (part->header.signature != NVRAM_SIG_OS) + continue; + + if (strcmp(part->header.name, "ppc64,linux")) + continue; + + if (part->header.length >= NVRAM_MIN_REQ) { + /* found our partition */ + nvram_error_log_index = part->index + NVRAM_HEADER_LEN; + nvram_error_log_size = ((part->header.length - 1) * + NVRAM_BLOCK_LEN) - sizeof(struct err_log_info); + return 0; + } + } + + /* try creating a partition with the free space we have */ + rc = nvram_create_os_partition(); + if (!rc) { + return 0; + } + + /* need to free up some space */ + rc = nvram_remove_os_partition(); + if (rc) { + return rc; + } + + /* create a partition in this new space */ + rc = nvram_create_os_partition(); + if (rc) { + printk(KERN_ERR "nvram_create_os_partition: Could not find a " + "NVRAM partition large enough\n"); + return rc; + } + + return 0; +} + + +static int nvram_scan_partitions(void) +{ + loff_t cur_index = 0; + struct nvram_header phead; struct nvram_partition * tmp_part; + unsigned char c_sum; + char * header; + long size; + int total_size; + + if (ppc_md.nvram_size == NULL) + return -ENODEV; + total_size = ppc_md.nvram_size(); - printk(KERN_WARNING "--------%s---------\n", label); - printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n"); - list_for_each(p, &nvram_part->partition) { - tmp_part = list_entry(p, struct nvram_partition, partition); - printk(KERN_WARNING "%d \t%02x\t%02x\t%d\t%s\n", - tmp_part->index, tmp_part->header.signature, - tmp_part->header.checksum, tmp_part->header.length, - tmp_part->header.name); + header = (char *) kmalloc(NVRAM_HEADER_LEN, GFP_KERNEL); + if (!header) { + printk(KERN_ERR "nvram_scan_partitions: Failed kmalloc\n"); + return -ENOMEM; } + + while (cur_index < total_size) { + + size = ppc_md.nvram_read(header, NVRAM_HEADER_LEN, &cur_index); + if (size != NVRAM_HEADER_LEN) { + printk(KERN_ERR "nvram_scan_partitions: Error parsing " + "nvram partitions\n"); + kfree(header); + return size; + } + + cur_index -= NVRAM_HEADER_LEN; /* nvram_read will advance us */ + + memcpy(&phead, header, NVRAM_HEADER_LEN); + + c_sum = nvram_checksum(&phead); + if (c_sum != phead.checksum) + printk(KERN_WARNING "WARNING: nvram partition checksum " + "was %02x, should be %02x!\n", phead.checksum, c_sum); + + tmp_part = (struct nvram_partition *) + kmalloc(sizeof(struct nvram_partition), GFP_KERNEL); + if (!tmp_part) { + printk(KERN_ERR "nvram_scan_partitions: kmalloc failed\n"); + kfree(header); + return -ENOMEM; + } + + memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN); + tmp_part->index = cur_index; + list_add_tail(&tmp_part->partition, &nvram_part->partition); + + cur_index += phead.length * NVRAM_BLOCK_LEN; + } + + kfree(header); + return 0; +} + +static int __init nvram_init(void) +{ + int error; + int rc; + + if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0) + return -ENODEV; + + rc = misc_register(&nvram_dev); + if (rc != 0) { + printk(KERN_ERR "nvram_init: failed to register device\n"); + return rc; + } + + /* initialize our anchor for the nvram partition list */ + nvram_part = (struct nvram_partition *) kmalloc(sizeof(struct nvram_partition), GFP_KERNEL); + if (!nvram_part) { + printk(KERN_ERR "nvram_init: Failed kmalloc\n"); + return -ENOMEM; + } + INIT_LIST_HEAD(&nvram_part->partition); + + /* Get all the NVRAM partitions */ + error = nvram_scan_partitions(); + if (error) { + printk(KERN_ERR "nvram_init: Failed nvram_scan_partitions\n"); + return error; + } + + if(nvram_setup_partition()) + printk(KERN_WARNING "nvram_init: Could not find nvram partition" + " for nvram buffered error logging.\n"); + +#ifdef DEBUG_NVRAM + nvram_print_partitions("NVRAM Partitions"); +#endif + + return rc; +} + +void __exit nvram_cleanup(void) +{ + misc_deregister( &nvram_dev ); } + + /* nvram_write_error_log * * We need to buffer the error logs into nvram to ensure that we have @@ -711,30 +726,6 @@ int nvram_clear_error_log() return 0; } -static int nvram_write_header(struct nvram_partition * part) -{ - loff_t tmp_index; - int rc; - - tmp_index = part->index; - rc = ppc_md.nvram_write((char *)&part->header, NVRAM_HEADER_LEN, &tmp_index); - - return rc; -} - -static unsigned char nvram_checksum(struct nvram_header *p) -{ - unsigned int c_sum, c_sum2; - unsigned short *sp = (unsigned short *)p->name; /* assume 6 shorts */ - c_sum = p->signature + p->length + sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5]; - - /* The sum may have spilled into the 3rd byte. Fold it back. */ - c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff; - /* The sum cannot exceed 2 bytes. Fold it into a checksum */ - c_sum2 = (c_sum >> 8) + (c_sum << 8); - c_sum = ((c_sum + c_sum2) >> 8) & 0xff; - return c_sum; -} module_init(nvram_init); module_exit(nvram_cleanup); diff --git a/arch/ppc64/kernel/setup.c b/arch/ppc64/kernel/setup.c index ab761aff6221..3acc921378b6 100644 --- a/arch/ppc64/kernel/setup.c +++ b/arch/ppc64/kernel/setup.c @@ -40,6 +40,7 @@ #include #include #include +#include extern unsigned long klimit; /* extern void *stab; */ @@ -261,6 +262,8 @@ void setup_system(unsigned long r3, unsigned long r4, unsigned long r5, void machine_restart(char *cmd) { + if (ppc_md.nvram_sync) + ppc_md.nvram_sync(); ppc_md.restart(cmd); } @@ -268,6 +271,8 @@ EXPORT_SYMBOL(machine_restart); void machine_power_off(void) { + if (ppc_md.nvram_sync) + ppc_md.nvram_sync(); ppc_md.power_off(); } @@ -275,6 +280,8 @@ EXPORT_SYMBOL(machine_power_off); void machine_halt(void) { + if (ppc_md.nvram_sync) + ppc_md.nvram_sync(); ppc_md.halt(); } diff --git a/include/asm-ppc64/machdep.h b/include/asm-ppc64/machdep.h index f34d3a824546..e747011fab75 100644 --- a/include/asm-ppc64/machdep.h +++ b/include/asm-ppc64/machdep.h @@ -94,6 +94,9 @@ struct machdep_calls { ssize_t (*nvram_write)(char *buf, size_t count, loff_t *index); ssize_t (*nvram_read)(char *buf, size_t count, loff_t *index); + ssize_t (*nvram_size)(void); + int (*nvram_sync)(void); + #ifdef CONFIG_SMP /* functions for dealing with other cpus */ diff --git a/include/asm-ppc64/nvram.h b/include/asm-ppc64/nvram.h index 93b1686a1116..4e6dd370d936 100644 --- a/include/asm-ppc64/nvram.h +++ b/include/asm-ppc64/nvram.h @@ -38,12 +38,15 @@ #define NVRAM_SIG_OF 0x50 /* open firmware config */ #define NVRAM_SIG_FW 0x51 /* general firmware */ #define NVRAM_SIG_HW 0x52 /* hardware (VPD) */ +#define NVRAM_SIG_FLIP 0x5a /* Apple flip/flop header */ +#define NVRAM_SIG_APPL 0x5f /* Apple "system" (???) */ #define NVRAM_SIG_SYS 0x70 /* system env vars */ #define NVRAM_SIG_CFG 0x71 /* config data */ #define NVRAM_SIG_ELOG 0x72 /* error log */ #define NVRAM_SIG_VEND 0x7e /* vendor defined */ #define NVRAM_SIG_FREE 0x7f /* Free space */ #define NVRAM_SIG_OS 0xa0 /* OS defined */ +#define NVRAM_SIG_PANIC 0xa1 /* Apple OSX "panic" */ /* If change this size, then change the size of NVNAME_LEN */ struct nvram_header { @@ -60,11 +63,53 @@ struct nvram_partition { }; -ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index); -ssize_t pSeries_nvram_write(char *buf, size_t count, loff_t *index); -int nvram_write_error_log(char * buff, int length, unsigned int err_type); -int nvram_read_error_log(char * buff, int length, unsigned int * err_type); -int nvram_clear_error_log(void); -void nvram_print_partitions(char * label); +extern int nvram_write_error_log(char * buff, int length, unsigned int err_type); +extern int nvram_read_error_log(char * buff, int length, unsigned int * err_type); +extern int nvram_clear_error_log(void); +extern struct nvram_partition *nvram_find_partition(int sig, const char *name); + +extern int pSeries_nvram_init(void); +extern int pmac_nvram_init(void); + +/* PowerMac specific nvram stuffs */ + +enum { + pmac_nvram_OF, /* Open Firmware partition */ + pmac_nvram_XPRAM, /* MacOS XPRAM partition */ + pmac_nvram_NR /* MacOS Name Registry partition */ +}; + +/* Return partition offset in nvram */ +extern int pmac_get_partition(int partition); + +/* Direct access to XPRAM on PowerMacs */ +extern u8 pmac_xpram_read(int xpaddr); +extern void pmac_xpram_write(int xpaddr, u8 data); + +/* Synchronize NVRAM */ +extern int nvram_sync(void); + +/* Some offsets in XPRAM */ +#define PMAC_XPRAM_MACHINE_LOC 0xe4 +#define PMAC_XPRAM_SOUND_VOLUME 0x08 + +/* Machine location structure in PowerMac XPRAM */ +struct pmac_machine_location { + unsigned int latitude; /* 2+30 bit Fractional number */ + unsigned int longitude; /* 2+30 bit Fractional number */ + unsigned int delta; /* mix of GMT delta and DLS */ +}; + +/* + * /dev/nvram ioctls + * + * Note that PMAC_NVRAM_GET_OFFSET is still supported, but is + * definitely obsolete. Do not use it if you can avoid it + */ + +#define OBSOLETE_PMAC_NVRAM_GET_OFFSET \ + _IOWR('p', 0x40, int) + +#define IOC_NVRAM_GET_OFFSET _IOWR('p', 0x42, int) /* Get NVRAM partition offset */ #endif /* _PPC64_NVRAM_H */ -- cgit v1.2.3 From 8864144f019de8cec7b9ae75ab46610a4a72e5f6 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 13:24:50 +1100 Subject: ppc64: Add the PowerMac PCI support This involves moving the final fixup to a function pointer in machdep, turning all the PCI DMA routines into function pointers in a separate structure and a bit of renaming work. The PowerMac currently use "direct" PCI DMA bypassing the iommu. The driver for the IOMMU will come later, allowing us to lift the limitation to 2Gb of RAM --- arch/ppc64/kernel/chrp_setup.c | 3 ++ arch/ppc64/kernel/iSeries_pci.c | 4 +-- arch/ppc64/kernel/iSeries_setup.c | 9 +++-- arch/ppc64/kernel/pSeries_pci.c | 2 +- arch/ppc64/kernel/pci.c | 26 ++++++++++++--- arch/ppc64/kernel/pci_dma.c | 31 +++++++++++++----- arch/ppc64/kernel/pci_dn.c | 1 + include/asm-ppc64/machdep.h | 3 ++ include/asm-ppc64/pci-bridge.h | 5 ++- include/asm-ppc64/pci.h | 69 +++++++++++++++++++++++++++++++-------- include/asm-ppc64/pci_dma.h | 6 ++-- 11 files changed, 124 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/arch/ppc64/kernel/chrp_setup.c b/arch/ppc64/kernel/chrp_setup.c index 2a0df2e5ba7f..c7f89af4de9e 100644 --- a/arch/ppc64/kernel/chrp_setup.c +++ b/arch/ppc64/kernel/chrp_setup.c @@ -70,6 +70,7 @@ void chrp_progress(char *, unsigned short); extern void openpic_init_IRQ(void); extern void find_and_init_phbs(void); +extern void pSeries_final_fixup(void); extern void pSeries_get_boot_time(struct rtc_time *rtc_time); extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); @@ -265,6 +266,8 @@ chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, ppc_md.init = chrp_init2; + ppc_md.pcibios_fixup = pSeries_final_fixup; + ppc_md.restart = rtas_restart; ppc_md.power_off = rtas_power_off; ppc_md.halt = rtas_halt; diff --git a/arch/ppc64/kernel/iSeries_pci.c b/arch/ppc64/kernel/iSeries_pci.c index f5bc53b3a8d7..9a07bbb6724d 100644 --- a/arch/ppc64/kernel/iSeries_pci.c +++ b/arch/ppc64/kernel/iSeries_pci.c @@ -241,9 +241,9 @@ void iSeries_pcibios_init(void) } /* - * pcibios_final_fixup(void) + * iSeries_pci_final_fixup(void) */ -void __init pcibios_final_fixup(void) +void __init iSeries_pci_final_fixup(void) { struct pci_dev *pdev = NULL; struct iSeries_Device_Node *node; diff --git a/arch/ppc64/kernel/iSeries_setup.c b/arch/ppc64/kernel/iSeries_setup.c index bf234f5ec4ce..2928075ab6fe 100644 --- a/arch/ppc64/kernel/iSeries_setup.c +++ b/arch/ppc64/kernel/iSeries_setup.c @@ -63,10 +63,11 @@ extern void tce_init_iSeries(void); static void build_iSeries_Memory_Map(void); static void setup_iSeries_cache_sizes(void); static void iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr); -void build_valid_hpte(unsigned long vsid, unsigned long ea, unsigned long pa, - pte_t *ptep, unsigned hpteflags, unsigned bolted); +extern void build_valid_hpte(unsigned long vsid, unsigned long ea, unsigned long pa, + pte_t *ptep, unsigned hpteflags, unsigned bolted); static void iSeries_setup_dprofile(void); -void iSeries_setup_arch(void); +extern void iSeries_setup_arch(void); +extern void iSeries_pci_final_fixup(void); /* Global Variables */ static unsigned long procFreqHz; @@ -318,6 +319,8 @@ void __init iSeries_init_early(void) ppc_md.get_irq = iSeries_get_irq; ppc_md.init = NULL; + ppc_md.pcibios_fixup = iSeries_pci_final_fixup; + ppc_md.restart = iSeries_restart; ppc_md.power_off = iSeries_power_off; ppc_md.halt = iSeries_halt; diff --git a/arch/ppc64/kernel/pSeries_pci.c b/arch/ppc64/kernel/pSeries_pci.c index 69e259d35c83..458d65cca89e 100644 --- a/arch/ppc64/kernel/pSeries_pci.c +++ b/arch/ppc64/kernel/pSeries_pci.c @@ -687,7 +687,7 @@ static void phbs_fixup_io(void) extern void chrp_request_regions(void); -void __init pcibios_final_fixup(void) +void __init pSeries_final_fixup(void) { struct pci_dev *dev = NULL; diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index f37fc0aa3f2f..ce83d1dd4507 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "pci.h" @@ -58,21 +59,32 @@ void pcibios_name_device(struct pci_dev* dev); void pcibios_final_fixup(void); static void fixup_broken_pcnet32(struct pci_dev* dev); static void fixup_windbond_82c105(struct pci_dev* dev); +extern void fixup_k2_sata(struct pci_dev* dev); void iSeries_pcibios_init(void); struct pci_controller *hose_head; struct pci_controller **hose_tail = &hose_head; +struct pci_dma_ops pci_dma_ops; +EXPORT_SYMBOL(pci_dma_ops); + int global_phb_number; /* Global phb counter */ /* Cached ISA bridge dev. */ struct pci_dev *ppc64_isabridge_dev = NULL; struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32 }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, fixup_windbond_82c105 }, - { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, + fixup_broken_pcnet32 }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, + fixup_windbond_82c105 }, + { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, + pcibios_name_device }, +#ifdef CONFIG_PPC_PMAC + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, 0x0240, + fixup_k2_sata }, +#endif { 0 } }; @@ -250,6 +262,9 @@ pci_alloc_pci_controller(enum phb_types controller_type) case phb_type_winnipeg: model = "PHB WP"; break; + case phb_type_apple: + model = "PHB APPLE"; + break; default: model = "PHB UK"; break; @@ -332,8 +347,9 @@ static int __init pcibios_init(void) pci_assign_unassigned_resources(); #endif - /* Call machine dependent fixup */ - pcibios_final_fixup(); + /* Call machine dependent final fixup */ + if (ppc_md.pcibios_fixup) + ppc_md.pcibios_fixup(); /* Cache the location of the ISA bridge (if we have one) */ ppc64_isabridge_dev = pci_find_class(PCI_CLASS_BRIDGE_ISA << 8, NULL); diff --git a/arch/ppc64/kernel/pci_dma.c b/arch/ppc64/kernel/pci_dma.c index 0d054e36d03a..64edeba4ba0d 100644 --- a/arch/ppc64/kernel/pci_dma.c +++ b/arch/ppc64/kernel/pci_dma.c @@ -1002,7 +1002,7 @@ static void getTceTableParmsPSeriesLP(struct pci_controller *phb, * Returns the virtual address of the buffer and sets dma_handle * to the dma address (tce) of the first page. */ -void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, +static void *tce_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) { struct TceTable * tbl; @@ -1055,7 +1055,7 @@ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, return ret; } -void pci_free_consistent(struct pci_dev *hwdev, size_t size, +static void tce_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) { struct TceTable * tbl; @@ -1089,7 +1089,7 @@ void pci_free_consistent(struct pci_dev *hwdev, size_t size, * need not be page aligned, the dma_addr_t returned will point to the same * byte within the page as vaddr. */ -dma_addr_t pci_map_single(struct pci_dev *hwdev, void *vaddr, +static dma_addr_t tce_map_single(struct pci_dev *hwdev, void *vaddr, size_t size, int direction ) { struct TceTable * tbl; @@ -1124,7 +1124,7 @@ dma_addr_t pci_map_single(struct pci_dev *hwdev, void *vaddr, return dma_handle; } -void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) +static void tce_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) { struct TceTable * tbl; unsigned order, nPages; @@ -1354,7 +1354,7 @@ static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, return dmaAddr; } -int pci_map_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction ) +static int tce_map_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction ) { struct TceTable * tbl; unsigned numTces; @@ -1389,7 +1389,7 @@ int pci_map_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nents, int di return num_dma; } -void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) +static void tce_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) { struct TceTable * tbl; unsigned order, numTces, i; @@ -1430,7 +1430,7 @@ void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int } #else -int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, +static int tce_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) { int i; @@ -1448,7 +1448,7 @@ int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, return nelems; } -void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, +static void tce_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) { while (nelems--) { @@ -1465,7 +1465,15 @@ void tce_init_pSeries(void) { ppc_md.tce_build = tce_build_pSeries; ppc_md.tce_free_one = tce_free_one_pSeries; + + pci_dma_ops.pci_alloc_consistent = tce_alloc_consistent; + pci_dma_ops.pci_free_consistent = tce_free_consistent; + pci_dma_ops.pci_map_single = tce_map_single; + pci_dma_ops.pci_unmap_single = tce_unmap_single; + pci_dma_ops.pci_map_sg = tce_map_sg; + pci_dma_ops.pci_unmap_sg = tce_unmap_sg; } + #endif #ifdef CONFIG_PPC_ISERIES @@ -1473,5 +1481,12 @@ void tce_init_iSeries(void) { ppc_md.tce_build = tce_build_iSeries; ppc_md.tce_free_one = tce_free_one_iSeries; + + pci_dma_ops.pci_alloc_consistent = tce_alloc_consistent; + pci_dma_ops.pci_free_consistent = tce_free_consistent; + pci_dma_ops.pci_map_single = tce_map_single; + pci_dma_ops.pci_unmap_single = tce_unmap_single; + pci_dma_ops.pci_map_sg = tce_map_sg; + pci_dma_ops.pci_unmap_sg = tce_unmap_sg; } #endif diff --git a/arch/ppc64/kernel/pci_dn.c b/arch/ppc64/kernel/pci_dn.c index c809d7301091..7b7d7efcfa63 100644 --- a/arch/ppc64/kernel/pci_dn.c +++ b/arch/ppc64/kernel/pci_dn.c @@ -181,6 +181,7 @@ struct device_node *fetch_dev_dn(struct pci_dev *dev) } return dn; } +EXPORT_SYMBOL(fetch_dev_dn); /****************************************************************** diff --git a/include/asm-ppc64/machdep.h b/include/asm-ppc64/machdep.h index e747011fab75..3c2af66260ea 100644 --- a/include/asm-ppc64/machdep.h +++ b/include/asm-ppc64/machdep.h @@ -69,6 +69,9 @@ struct machdep_calls { void (*init_IRQ)(void); int (*get_irq)(struct pt_regs *); + /* PCI stuff */ + void (*pcibios_fixup)(void); + /* Optional, may be NULL. */ void (*init)(void); diff --git a/include/asm-ppc64/pci-bridge.h b/include/asm-ppc64/pci-bridge.h index 33933158cc51..a092b9cae621 100644 --- a/include/asm-ppc64/pci-bridge.h +++ b/include/asm-ppc64/pci-bridge.h @@ -21,7 +21,8 @@ enum phb_types { phb_type_hypervisor = 0x1, phb_type_python = 0x10, phb_type_speedwagon = 0x11, - phb_type_winnipeg = 0x12 + phb_type_winnipeg = 0x12, + phb_type_apple = 0xff }; /* @@ -47,6 +48,8 @@ struct pci_controller { unsigned long pci_io_offset; struct pci_ops *ops; + volatile unsigned int *cfg_addr; + volatile unsigned char *cfg_data; /* Currently, we limit ourselves to 1 IO range and 3 mem * ranges since the common pci_bus structure can't handle more diff --git a/include/asm-ppc64/pci.h b/include/asm-ppc64/pci.h index 835b882fb18d..f2e5f70a4002 100644 --- a/include/asm-ppc64/pci.h +++ b/include/asm-ppc64/pci.h @@ -55,19 +55,62 @@ static inline int pcibios_prep_mwi(struct pci_dev *dev) extern unsigned int pcibios_assign_all_busses(void); -extern void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle); -extern void pci_free_consistent(struct pci_dev *hwdev, size_t size, - void *vaddr, dma_addr_t dma_handle); - -extern dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, - size_t size, int direction); -extern void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, - size_t size, int direction); -extern int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, - int nents, int direction); -extern void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, - int nents, int direction); +/* + * PCI DMA operations are abstracted for G5 vs. i/pSeries + */ +struct pci_dma_ops { + void * (*pci_alloc_consistent)(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle); + void (*pci_free_consistent)(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle); + + dma_addr_t (*pci_map_single)(struct pci_dev *hwdev, void *ptr, + size_t size, int direction); + void (*pci_unmap_single)(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction); + int (*pci_map_sg)(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction); + void (*pci_unmap_sg)(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction); +}; + +extern struct pci_dma_ops pci_dma_ops; + +static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + return pci_dma_ops.pci_alloc_consistent(hwdev, size, dma_handle); +} + +static inline void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + pci_dma_ops.pci_free_consistent(hwdev, size, vaddr, dma_handle); +} + +static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, + size_t size, int direction) +{ + return pci_dma_ops.pci_map_single(hwdev, ptr, size, direction); +} + +static inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ + pci_dma_ops.pci_unmap_single(hwdev, dma_addr, size, direction); +} + +static inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + return pci_dma_ops.pci_map_sg(hwdev, sg, nents, direction); +} + +static inline void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + pci_dma_ops.pci_unmap_sg(hwdev, sg, nents, direction); +} static inline void pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, diff --git a/include/asm-ppc64/pci_dma.h b/include/asm-ppc64/pci_dma.h index 7b88d2c35838..bee2fc242da6 100644 --- a/include/asm-ppc64/pci_dma.h +++ b/include/asm-ppc64/pci_dma.h @@ -94,7 +94,9 @@ extern struct TceTable virtBusTceTable; /* Tce table for virtual bus */ extern void create_tce_tables(void); extern void create_pci_bus_tce_table(unsigned long); -void tce_init_pSeries(void); -void tce_init_iSeries(void); +extern void tce_init_pSeries(void); +extern void tce_init_iSeries(void); + +extern void pci_dma_init_direct(void); #endif -- cgit v1.2.3 From 17512c6068afca6ba5de773590d0d300a6b2707c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 13:31:14 +1100 Subject: ppc64: Add the feature_call function pointer to machdep --- include/asm-ppc64/machdep.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/asm-ppc64/machdep.h b/include/asm-ppc64/machdep.h index 3c2af66260ea..456633c66f88 100644 --- a/include/asm-ppc64/machdep.h +++ b/include/asm-ppc64/machdep.h @@ -100,6 +100,11 @@ struct machdep_calls { ssize_t (*nvram_size)(void); int (*nvram_sync)(void); + /* Motherboard/chipset features. This is a kind of general purpose + * hook used to control some machine specific features (like reset + * lines, chip power control, etc...). + */ + long (*feature_call)(unsigned int feature, ...); #ifdef CONFIG_SMP /* functions for dealing with other cpus */ -- cgit v1.2.3 From 24b4482a855ad1819bbf0fbb72a0b7c5c5af37b8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 13:31:59 +1100 Subject: ppc64: Remove duplicate (& incorrect) definition of kern_add_valid() --- include/asm-ppc64/mmzone.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/asm-ppc64/mmzone.h b/include/asm-ppc64/mmzone.h index 7503c373f55e..e2b3d7792142 100644 --- a/include/asm-ppc64/mmzone.h +++ b/include/asm-ppc64/mmzone.h @@ -72,9 +72,6 @@ static inline int pa_to_nid(unsigned long pa) #define local_mapnr(kvaddr) \ ( (__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr)) -/* XXX fix - Anton - and wli */ -#define kern_addr_valid(kaddr) (0) - /* Written this way to avoid evaluating arguments twice */ #define discontigmem_pfn_to_page(pfn) \ ({ \ -- cgit v1.2.3 From b8d9ae1c23e45e7a929f4d6de039ab147be4da23 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 13 Feb 2004 13:43:20 +1100 Subject: ppc64: Add SMP support for PowerMac G5 --- arch/ppc64/kernel/smp.c | 61 +++++++++++++++++++++++++++------------------ include/asm-ppc64/machdep.h | 4 --- 2 files changed, 37 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/arch/ppc64/kernel/smp.c b/arch/ppc64/kernel/smp.c index 101441d0adc7..36746aefb1d2 100644 --- a/arch/ppc64/kernel/smp.c +++ b/arch/ppc64/kernel/smp.c @@ -62,7 +62,7 @@ cpumask_t cpu_present_at_boot = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); EXPORT_SYMBOL(cpu_possible_map); -static struct smp_ops_t *smp_ops; +struct smp_ops_t *smp_ops; static volatile unsigned int cpu_callin_map[NR_CPUS]; @@ -76,6 +76,8 @@ extern long register_vpa(unsigned long flags, unsigned long proc, #define smp_message_pass(t,m,d,w) smp_ops->message_pass((t),(m),(d),(w)) +/* Low level assembly function used to backup CPU 0 state */ +extern void __save_cpu_setup(void); #ifdef CONFIG_PPC_ISERIES static unsigned long iSeries_smp_message[NR_CPUS]; @@ -175,21 +177,23 @@ static void __devinit smp_iSeries_setup_cpu(int nr) { } +static struct smp_ops_t iSeries_smp_ops = { + .message_pass = smp_iSeries_message_pass, + .probe = smp_iSeries_probe, + .kick_cpu = smp_iSeries_kick_cpu, + .setup_cpu = smp_iSeries_setup_cpu, +}; + /* This is called very early. */ void __init smp_init_iSeries(void) { - smp_ops = &ppc_md.smp_ops; - smp_ops->message_pass = smp_iSeries_message_pass; - smp_ops->probe = smp_iSeries_probe; - smp_ops->kick_cpu = smp_iSeries_kick_cpu; - smp_ops->setup_cpu = smp_iSeries_setup_cpu; + smp_ops = &iSeries_smp_ops; systemcfg->processorCount = smp_iSeries_numProcs(); } #endif #ifdef CONFIG_PPC_PSERIES -static void -smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) +void smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) { /* make sure we're sending something that translates to an IPI */ if ( msg > 0x3 ){ @@ -233,8 +237,7 @@ static void __devinit smp_openpic_setup_cpu(int cpu) do_openpic_setup_cpu(); } -static void -smp_kick_cpu(int nr) +static void smp_pSeries_kick_cpu(int nr) { /* Verify we have a Paca for processor nr */ if ( ( nr <= 0 ) || @@ -283,8 +286,7 @@ void vpa_init(int cpu) register_vpa(flags, cpu, __pa((unsigned long)&(paca[cpu].xLpPaca))); } -static void -smp_xics_message_pass(int target, int msg, unsigned long data, int wait) +static void smp_xics_message_pass(int target, int msg, unsigned long data, int wait) { int i; @@ -351,27 +353,34 @@ static void __devinit pSeries_take_timebase(void) spin_unlock(&timebase_lock); } +static struct smp_ops_t pSeries_openpic_smp_ops = { + .message_pass = smp_openpic_message_pass, + .probe = smp_openpic_probe, + .kick_cpu = smp_pSeries_kick_cpu, + .setup_cpu = smp_openpic_setup_cpu, +}; + +static struct smp_ops_t pSeries_xics_smp_ops = { + .message_pass = smp_xics_message_pass, + .probe = smp_xics_probe, + .kick_cpu = smp_pSeries_kick_cpu, + .setup_cpu = smp_xics_setup_cpu, +}; + /* This is called very early */ void __init smp_init_pSeries(void) { - smp_ops = &ppc_md.smp_ops; - if (naca->interrupt_controller == IC_OPEN_PIC) { - smp_ops->message_pass = smp_openpic_message_pass; - smp_ops->probe = smp_openpic_probe; - smp_ops->setup_cpu = smp_openpic_setup_cpu; - } else { - smp_ops->message_pass = smp_xics_message_pass; - smp_ops->probe = smp_xics_probe; - smp_ops->setup_cpu = smp_xics_setup_cpu; - } + if (naca->interrupt_controller == IC_OPEN_PIC) + smp_ops = &pSeries_openpic_smp_ops; + else + smp_ops = &pSeries_xics_smp_ops; + /* Non-lpar has additional take/give timebase */ if (systemcfg->platform == PLATFORM_PSERIES) { smp_ops->give_timebase = pSeries_give_timebase; smp_ops->take_timebase = pSeries_take_timebase; } - - smp_ops->kick_cpu = smp_kick_cpu; } #endif @@ -606,6 +615,10 @@ void __init smp_prepare_cpus(unsigned int max_cpus) #endif max_cpus = smp_ops->probe(); + + /* Backup CPU 0 state if necessary */ + __save_cpu_setup(); + smp_space_timers(max_cpus); } diff --git a/include/asm-ppc64/machdep.h b/include/asm-ppc64/machdep.h index 456633c66f88..513ea51cd358 100644 --- a/include/asm-ppc64/machdep.h +++ b/include/asm-ppc64/machdep.h @@ -106,10 +106,6 @@ struct machdep_calls { */ long (*feature_call)(unsigned int feature, ...); -#ifdef CONFIG_SMP - /* functions for dealing with other cpus */ - struct smp_ops_t smp_ops; -#endif /* CONFIG_SMP */ }; extern struct machdep_calls ppc_md; -- cgit v1.2.3 From 60ce3fec2394b70870381f348159a29374bd9d11 Mon Sep 17 00:00:00 2001 From: Chas Williams Date: Wed, 11 Feb 2004 06:01:20 -0800 Subject: [ATM]: prevent userspace compilation errors with glibc-kernheaders --- include/linux/atmdev.h | 4 +--- include/linux/sonet.h | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index 0b371eb0e69d..81f37e55c510 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -200,9 +200,7 @@ struct atm_cirange { "SESSION", "HASSAP", "BOUND", "CLOSE" -#ifndef __KERNEL__ -#undef __AAL_STAT_ITEMS -#else +#ifdef __KERNEL__ #include /* wait_queue_head_t */ #include /* struct timeval */ diff --git a/include/linux/sonet.h b/include/linux/sonet.h index 30c45ec59e2e..753680296e17 100644 --- a/include/linux/sonet.h +++ b/include/linux/sonet.h @@ -56,9 +56,7 @@ struct sonet_stats { #define SONET_FRSENSE_SIZE 6 /* C1[3],H1[3] (0xff for unknown) */ -#ifndef __KERNEL__ -#undef __SONET_ITEMS -#else +#ifdef __KERNEL__ #include -- cgit v1.2.3 From 7a35dff56037cdc1fa0a46b11b48a0776928fc62 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 11 Feb 2004 07:29:54 -0800 Subject: [PATCH] New radeonfb Here is the new radeonfb. It doesn't remove the old one, just in case, though CONFIG_FB_RADEON now builds the new one. The new driver supports recent cards, has better monitor detection, including DDC2, fixes a couple of constants in the old driver, and a lot more. I had to add an empty fb_set_suspend() function to fbmem.c (the real implementation is in James tree and will be here soon). That means that Power Management on Apple laptops isn't completely right yet until the core fbdev fixes get in, but it's good enough for now. --- drivers/video/Kconfig | 25 + drivers/video/Makefile | 3 +- drivers/video/aty/Makefile | 6 + drivers/video/aty/ati_ids.h | 167 +++ drivers/video/aty/radeon_accel.c | 288 +++++ drivers/video/aty/radeon_base.c | 2387 ++++++++++++++++++++++++++++++++++++ drivers/video/aty/radeon_i2c.c | 258 ++++ drivers/video/aty/radeon_monitor.c | 908 ++++++++++++++ drivers/video/aty/radeon_pm.c | 929 ++++++++++++++ drivers/video/aty/radeonfb.h | 566 +++++++++ drivers/video/fbmem.c | 4 + drivers/video/radeonfb.c | 6 +- include/linux/fb.h | 1 + include/video/radeon.h | 1102 ++++++++++++++++- 14 files changed, 6632 insertions(+), 18 deletions(-) create mode 100644 drivers/video/aty/ati_ids.h create mode 100644 drivers/video/aty/radeon_accel.c create mode 100644 drivers/video/aty/radeon_base.c create mode 100644 drivers/video/aty/radeon_i2c.c create mode 100644 drivers/video/aty/radeon_monitor.c create mode 100644 drivers/video/aty/radeon_pm.c create mode 100644 drivers/video/aty/radeonfb.h (limited to 'include') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ba422c785e8c..cd73949a641a 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -614,6 +614,16 @@ config FB_MATROX_MULTIHEAD There is no need for enabling 'Matrox multihead support' if you have only one Matrox card in the box. +config FB_RADEON_OLD + tristate "ATI Radeon display support (Old driver)" + depends on FB && PCI + help + Choose this option if you want to use an ATI Radeon graphics card as + a framebuffer device. There are both PCI and AGP versions. You + don't need to choose this to run the Radeon in plain VGA mode. + There is a product page at + . + config FB_RADEON tristate "ATI Radeon display support" depends on FB && PCI @@ -621,9 +631,24 @@ config FB_RADEON Choose this option if you want to use an ATI Radeon graphics card as a framebuffer device. There are both PCI and AGP versions. You don't need to choose this to run the Radeon in plain VGA mode. + + If you say Y here and want DDC/I2C support you must first say Y to + "I2C support" and "I2C bit-banging support" in the character devices + section. + + If you say M here then "I2C support" and "I2C bit-banging support" + can be build either as modules or built-in. + There is a product page at . +config FB_RADEON_I2C + bool "DDC/I2C for ATI Radeon support" + depends on FB_RADEON && (I2C_ALGOBIT=FB_RADEON || I2C_ALGOBIT=y) + default y + help + Say Y here if you want DDC/I2C support for your Radeon board. + config FB_ATY128 tristate "ATI Rage128 display support" depends on FB && PCI diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 0be86a27fdc2..6bca6513ccfe 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_FB_APOLLO) += dnfb.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_Q40) += q40fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_ATARI) += atafb.o obj-$(CONFIG_FB_68328) += 68328fb.o -obj-$(CONFIG_FB_RADEON) += radeonfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o +obj-$(CONFIG_FB_RADEON_OLD) += radeonfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_NEOMAGIC) += neofb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_IGA) += igafb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_CONTROL) += controlfb.o macmodes.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o @@ -60,6 +60,7 @@ obj-$(CONFIG_FB_RIVA) += riva/ cfbimgblt.o vgastate.o obj-$(CONFIG_FB_SIS) += sis/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_ATY) += aty/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_ATY128) += aty/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o +obj-$(CONFIG_FB_RADEON) += aty/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_I810) += i810/ cfbfillrect.o cfbcopyarea.o \ cfbimgblt.o vgastate.o diff --git a/drivers/video/aty/Makefile b/drivers/video/aty/Makefile index 7515715f75cc..8aa5368736ac 100644 --- a/drivers/video/aty/Makefile +++ b/drivers/video/aty/Makefile @@ -1,7 +1,13 @@ obj-$(CONFIG_FB_ATY) += atyfb.o obj-$(CONFIG_FB_ATY128) += aty128fb.o +obj-$(CONFIG_FB_RADEON) += radeonfb.o atyfb-y := atyfb_base.o mach64_accel.o atyfb-$(CONFIG_FB_ATY_GX) += mach64_gx.o atyfb-$(CONFIG_FB_ATY_CT) += mach64_ct.o mach64_cursor.o atyfb-objs := $(atyfb-y) + +radeonfb-y := radeon_base.o radeon_pm.o radeon_monitor.o radeon_accel.o +radeonfb-$(CONFIG_FB_RADEON_I2C) += radeon_i2c.o +radeonfb-objs := $(radeonfb-y) + diff --git a/drivers/video/aty/ati_ids.h b/drivers/video/aty/ati_ids.h new file mode 100644 index 000000000000..d99e518aa5f2 --- /dev/null +++ b/drivers/video/aty/ati_ids.h @@ -0,0 +1,167 @@ +/* + * ATI PCI IDs from XFree86, kept here to make sync'ing with + * XFree much simpler. Currently, this list is only used by + * radeonfb + */ + +#define PCI_CHIP_RS100_4136 0x4136 +#define PCI_CHIP_RS200_4137 0x4137 +#define PCI_CHIP_R300_AD 0x4144 +#define PCI_CHIP_R300_AE 0x4145 +#define PCI_CHIP_R300_AF 0x4146 +#define PCI_CHIP_R300_AG 0x4147 +#define PCI_CHIP_R350_AH 0x4148 +#define PCI_CHIP_R350_AI 0x4149 +#define PCI_CHIP_R350_AJ 0x414A +#define PCI_CHIP_R350_AK 0x414B +#define PCI_CHIP_RV350_AP 0x4150 +#define PCI_CHIP_RV350_AQ 0x4151 +#define PCI_CHIP_RV360_AR 0x4152 +#define PCI_CHIP_RV350_AS 0x4153 +#define PCI_CHIP_RV350_AT 0x4154 +#define PCI_CHIP_RV350_AV 0x4156 +#define PCI_CHIP_MACH32 0x4158 +#define PCI_CHIP_RS250_4237 0x4237 +#define PCI_CHIP_R200_BB 0x4242 +#define PCI_CHIP_R200_BC 0x4243 +#define PCI_CHIP_RS100_4336 0x4336 +#define PCI_CHIP_RS200_4337 0x4337 +#define PCI_CHIP_MACH64CT 0x4354 +#define PCI_CHIP_MACH64CX 0x4358 +#define PCI_CHIP_RS250_4437 0x4437 +#define PCI_CHIP_MACH64ET 0x4554 +#define PCI_CHIP_MACH64GB 0x4742 +#define PCI_CHIP_MACH64GD 0x4744 +#define PCI_CHIP_MACH64GI 0x4749 +#define PCI_CHIP_MACH64GL 0x474C +#define PCI_CHIP_MACH64GM 0x474D +#define PCI_CHIP_MACH64GN 0x474E +#define PCI_CHIP_MACH64GO 0x474F +#define PCI_CHIP_MACH64GP 0x4750 +#define PCI_CHIP_MACH64GQ 0x4751 +#define PCI_CHIP_MACH64GR 0x4752 +#define PCI_CHIP_MACH64GS 0x4753 +#define PCI_CHIP_MACH64GT 0x4754 +#define PCI_CHIP_MACH64GU 0x4755 +#define PCI_CHIP_MACH64GV 0x4756 +#define PCI_CHIP_MACH64GW 0x4757 +#define PCI_CHIP_MACH64GX 0x4758 +#define PCI_CHIP_MACH64GY 0x4759 +#define PCI_CHIP_MACH64GZ 0x475A +#define PCI_CHIP_RV250_Id 0x4964 +#define PCI_CHIP_RV250_Ie 0x4965 +#define PCI_CHIP_RV250_If 0x4966 +#define PCI_CHIP_RV250_Ig 0x4967 +#define PCI_CHIP_MACH64LB 0x4C42 +#define PCI_CHIP_MACH64LD 0x4C44 +#define PCI_CHIP_RAGE128LE 0x4C45 +#define PCI_CHIP_RAGE128LF 0x4C46 +#define PCI_CHIP_MACH64LG 0x4C47 +#define PCI_CHIP_MACH64LI 0x4C49 +#define PCI_CHIP_MACH64LM 0x4C4D +#define PCI_CHIP_MACH64LN 0x4C4E +#define PCI_CHIP_MACH64LP 0x4C50 +#define PCI_CHIP_MACH64LQ 0x4C51 +#define PCI_CHIP_MACH64LR 0x4C52 +#define PCI_CHIP_MACH64LS 0x4C53 +#define PCI_CHIP_RADEON_LW 0x4C57 +#define PCI_CHIP_RADEON_LX 0x4C58 +#define PCI_CHIP_RADEON_LY 0x4C59 +#define PCI_CHIP_RADEON_LZ 0x4C5A +#define PCI_CHIP_RV250_Ld 0x4C64 +#define PCI_CHIP_RV250_Le 0x4C65 +#define PCI_CHIP_RV250_Lf 0x4C66 +#define PCI_CHIP_RV250_Lg 0x4C67 +#define PCI_CHIP_RAGE128MF 0x4D46 +#define PCI_CHIP_RAGE128ML 0x4D4C +#define PCI_CHIP_R300_ND 0x4E44 +#define PCI_CHIP_R300_NE 0x4E45 +#define PCI_CHIP_R300_NF 0x4E46 +#define PCI_CHIP_R300_NG 0x4E47 +#define PCI_CHIP_R350_NH 0x4E48 +#define PCI_CHIP_R350_NI 0x4E49 +#define PCI_CHIP_R360_NJ 0x4E4A +#define PCI_CHIP_R350_NK 0x4E4B +#define PCI_CHIP_RV350_NP 0x4E50 +#define PCI_CHIP_RV350_NQ 0x4E51 +#define PCI_CHIP_RV350_NR 0x4E52 +#define PCI_CHIP_RV350_NS 0x4E53 +#define PCI_CHIP_RV350_NT 0x4E54 +#define PCI_CHIP_RV350_NV 0x4E56 +#define PCI_CHIP_RAGE128PA 0x5041 +#define PCI_CHIP_RAGE128PB 0x5042 +#define PCI_CHIP_RAGE128PC 0x5043 +#define PCI_CHIP_RAGE128PD 0x5044 +#define PCI_CHIP_RAGE128PE 0x5045 +#define PCI_CHIP_RAGE128PF 0x5046 +#define PCI_CHIP_RAGE128PG 0x5047 +#define PCI_CHIP_RAGE128PH 0x5048 +#define PCI_CHIP_RAGE128PI 0x5049 +#define PCI_CHIP_RAGE128PJ 0x504A +#define PCI_CHIP_RAGE128PK 0x504B +#define PCI_CHIP_RAGE128PL 0x504C +#define PCI_CHIP_RAGE128PM 0x504D +#define PCI_CHIP_RAGE128PN 0x504E +#define PCI_CHIP_RAGE128PO 0x504F +#define PCI_CHIP_RAGE128PP 0x5050 +#define PCI_CHIP_RAGE128PQ 0x5051 +#define PCI_CHIP_RAGE128PR 0x5052 +#define PCI_CHIP_RAGE128PS 0x5053 +#define PCI_CHIP_RAGE128PT 0x5054 +#define PCI_CHIP_RAGE128PU 0x5055 +#define PCI_CHIP_RAGE128PV 0x5056 +#define PCI_CHIP_RAGE128PW 0x5057 +#define PCI_CHIP_RAGE128PX 0x5058 +#define PCI_CHIP_RADEON_QD 0x5144 +#define PCI_CHIP_RADEON_QE 0x5145 +#define PCI_CHIP_RADEON_QF 0x5146 +#define PCI_CHIP_RADEON_QG 0x5147 +#define PCI_CHIP_R200_QH 0x5148 +#define PCI_CHIP_R200_QI 0x5149 +#define PCI_CHIP_R200_QJ 0x514A +#define PCI_CHIP_R200_QK 0x514B +#define PCI_CHIP_R200_QL 0x514C +#define PCI_CHIP_R200_QM 0x514D +#define PCI_CHIP_R200_QN 0x514E +#define PCI_CHIP_R200_QO 0x514F +#define PCI_CHIP_RV200_QW 0x5157 +#define PCI_CHIP_RV200_QX 0x5158 +#define PCI_CHIP_RV100_QY 0x5159 +#define PCI_CHIP_RV100_QZ 0x515A +#define PCI_CHIP_RAGE128RE 0x5245 +#define PCI_CHIP_RAGE128RF 0x5246 +#define PCI_CHIP_RAGE128RG 0x5247 +#define PCI_CHIP_RAGE128RK 0x524B +#define PCI_CHIP_RAGE128RL 0x524C +#define PCI_CHIP_RAGE128SE 0x5345 +#define PCI_CHIP_RAGE128SF 0x5346 +#define PCI_CHIP_RAGE128SG 0x5347 +#define PCI_CHIP_RAGE128SH 0x5348 +#define PCI_CHIP_RAGE128SK 0x534B +#define PCI_CHIP_RAGE128SL 0x534C +#define PCI_CHIP_RAGE128SM 0x534D +#define PCI_CHIP_RAGE128SN 0x534E +#define PCI_CHIP_RAGE128TF 0x5446 +#define PCI_CHIP_RAGE128TL 0x544C +#define PCI_CHIP_RAGE128TR 0x5452 +#define PCI_CHIP_RAGE128TS 0x5453 +#define PCI_CHIP_RAGE128TT 0x5454 +#define PCI_CHIP_RAGE128TU 0x5455 +#define PCI_CHIP_MACH64VT 0x5654 +#define PCI_CHIP_MACH64VU 0x5655 +#define PCI_CHIP_MACH64VV 0x5656 +#define PCI_CHIP_RS300_5834 0x5834 +#define PCI_CHIP_RS300_5835 0x5835 +#define PCI_CHIP_RS300_5836 0x5836 +#define PCI_CHIP_RS300_5837 0x5837 +#define PCI_CHIP_RV280_5960 0x5960 +#define PCI_CHIP_RV280_5961 0x5961 +#define PCI_CHIP_RV280_5962 0x5962 +#define PCI_CHIP_RV280_5963 0x5963 +#define PCI_CHIP_RV280_5964 0x5964 +#define PCI_CHIP_RV280_5968 0x5968 +#define PCI_CHIP_RV280_5969 0x5969 +#define PCI_CHIP_RV280_596A 0x596A +#define PCI_CHIP_RV280_596B 0x596B +#define PCI_CHIP_RV280_5C61 0x5C61 +#define PCI_CHIP_RV280_5C63 0x5C63 diff --git a/drivers/video/aty/radeon_accel.c b/drivers/video/aty/radeon_accel.c new file mode 100644 index 000000000000..f05a2d50fb6f --- /dev/null +++ b/drivers/video/aty/radeon_accel.c @@ -0,0 +1,288 @@ +#include "radeonfb.h" + +/* the accelerated functions here are patterned after the + * "ACCEL_MMIO" ifdef branches in XFree86 + * --dte + */ +static void radeonfb_prim_fillrect(struct radeonfb_info *rinfo, + const struct fb_fillrect *region) +{ + radeon_fifo_wait(4); + + OUTREG(DP_GUI_MASTER_CNTL, + rinfo->dp_gui_master_cntl /* contains, like GMC_DST_32BPP */ + | GMC_BRUSH_SOLID_COLOR + | ROP3_P); + OUTREG(DP_BRUSH_FRGD_CLR, region->color); + OUTREG(DP_WRITE_MSK, 0xffffffff); + OUTREG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); + + radeon_fifo_wait(2); + OUTREG(DST_Y_X, (region->dy << 16) | region->dx); + OUTREG(DST_WIDTH_HEIGHT, (region->width << 16) | region->height); +} + +void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region) +{ + struct radeonfb_info *rinfo = info->par; + struct fb_fillrect modded; + int vxres, vyres; + + if (rinfo->asleep) + return; + if (radeon_accel_disabled()) { + cfb_fillrect(info, region); + return; + } + + vxres = info->var.xres; + vyres = info->var.yres; + + memcpy(&modded, region, sizeof(struct fb_fillrect)); + + if(!modded.width || !modded.height || + modded.dx >= vxres || modded.dy >= vyres) + return; + + if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx; + if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; + + radeonfb_prim_fillrect(rinfo, &modded); +} + +static void radeonfb_prim_copyarea(struct radeonfb_info *rinfo, + const struct fb_copyarea *area) +{ + radeon_fifo_wait(3); + OUTREG(DP_GUI_MASTER_CNTL, + rinfo->dp_gui_master_cntl /* i.e. GMC_DST_32BPP */ + | GMC_SRC_DSTCOLOR + | ROP3_S + | DP_SRC_RECT ); + OUTREG(DP_WRITE_MSK, 0xffffffff); + OUTREG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); + + radeon_fifo_wait(3); + OUTREG(SRC_Y_X, (area->sy << 16) | area->sx); + OUTREG(DST_Y_X, (area->dy << 16) | area->dx); + OUTREG(DST_HEIGHT_WIDTH, (area->height << 16) | area->width); +} + + +void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area) +{ + struct radeonfb_info *rinfo = info->par; + struct fb_copyarea modded; + u32 vxres, vyres; + modded.sx = area->sx; + modded.sy = area->sy; + modded.dx = area->dx; + modded.dy = area->dy; + modded.width = area->width; + modded.height = area->height; + + if (rinfo->asleep) + return; + if (radeon_accel_disabled()) { + cfb_copyarea(info, area); + return; + } + + vxres = info->var.xres; + vyres = info->var.yres; + + if(!modded.width || !modded.height || + modded.sx >= vxres || modded.sy >= vyres || + modded.dx >= vxres || modded.dy >= vyres) + return; + + if(modded.sx + modded.width > vxres) modded.width = vxres - modded.sx; + if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx; + if(modded.sy + modded.height > vyres) modded.height = vyres - modded.sy; + if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; + + radeonfb_prim_copyarea(rinfo, &modded); +} + +void radeonfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct radeonfb_info *rinfo = info->par; + + if (rinfo->asleep) + return; + radeon_engine_idle(); + + cfb_imageblit(info, image); +} + +int radeonfb_sync(struct fb_info *info) +{ + struct radeonfb_info *rinfo = info->par; + + if (rinfo->asleep) + return 0; + radeon_engine_idle(); + + return 0; +} + +void radeon_engine_reset(struct radeonfb_info *rinfo) +{ + u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset; + u32 host_path_cntl; + + radeon_engine_flush (rinfo); + + /* Some ASICs have bugs with dynamic-on feature, which are + * ASIC-version dependent, so we force all blocks on for now + * -- from XFree86 + * We don't do that on macs, things just work here with dynamic + * clocking... --BenH + */ +#ifdef CONFIG_ALL_PPC + if (_machine != _MACH_Pmac && rinfo->hasCRTC2) +#else + if (rinfo->has_CRTC2) +#endif + { + u32 tmp; + + tmp = INPLL(SCLK_CNTL); + OUTPLL(SCLK_CNTL, ((tmp & ~DYN_STOP_LAT_MASK) | + CP_MAX_DYN_STOP_LAT | + SCLK_FORCEON_MASK)); + + if (rinfo->family == CHIP_FAMILY_RV200) + { + tmp = INPLL(SCLK_MORE_CNTL); + OUTPLL(SCLK_MORE_CNTL, tmp | SCLK_MORE_FORCEON); + } + } + + clock_cntl_index = INREG(CLOCK_CNTL_INDEX); + mclk_cntl = INPLL(MCLK_CNTL); + + OUTPLL(MCLK_CNTL, (mclk_cntl | + FORCEON_MCLKA | + FORCEON_MCLKB | + FORCEON_YCLKA | + FORCEON_YCLKB | + FORCEON_MC | + FORCEON_AIC)); + + host_path_cntl = INREG(HOST_PATH_CNTL); + rbbm_soft_reset = INREG(RBBM_SOFT_RESET); + + if (rinfo->family == CHIP_FAMILY_R300 || + rinfo->family == CHIP_FAMILY_R350 || + rinfo->family == CHIP_FAMILY_RV350) { + u32 tmp; + + OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset | + SOFT_RESET_CP | + SOFT_RESET_HI | + SOFT_RESET_E2)); + INREG(RBBM_SOFT_RESET); + OUTREG(RBBM_SOFT_RESET, 0); + tmp = INREG(RB2D_DSTCACHE_MODE); + OUTREG(RB2D_DSTCACHE_MODE, tmp | (1 << 17)); /* FIXME */ + } else { + OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset | + SOFT_RESET_CP | + SOFT_RESET_HI | + SOFT_RESET_SE | + SOFT_RESET_RE | + SOFT_RESET_PP | + SOFT_RESET_E2 | + SOFT_RESET_RB); + INREG(RBBM_SOFT_RESET); + OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (u32) + ~(SOFT_RESET_CP | + SOFT_RESET_HI | + SOFT_RESET_SE | + SOFT_RESET_RE | + SOFT_RESET_PP | + SOFT_RESET_E2 | + SOFT_RESET_RB)); + INREG(RBBM_SOFT_RESET); + } + + OUTREG(HOST_PATH_CNTL, host_path_cntl | HDP_SOFT_RESET); + INREG(HOST_PATH_CNTL); + OUTREG(HOST_PATH_CNTL, host_path_cntl); + + if (rinfo->family != CHIP_FAMILY_R300 || + rinfo->family != CHIP_FAMILY_R350 || + rinfo->family != CHIP_FAMILY_RV350) + OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset); + + OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index); + OUTPLL(MCLK_CNTL, mclk_cntl); + if (rinfo->R300_cg_workaround) + R300_cg_workardound(rinfo); +} + +void radeon_engine_init (struct radeonfb_info *rinfo) +{ + unsigned long temp; + + /* disable 3D engine */ + OUTREG(RB3D_CNTL, 0); + + radeon_engine_reset(rinfo); + + radeon_fifo_wait (1); + if ((rinfo->family != CHIP_FAMILY_R300) && + (rinfo->family != CHIP_FAMILY_R350) && + (rinfo->family != CHIP_FAMILY_RV350)) + OUTREG(RB2D_DSTCACHE_MODE, 0); + + radeon_fifo_wait (3); + /* We re-read MC_FB_LOCATION from card as it can have been + * modified by XFree drivers (ouch !) + */ + rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16; + + OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) | + (rinfo->fb_local_base >> 10)); + OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10)); + OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10)); + + radeon_fifo_wait (1); +#if defined(__BIG_ENDIAN) + OUTREGP(DP_DATATYPE, HOST_BIG_ENDIAN_EN, ~HOST_BIG_ENDIAN_EN); +#else + OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN); +#endif + radeon_fifo_wait (2); + OUTREG(DEFAULT_SC_TOP_LEFT, 0); + OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX | + DEFAULT_SC_BOTTOM_MAX)); + + temp = radeon_get_dstbpp(rinfo->depth); + rinfo->dp_gui_master_cntl = ((temp << 8) | GMC_CLR_CMP_CNTL_DIS); + + radeon_fifo_wait (1); + OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl | + GMC_BRUSH_SOLID_COLOR | + GMC_SRC_DATATYPE_COLOR)); + + radeon_fifo_wait (7); + + /* clear line drawing regs */ + OUTREG(DST_LINE_START, 0); + OUTREG(DST_LINE_END, 0); + + /* set brush color regs */ + OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff); + OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000); + + /* set source color regs */ + OUTREG(DP_SRC_FRGD_CLR, 0xffffffff); + OUTREG(DP_SRC_BKGD_CLR, 0x00000000); + + /* default write mask */ + OUTREG(DP_WRITE_MSK, 0xffffffff); + + radeon_engine_idle (); +} diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c new file mode 100644 index 000000000000..c8b6c1835214 --- /dev/null +++ b/drivers/video/aty/radeon_base.c @@ -0,0 +1,2387 @@ +/* + * drivers/video/radeonfb.c + * framebuffer driver for ATI Radeon chipset video boards + * + * Copyright 2003 Ben. Herrenschmidt + * Copyright 2000 Ani Joshi + * + * i2c bits from Luca Tettamanti + * + * Special thanks to ATI DevRel team for their hardware donations. + * + * ...Insert GPL boilerplate here... + * + * Significant portions of this driver apdated from XFree86 Radeon + * driver which has the following copyright notice: + * + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * XFree86 driver authors: + * + * Kevin E. Martin + * Rickard E. Faith + * Alan Hourihane + * + */ + + +#define RADEON_VERSION "0.2.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_PPC_OF + +#include +#include +#include "../macmodes.h" + +#ifdef CONFIG_PMAC_BACKLIGHT +#include +#endif + +#ifdef CONFIG_BOOTX_TEXT +#include +#endif + +#endif /* CONFIG_PPC_OF */ + +#ifdef CONFIG_MTRR +#include +#endif + +#include