Commit 5b3e7241 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/home/bcrl/aio-2.5

into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
parents 6bd31c21 d92ff5df
......@@ -162,7 +162,6 @@ LOG := $(patsubst %.sgml, %.log, $(BOOKS))
OUT := $(patsubst %.sgml, %.out, $(BOOKS))
clean:
@rm -f core *~
@rm -f $(BOOKS)
@rm -f $(DVI) $(AUX) $(TEX) $(LOG) $(OUT)
@rm -f $(PNG-parportbook) $(EPS-parportbook)
......
......@@ -8,6 +8,8 @@ affs.txt
- info and mount options for the Amiga Fast File System.
bfs.txt
- info for the SCO UnixWare Boot Filesystem (BFS).
cifs.txt
- description of the CIFS filesystem
coda.txt
- description of the CODA filesystem.
cramfs.txt
......
This module, cifs, is a filesystem that implements the SMB/CIFS protocol, which is the
protocol used by Windows based operating systems (including Windows 2000 and its successors)
as well as Samba, OS/2 and many others operating systems and network file server appliances.
The Cifs VFS filesystem module is designed to work well with servers that implement the newer versions
(dialects) of the SMB/CIFS protocol such as Samba, the program written by Andrew Tridgell
that turns any Unix host into a file server for DOS or Windows clients, as well as Windows NT,
Windows 2000 and its successors. It is not designed to handle older smb servers well, those that
implement older versions of the dialect (such as OS/2 or Windows 95), for this purpose use smbfs.
This module can support mounting without a mount helper program. The mount syntax is:
mount //server_ip_address/share_name /mnt -o user=username,password=your_password
where "username", "your_password" and "server_ip_address" and "share_name" should be replaced
with specific values (supplied by the user) e.g.
mount //9.53.216.16/public /mnt -o user=jsmith,password=openup
This cifs implementation is designed to handle network caching (safely) as well as to implement locking,
large file (64 bit access), distributed file system ("dfs") and other advanced protocol features. It
also implements the SNIA standard for Unix extensions to CIFS (when communicating with servers such as
Samba 2.2.3 or later which support it).
For more information contact sfrench@us.ibm.com
Cifs is an SMB client (or gateway). For more info on the SMB/CIFS protocol and Samba, including
documentation, please go to http://www.samba.org/ and then on to your nearest mirror. For more
information about the cifs vfs, go to the project page at:
http://us1.samba.org/samba/Linux_CIFS_client.html
......@@ -4,9 +4,9 @@ SUBLEVEL = 41
EXTRAVERSION =
# *DOCUMENTATION*
# Too see a list of typical targets execute "make help"
# To see a list of typical targets execute "make help"
# More info can be located in ./Documentation/kbuild
# Comments in this file is targeted only to the developer, do not
# Comments in this file are targeted only to the developer, do not
# expect to learn how to build the kernel reading this file.
# We are using a recursive build, so we need to do a little thinking
......@@ -142,7 +142,7 @@ NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
MAKEFILES = $(TOPDIR)/.config
MAKEFILES = .config
GENKSYMS = /sbin/genksyms
DEPMOD = /sbin/depmod
KALLSYMS = /sbin/kallsyms
......@@ -387,17 +387,17 @@ targets += arch/$(ARCH)/vmlinux.lds.s
# Single targets
# ---------------------------------------------------------------------------
%.s: %.c FORCE
%.s: %.c scripts FORCE
+@$(call descend,$(@D),$@)
%.i: %.c FORCE
%.i: %.c scripts FORCE
+@$(call descend,$(@D),$@)
%.o: %.c FORCE
%.o: %.c scripts FORCE
+@$(call descend,$(@D),$@)
%.lst: %.c FORCE
%.lst: %.c scripts FORCE
+@$(call descend,$(@D),$@)
%.s: %.S FORCE
%.s: %.S scripts FORCE
+@$(call descend,$(@D),$@)
%.o: %.S FORCE
%.o: %.S scripts FORCE
+@$(call descend,$(@D),$@)
# FIXME: The asm symlink changes when $(ARCH) changes. That's
......@@ -883,7 +883,7 @@ if_changed_dep = $(if $(strip $? $(filter-out FORCE $(wildcard $^),$^)\
@set -e; \
$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))';) \
$(cmd_$(1)); \
$(TOPDIR)/scripts/fixdep $(depfile) $@ $(TOPDIR) '$(cmd_$(1))' > $(@D)/.$(@F).tmp; \
scripts/fixdep $(depfile) $@ '$(cmd_$(1))' > $(@D)/.$(@F).tmp; \
rm -f $(depfile); \
mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd)
......
......@@ -209,10 +209,10 @@ define rule_cc_ver_c
$(if $($(quiet)cmd_cc_ver_c),echo ' $($(quiet)cmd_cc_ver_c)';) \
$(cmd_cc_ver_c); \
if [ ! -r $(depfile) ]; then exit 1; fi; \
$(TOPDIR)/scripts/fixdep $(depfile) $@ $(TOPDIR) '$(cmd_cc_ver_c)' > $(@D)/.$(@F).tmp; \
scripts/fixdep $(depfile) $@ '$(cmd_cc_ver_c)' > $(@D)/.$(@F).tmp; \
rm -f $(depfile); \
if [ ! -r $@ ] || cmp -s $@ $@.tmp; then \
touch $(TOPDIR)/include/linux/modversions.h; \
touch include/linux/modversions.h; \
fi; \
mv -f $@.tmp $@
mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd
......@@ -312,7 +312,7 @@ cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
$(call if_changed_dep,cc_o_c)
quiet_cmd_cc_lst_c = MKLST $(echo_target)
cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && $(TOPDIR)/scripts/makelst $*.o $(TOPDIR)/System.map $(OBJDUMP) > $@
cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && sh scripts/makelst $*.o System.map $(OBJDUMP) > $@
%.lst: %.c FORCE
$(call if_changed_dep,cc_lst_c)
......@@ -561,7 +561,7 @@ if_changed_dep = $(if $(strip $? $(filter-out FORCE $(wildcard $^),$^)\
@set -e; \
$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))';) \
$(cmd_$(1)); \
$(TOPDIR)/scripts/fixdep $(depfile) $@ $(TOPDIR) '$(cmd_$(1))' > $(@D)/.$(@F).tmp; \
scripts/fixdep $(depfile) $@ '$(cmd_$(1))' > $(@D)/.$(@F).tmp; \
rm -f $(depfile); \
mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd)
......
......@@ -65,6 +65,14 @@ CONFIG_X86_NUMAQ
You will need a new lynxer.elf file to flash your firmware with - send
email to Martin.Bligh@us.ibm.com
CONFIG_X86_CYCLONE
This patch used the Cyclone performance counter on IBM x440, x360,
and other Summit based systems for calculating gettimeofday. This
fixes problems resulting from TSC skew found in multi-CEC system.
If you are suffering from time skew using a multi-CEC system, say YES.
Otherwise it is safe to say NO.
CONFIG_X86_UP_IOAPIC
An IO-APIC (I/O Advanced Programmable Interrupt Controller) is an
SMP-capable replacement for PC-style interrupt controllers. Most
......
......@@ -52,7 +52,8 @@ endif
HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
libs-y += arch/i386/lib/
core-y += arch/i386/kernel/ arch/i386/mm/ \
core-y += arch/i386/kernel/ \
arch/i386/mm/ \
arch/i386/$(MACHINE)/
drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/
drivers-$(CONFIG_PCI) += arch/i386/pci/
......
......@@ -12,6 +12,7 @@ obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
bootflag.o
obj-y += cpu/
obj-y += timers/
obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o
obj-$(CONFIG_MCA) += mca.o
obj-$(CONFIG_X86_MSR) += msr.o
......
This diff is collapsed.
#
# Makefile for x86 timers
#
obj-y := timer.o
obj-y += timer_tsc.o
obj-y += timer_pit.o
obj-$(CONFIG_X86_CYCLONE) += timer_cyclone.o
include $(TOPDIR)/Rules.make
#include <linux/kernel.h>
#include <asm/timer.h>
/* list of externed timers */
extern struct timer_opts timer_pit;
extern struct timer_opts timer_tsc;
/* list of timers, ordered by preference, NULL terminated */
static struct timer_opts* timers[] = {
&timer_tsc,
#ifndef CONFIG_X86_TSC
&timer_pit,
#endif
NULL,
};
/* iterates through the list of timers, returning the first
* one that initializes successfully.
*/
struct timer_opts* select_timer(void)
{
int i = 0;
/* find most preferred working timer */
while (timers[i]) {
if (timers[i]->init)
if (timers[i]->init() == 0)
return timers[i];
++i;
}
panic("select_timer: Cannot find a suitable timer\n");
return NULL;
}
/* Cyclone-timer:
* This code implements timer_ops for the cyclone counter found
* on IBM x440, x360, and other Summit based systems.
*
* Copyright (C) 2002 IBM, John Stultz (johnstul@us.ibm.com)
*/
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/timex.h>
#include <asm/timer.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/fixmap.h>
extern spinlock_t i8253_lock;
/* Number of usecs that the last interrupt was delayed */
static int delay_at_last_interrupt;
#define CYCLONE_CBAR_ADDR 0xFEB00CD0
#define CYCLONE_PMCC_OFFSET 0x51A0
#define CYCLONE_MPMC_OFFSET 0x51D0
#define CYCLONE_MPCS_OFFSET 0x51A8
#define CYCLONE_TIMER_FREQ 100000000
int use_cyclone = 0;
static u32* volatile cyclone_timer; /* Cyclone MPMC0 register */
static u32 last_cyclone_timer;
static void mark_offset_cyclone(void)
{
int count;
spin_lock(&i8253_lock);
/* quickly read the cyclone timer */
if(cyclone_timer)
last_cyclone_timer = cyclone_timer[0];
/* calculate delay_at_last_interrupt */
outb_p(0x00, 0x43); /* latch the count ASAP */
count = inb_p(0x40); /* read the latched count */
count |= inb(0x40) << 8;
spin_unlock(&i8253_lock);
count = ((LATCH-1) - count) * TICK_SIZE;
delay_at_last_interrupt = (count + LATCH/2) / LATCH;
}
static unsigned long get_offset_cyclone(void)
{
u32 offset;
if(!cyclone_timer)
return delay_at_last_interrupt;
/* Read the cyclone timer */
offset = cyclone_timer[0];
/* .. relative to previous jiffy */
offset = offset - last_cyclone_timer;
/* convert cyclone ticks to microseconds */
/* XXX slow, can we speed this up? */
offset = offset/(CYCLONE_TIMER_FREQ/1000000);
/* our adjusted time offset in microseconds */
return delay_at_last_interrupt + offset;
}
static int init_cyclone(void)
{
u32* reg;
u32 base; /* saved cyclone base address */
u32 pageaddr; /* page that contains cyclone_timer register */
u32 offset; /* offset from pageaddr to cyclone_timer register */
int i;
/*make sure we're on a summit box*/
/*XXX need to use proper summit hooks! such as xapic -john*/
if(!use_cyclone) return 0;
printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
/* find base address */
pageaddr = (CYCLONE_CBAR_ADDR)&PAGE_MASK;
offset = (CYCLONE_CBAR_ADDR)&(~PAGE_MASK);
set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
if(!reg){
printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
return 0;
}
base = *reg;
if(!base){
printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
return 0;
}
/* setup PMCC */
pageaddr = (base + CYCLONE_PMCC_OFFSET)&PAGE_MASK;
offset = (base + CYCLONE_PMCC_OFFSET)&(~PAGE_MASK);
set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
if(!reg){
printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
return 0;
}
reg[0] = 0x00000001;
/* setup MPCS */
pageaddr = (base + CYCLONE_MPCS_OFFSET)&PAGE_MASK;
offset = (base + CYCLONE_MPCS_OFFSET)&(~PAGE_MASK);
set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
if(!reg){
printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
return 0;
}
reg[0] = 0x00000001;
/* map in cyclone_timer */
pageaddr = (base + CYCLONE_MPMC_OFFSET)&PAGE_MASK;
offset = (base + CYCLONE_MPMC_OFFSET)&(~PAGE_MASK);
set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
cyclone_timer = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
if(!cyclone_timer){
printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
return 0;
}
/*quick test to make sure its ticking*/
for(i=0; i<3; i++){
u32 old = cyclone_timer[0];
int stall = 100;
while(stall--) barrier();
if(cyclone_timer[0] == old){
printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
cyclone_timer = 0;
return 0;
}
}
/* Everything looks good! */
return 1;
}
#if 0 /* XXX future work */
static void delay_cyclone(unsigned long loops)
{
unsigned long bclock, now;
if(!cyclone_timer)
return;
bclock = cyclone_timer[0];
do {
rep_nop();
now = cyclone_timer[0];
} while ((now-bclock) < loops);
}
#endif
/************************************************************/
/* cyclone timer_opts struct */
struct timer_opts timer_cyclone = {
init: init_cyclone,
mark_offset: mark_offset_cyclone,
get_offset: get_offset_cyclone
};
/*
* This code largely moved from arch/i386/kernel/time.c.
* See comments there for proper credits.
*/
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/timer.h>
#include <asm/io.h>
extern spinlock_t i8259A_lock;
extern spinlock_t i8253_lock;
#include "do_timer.h"
static int init_pit(void)
{
return 0;
}
static void mark_offset_pit(void)
{
/* nothing needed */
}
/* This function must be called with interrupts disabled
* It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
*
* However, the pc-audio speaker driver changes the divisor so that
* it gets interrupted rather more often - it loads 64 into the
* counter rather than 11932! This has an adverse impact on
* do_gettimeoffset() -- it stops working! What is also not
* good is that the interval that our timer function gets called
* is no longer 10.0002 ms, but 9.9767 ms. To get around this
* would require using a different timing source. Maybe someone
* could use the RTC - I know that this can interrupt at frequencies
* ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix
* it so that at startup, the timer code in sched.c would select
* using either the RTC or the 8253 timer. The decision would be
* based on whether there was any other device around that needed
* to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz,
* and then do some jiggery to have a version of do_timer that
* advanced the clock by 1/1024 s. Every time that reached over 1/100
* of a second, then do all the old code. If the time was kept correct
* then do_gettimeoffset could just return 0 - there is no low order
* divider that can be accessed.
*
* Ideally, you would be able to use the RTC for the speaker driver,
* but it appears that the speaker driver really needs interrupt more
* often than every 120 us or so.
*
* Anyway, this needs more thought.... pjsg (1993-08-28)
*
* If you are really that interested, you should be reading
* comp.protocols.time.ntp!
*/
static unsigned long get_offset_pit(void)
{
int count;
static int count_p = LATCH; /* for the first call after boot */
static unsigned long jiffies_p = 0;
/*
* cache volatile jiffies temporarily; we have IRQs turned off.
*/
unsigned long jiffies_t;
/* gets recalled with irq locally disabled */
spin_lock(&i8253_lock);
/* timer count may underflow right here */
outb_p(0x00, 0x43); /* latch the count ASAP */
count = inb_p(0x40); /* read the latched count */
/*
* We do this guaranteed double memory access instead of a _p
* postfix in the previous port access. Wheee, hackady hack
*/
jiffies_t = jiffies;
count |= inb_p(0x40) << 8;
/* VIA686a test code... reset the latch if count > max + 1 */
if (count > LATCH) {
outb_p(0x34, 0x43);
outb_p(LATCH & 0xff, 0x40);
outb(LATCH >> 8, 0x40);
count = LATCH - 1;
}
spin_unlock(&i8253_lock);
/*
* avoiding timer inconsistencies (they are rare, but they happen)...
* there are two kinds of problems that must be avoided here:
* 1. the timer counter underflows
* 2. hardware problem with the timer, not giving us continuous time,
* the counter does small "jumps" upwards on some Pentium systems,
* (see c't 95/10 page 335 for Neptun bug.)
*/
if( jiffies_t == jiffies_p ) {
if( count > count_p ) {
/* the nutcase */
count = do_timer_overflow(count);
}
} else
jiffies_p = jiffies_t;
count_p = count;
count = ((LATCH-1) - count) * TICK_SIZE;
count = (count + LATCH/2) / LATCH;
return count;
}
/* tsc timer_opts struct */
struct timer_opts timer_pit = {
.init = init_pit,
.mark_offset = mark_offset_pit,
.get_offset = get_offset_pit,
};
/*
* This code largely moved from arch/i386/kernel/time.c.
* See comments there for proper credits.
*/
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/timex.h>
#include <linux/errno.h>
#include <asm/timer.h>
#include <asm/io.h>
extern int x86_udelay_tsc;
extern spinlock_t i8253_lock;
static int use_tsc;
/* Number of usecs that the last interrupt was delayed */
static int delay_at_last_interrupt;
static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
/* Cached *multiplier* to convert TSC counts to microseconds.
* (see the equation below).
* Equal to 2^32 * (1 / (clocks per usec) ).
* Initialized in time_init.
*/
unsigned long fast_gettimeoffset_quotient;
static unsigned long get_offset_tsc(void)
{
register unsigned long eax, edx;
/* Read the Time Stamp Counter */
rdtsc(eax,edx);
/* .. relative to previous jiffy (32 bits is enough) */
eax -= last_tsc_low; /* tsc_low delta */
/*
* Time offset = (tsc_low delta) * fast_gettimeoffset_quotient
* = (tsc_low delta) * (usecs_per_clock)
* = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy)
*
* Using a mull instead of a divl saves up to 31 clock cycles
* in the critical path.
*/
__asm__("mull %2"
:"=a" (eax), "=d" (edx)
:"rm" (fast_gettimeoffset_quotient),
"0" (eax));
/* our adjusted time offset in microseconds */
return delay_at_last_interrupt + edx;
}
static void mark_offset_tsc(void)
{
int count;
/*
* It is important that these two operations happen almost at
* the same time. We do the RDTSC stuff first, since it's
* faster. To avoid any inconsistencies, we need interrupts
* disabled locally.
*/
/*
* Interrupts are just disabled locally since the timer irq
* has the SA_INTERRUPT flag set. -arca
*/
/* read Pentium cycle counter */
rdtscl(last_tsc_low);
spin_lock(&i8253_lock);
outb_p(0x00, 0x43); /* latch the count ASAP */
count = inb_p(0x40); /* read the latched count */
count |= inb(0x40) << 8;
spin_unlock(&i8253_lock);
count = ((LATCH-1) - count) * TICK_SIZE;
delay_at_last_interrupt = (count + LATCH/2) / LATCH;
}
/* ------ Calibrate the TSC -------
* Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
* Too much 64-bit arithmetic here to do this cleanly in C, and for
* accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
* output busy loop as low as possible. We avoid reading the CTC registers
* directly because of the awkward 8-bit access mechanism of the 82C54
* device.
*/
#define CALIBRATE_LATCH (5 * LATCH)
#define CALIBRATE_TIME (5 * 1000020/HZ)
static unsigned long __init calibrate_tsc(void)
{
/* Set the Gate high, disable speaker */
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
/*
* Now let's take care of CTC channel 2
*
* Set the Gate high, program CTC channel 2 for mode 0,
* (interrupt on terminal count mode), binary count,
* load 5 * LATCH count, (LSB and MSB) to begin countdown.
*/
outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */
outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */
{
unsigned long startlow, starthigh;
unsigned long endlow, endhigh;
unsigned long count;
rdtsc(startlow,starthigh);
count = 0;
do {
count++;
} while ((inb(0x61) & 0x20) == 0);
rdtsc(endlow,endhigh);
last_tsc_low = endlow;
/* Error: ECTCNEVERSET */
if (count <= 1)
goto bad_ctc;
/* 64-bit subtract - gcc just messes up with long longs */
__asm__("subl %2,%0\n\t"
"sbbl %3,%1"
:"=a" (endlow), "=d" (endhigh)
:"g" (startlow), "g" (starthigh),
"0" (endlow), "1" (endhigh));
/* Error: ECPUTOOFAST */
if (endhigh)
goto bad_ctc;
/* Error: ECPUTOOSLOW */
if (endlow <= CALIBRATE_TIME)
goto bad_ctc;
__asm__("divl %2"
:"=a" (endlow), "=d" (endhigh)
:"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
return endlow;
}
/*
* The CTC wasn't reliable: we got a hit on the very first read,
* or the CPU was so fast/slow that the quotient wouldn't fit in
* 32 bits..
*/
bad_ctc:
return 0;
}
#ifdef CONFIG_CPU_FREQ
static int
time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
void *data)
{
struct cpufreq_freqs *freq = data;
unsigned int i;
if (!cpu_has_tsc)
return 0;
switch (val) {
case CPUFREQ_PRECHANGE:
if ((freq->old < freq->new) &&
((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == 0))) {
cpu_khz = cpufreq_scale(cpu_khz, freq->old, freq->new);
fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_quotient, freq->new, freq->old);
}
for (i=0; i<NR_CPUS; i++)
if ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == i))
cpu_data[i].loops_per_jiffy = cpufreq_scale(cpu_data[i].loops_per_jiffy, freq->old, freq->new);
break;
case CPUFREQ_POSTCHANGE:
if ((freq->new < freq->old) &&
((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == 0))) {
cpu_khz = cpufreq_scale(cpu_khz, freq->old, freq->new);
fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_quotient, freq->new, freq->old);
}
for (i=0; i<NR_CPUS; i++)
if ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == i))
cpu_data[i].loops_per_jiffy = cpufreq_scale(cpu_data[i].loops_per_jiffy, freq->old, freq->new);
break;
}
return 0;
}
static struct notifier_block time_cpufreq_notifier_block = {
notifier_call: time_cpufreq_notifier
};
#endif
static int init_tsc(void)
{
/*
* If we have APM enabled or the CPU clock speed is variable
* (CPU stops clock on HLT or slows clock to save power)
* then the TSC timestamps may diverge by up to 1 jiffy from
* 'real time' but nothing will break.
* The most frequent case is that the CPU is "woken" from a halt
* state by the timer interrupt itself, so we get 0 error. In the
* rare cases where a driver would "wake" the CPU and request a
* timestamp, the maximum error is < 1 jiffy. But timestamps are
* still perfectly ordered.
* Note that the TSC counter will be reset if APM suspends
* to disk; this won't break the kernel, though, 'cuz we're
* smart. See arch/i386/kernel/apm.c.
*/
/*
* Firstly we have to do a CPU check for chips with
* a potentially buggy TSC. At this point we haven't run
* the ident/bugs checks so we must run this hook as it
* may turn off the TSC flag.
*
* NOTE: this doesnt yet handle SMP 486 machines where only
* some CPU's have a TSC. Thats never worked and nobody has
* moaned if you have the only one in the world - you fix it!
*/
dodgy_tsc();
if (cpu_has_tsc) {
unsigned long tsc_quotient = calibrate_tsc();
if (tsc_quotient) {
fast_gettimeoffset_quotient = tsc_quotient;
use_tsc = 1;
/*
* We could be more selective here I suspect
* and just enable this for the next intel chips ?
*/
x86_udelay_tsc = 1;
/* report CPU clock rate in Hz.
* The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
* clock/second. Our precision is about 100 ppm.
*/
{ unsigned long eax=0, edx=1000;
__asm__("divl %2"
:"=a" (cpu_khz), "=d" (edx)
:"r" (tsc_quotient),
"0" (eax), "1" (edx));
printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000);
}
#ifdef CONFIG_CPU_FREQ
cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
#endif
return 0;
}
}
return -ENODEV;
}
/************************************************************/
/* tsc timer_opts struct */
struct timer_opts timer_tsc = {
.init = init_tsc,
.mark_offset = mark_offset_tsc,
.get_offset = get_offset_tsc,
};
......@@ -689,21 +689,28 @@ static int cdrom_read_mech_status(struct cdrom_device_info *cdi,
static int cdrom_slot_status(struct cdrom_device_info *cdi, int slot)
{
struct cdrom_changer_info info;
struct cdrom_changer_info *info;
int ret;
cdinfo(CD_CHANGER, "entering cdrom_slot_status()\n");
if (cdi->sanyo_slot)
return CDS_NO_INFO;
if ((ret = cdrom_read_mech_status(cdi, &info)))
return ret;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
if ((ret = cdrom_read_mech_status(cdi, info)))
goto out_free;
if (info.slots[slot].disc_present)
return CDS_DISC_OK;
if (info->slots[slot].disc_present)
ret = CDS_DISC_OK;
else
return CDS_NO_DISC;
ret = CDS_NO_DISC;
out_free:
kfree(info);
return ret;
}
/* Return the number of slots for an ATAPI/SCSI cdrom,
......@@ -713,15 +720,20 @@ int cdrom_number_of_slots(struct cdrom_device_info *cdi)
{
int status;
int nslots = 1;
struct cdrom_changer_info info;
struct cdrom_changer_info *info;
cdinfo(CD_CHANGER, "entering cdrom_number_of_slots()\n");
/* cdrom_read_mech_status requires a valid value for capacity: */
cdi->capacity = 0;
if ((status = cdrom_read_mech_status(cdi, &info)) == 0)
nslots = info.hdr.nslots;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
if ((status = cdrom_read_mech_status(cdi, info)) == 0)
nslots = info->hdr.nslots;
kfree(info);
return nslots;
}
......@@ -755,7 +767,7 @@ static int cdrom_load_unload(struct cdrom_device_info *cdi, int slot)
static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
{
struct cdrom_changer_info info;
struct cdrom_changer_info *info;
int curslot;
int ret;
......@@ -771,10 +783,17 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
return cdrom_load_unload(cdi, -1);
}
if ((ret = cdrom_read_mech_status(cdi, &info)))
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
if ((ret = cdrom_read_mech_status(cdi, info))) {
kfree(info);
return ret;
}
curslot = info.hdr.curslot;
curslot = info->hdr.curslot;
kfree(info);
if (cdi->use_count > 1 || keeplocked) {
if (slot == CDSL_CURRENT) {
......@@ -1502,7 +1521,8 @@ int cdrom_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
}
case CDROM_MEDIA_CHANGED: {
struct cdrom_changer_info info;
struct cdrom_changer_info *info;
int changed;
cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n");
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
......@@ -1515,10 +1535,18 @@ int cdrom_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
if ((unsigned int)arg >= cdi->capacity)
return -EINVAL;
if ((ret = cdrom_read_mech_status(cdi, &info)))
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
if ((ret = cdrom_read_mech_status(cdi, info))) {
kfree(info);
return ret;
}
return info.slots[arg].change;
changed = info->slots[arg].change;
kfree(info);
return changed;
}
case CDROM_SET_OPTIONS: {
......
......@@ -30,6 +30,10 @@ static struct file_operations raw_ctl_fops;
/*
* Open/close code for raw IO.
*
* We just rewrite the i_mapping for the /dev/raw/rawN file descriptor to
* point at the blockdev's address_space and set the file handle to use
* O_DIRECT.
*
* Set the device's soft blocksize to the minimum possible. This gives the
* finest possible alignment and has no adverse impact on performance.
*/
......@@ -56,7 +60,12 @@ static int raw_open(struct inode *inode, struct file *filp)
err = blkdev_get(bdev, filp->f_mode, 0, BDEV_RAW);
if (!err) {
err = set_blocksize(bdev, bdev_hardsect_size(bdev));
raw_devices[minor].inuse++;
if (err == 0) {
raw_devices[minor].inuse++;
filp->f_dentry->d_inode->i_mapping =
bdev->bd_inode->i_mapping;
filp->f_flags |= O_DIRECT;
}
}
}
up(&raw_mutex);
......@@ -200,72 +209,22 @@ raw_ctl_ioctl(struct inode *inode, struct file *filp,
return err;
}
static ssize_t
rw_raw_dev(int rw, struct file *filp, const struct iovec *iov, unsigned long nr_segs, loff_t *offp)
{
const int minor = minor(filp->f_dentry->d_inode->i_rdev);
struct block_device *bdev = raw_devices[minor].binding;
struct inode *inode = bdev->bd_inode;
size_t count = iov_length(iov, nr_segs);
ssize_t ret = 0;
if (count == 0)
goto out;
if ((ssize_t)count < 0)
return -EINVAL;
if (*offp >= inode->i_size)
return -ENXIO;
if (count + *offp > inode->i_size) {
count = inode->i_size - *offp;
nr_segs = iov_shorten((struct iovec *)iov, nr_segs, count);
}
ret = generic_file_direct_IO(rw, filp, iov, *offp, nr_segs);
if (ret > 0)
*offp += ret;
out:
return ret;
}
static ssize_t
raw_read(struct file *filp, char *buf, size_t size, loff_t *offp)
{
struct iovec local_iov = { .iov_base = buf, .iov_len = size};
return rw_raw_dev(READ, filp, &local_iov, 1, offp);
}
static ssize_t
raw_write(struct file *filp, const char *buf, size_t size, loff_t *offp)
static ssize_t raw_file_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
struct iovec local_iov = { .iov_base = (char *)buf, .iov_len = size};
struct iovec local_iov = { .iov_base = (void *)buf, .iov_len = count };
return rw_raw_dev(WRITE, filp, &local_iov, 1, offp);
}
static ssize_t
raw_readv(struct file *filp, const struct iovec *iov, unsigned long nr_segs, loff_t *offp)
{
return rw_raw_dev(READ, filp, iov, nr_segs, offp);
}
static ssize_t
raw_writev(struct file *filp, const struct iovec *iov, unsigned long nr_segs, loff_t *offp)
{
return rw_raw_dev(WRITE, filp, iov, nr_segs, offp);
return generic_file_write_nolock(file, &local_iov, 1, ppos);
}
static struct file_operations raw_fops = {
.read = raw_read,
.write = raw_write,
.read = generic_file_read,
.write = raw_file_write,
.open = raw_open,
.release= raw_release,
.ioctl = raw_ioctl,
.readv = raw_readv,
.writev = raw_writev,
.readv = generic_file_readv,
.writev = generic_file_writev,
.owner = THIS_MODULE,
};
......
......@@ -137,7 +137,7 @@ include $(TOPDIR)/Rules.make
$(obj)/53c7,8xx.o: $(obj)/53c8xx_d.h $(obj)/53c8xx_u.h
$(obj)/53c7xx.o: $(obj)/53c7xx_d.h $(obj)/53c7xx_u.h
$(obj)/sim710.o: $(obj)/sim710_d.h
$(obj)/53c700.o $(MODVERDIR)/53c700.ver: $(obj)/53c700_d.h
$(obj)/53c700.o $(MODVERDIR)/$(obj)/53c700.ver: $(obj)/53c700_d.h
# If you want to play with the firmware, uncomment
# GENERATE_FIRMWARE := 1
......@@ -162,4 +162,4 @@ $(obj)/sim710_u.h: $(obj)/sim710_d.h
$(obj)/53c700_d.h: $(src)/53c700.scr $(src)/script_asm.pl
$(PERL) -s $(src)/script_asm.pl -ncr7x0_family $@ $(@:_d.h=_u.h) < $<
endif
\ No newline at end of file
endif
EXTRA_CFLAGS += -I$(TOPDIR)/drivers/scsi
# Adaptec aacraid
obj-$(CONFIG_SCSI_AACRAID) := aacraid.o
aacraid-objs := linit.o aachba.o commctrl.o comminit.o commsup.o \
......
......@@ -902,10 +902,12 @@ static void tscam(struct Scsi_Host *host)
0x38, 0x31, 0x32, 0x2b, 0x34, 0x2d, 0x2e, 0x27
};
/* I can't believe we need this before we've even done anything. Remove it
* and see if anyone bitches.
for (i = 0; i < 0x10; i++) {
udelay(0xffff);
}
*/
tmport = dev->ioport + 1;
outb(0x08, tmport++);
......@@ -993,8 +995,7 @@ static void tscam(struct Scsi_Host *host)
inb(0x80); /* 2 deskew delay(45ns*2=90ns) */
val &= 0x007f; /* no bsy */
outw(val, tmport);
udelay(0xffff); /* recommanded SCAM selection response time */
udelay(0xffff);
mdelay(128);
val &= 0x00fb; /* after 1ms no msg */
outw(val, tmport);
wait_nomsg:
......@@ -2420,9 +2421,9 @@ static int atp870u_detect(Scsi_Host_Template * tpnt)
k = (inb(tmport) & 0xf3) | 0x10;
outb(k, tmport);
outb((k & 0xdf), tmport);
udelay(0x8000);
mdelay(32);
outb(k, tmport);
udelay(0x8000);
mdelay(32);
tmport = base_io;
outb((host_id | 0x08), tmport);
tmport += 0x18;
......@@ -2539,9 +2540,9 @@ static int atp870u_detect(Scsi_Host_Template * tpnt)
outb(k, tmport);
tmport += 0x03;
outb(0x20, tmport);
udelay(0x8000);
mdelay(32);
outb(0, tmport);
udelay(0x8000);
mdelay(32);
tmport = base_io + 0x5b;
inb(tmport);
tmport -= 0x04;
......@@ -2581,7 +2582,7 @@ static int atp870u_detect(Scsi_Host_Template * tpnt)
shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */
}
shpnt->irq = irq;
restore_flags(flags);
spin_unlock_irqrestore(shpnt->host_lock, flags);
if (dev_id[h] == 0x8081) {
request_region(base_io, 0x60, "atp870u"); /* Register the IO ports that we use */
} else {
......
......@@ -950,6 +950,33 @@ CONFIG_VXFS_FS
module, say M here and read <file:Documentation/modules.txt>. If
unsure, say N.
CIFS (Common Internet File System) support
CONFIG_CIFS
This is the client VFS module for the Common Internet File System
(CIFS) protocol which is the successor to the Server Message Block
(SMB) protocol, the native file sharing mechanism for most early
PC operating systems. CIFS is fully supported by current network
file servers such as Windows 2000 (including Windows NT version 4
and Windows XP) as well by Samba (which provides excellent CIFS
server support for Linux and many other operating systems). For
production systems the smbfs module may be used instead of this
cifs module since smbfs is currently more stable and provides
support for older servers. The intent of this module is to provide the
most advanced network file system function for CIFS compliant servers,
including support for dfs (heirarchical name space), secure per-user
session establishment, safe distributed caching (oplock), optional
packet signing, Unicode and other internationalization improvements, and
optional Winbind (nsswitch) integration. This module is in an early
development stage, so unless you are specifically interested in this
filesystem, just say N.
CIFS Debugging
CONFIG_CIFS_DEBUG
If you are experiencing any problems with the CIFS filesystem, say
Y here. This will result in additional debugging messages to be
written to the system log. Under normal circumstances, this
results in very little overhead.
CONFIG_SMB_FS
SMB (Server Message Block) is the protocol Windows for Workgroups
(WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share
......
......@@ -140,6 +140,8 @@ if [ "$CONFIG_NET" = "y" ]; then
fi
define_tristate CONFIG_EXPORTFS $CONFIG_NFSD
dep_tristate 'CIFS support (advanced network filesystem for Samba, Window and other CIFS compliant servers)(EXPERIMENTAL)' CONFIG_CIFS $CONFIG_INET
dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET
if [ "$CONFIG_SMB_FS" != "n" ]; then
bool ' Use a default NLS' CONFIG_SMB_NLS_DEFAULT
......
......@@ -65,6 +65,7 @@ obj-$(CONFIG_LOCKD) += lockd/
obj-$(CONFIG_NLS) += nls/
obj-$(CONFIG_SYSV_FS) += sysv/
obj-$(CONFIG_SMB_FS) += smbfs/
obj-$(CONFIG_CIFS) += cifs/
obj-$(CONFIG_NCP_FS) += ncpfs/
obj-$(CONFIG_HPFS_FS) += hpfs/
obj-$(CONFIG_NTFS_FS) += ntfs/
......
Original Author
===============
Steve French (sfrench@samba.org)
The author wishes to express his appreciation and thanks to:
Andrew Tridgell (Samba team) for his early suggestions about smb/cifs VFS
improvements. Thanks to IBM for allowing me the time and test resources to pursue
this project. Jim McDonough from IBM (and the Samba Team) for his help.
The IBM Linux JFS team for explaining many esoteric Linux filesystem features.
Dave Boutcher of IBM Rochester (author of the OS/400 smb/cifs filesystem client)
for proving years ago that a very good smb/cifs client could be done on a Unix like
operating system. Volker Lendecke, Andrew Tridgell, Urban Widmark, John Newbigin
and others for their work on the Linux smbfs module over the years. Thanks to
the other members of the Storage Network Industry Association CIFS Technical
Workgroup for their work specifying this highly complex protocol and finally
thanks to the Samba team for their technical advice and encouragement.
Version 0.54
------------
Fix problem with captive thread hanging around at unmount time. Adjust to 2.5.42-pre
changes to superblock layout. Remove wasteful allocation of smb buffers (now the send
buffer is reused for responses). Add more oplock handling. Additional minor cleanup.
Version 0.53
------------
More stylistic updates to better match kernel style. Add additional statistics
for filesystem which can be viewed via /proc/fs/cifs. Add more pieces of NTLMv2
and CIFS Packet Signing enablement.
Version 0.52
------------
Replace call to sleep_on with safer wait_on_event.
Make stylistic changes to better match kernel style recommendations.
Remove most typedef usage (except for the PDUs themselves).
Version 0.51
------------
Update mount so the -unc mount option is no longer required (the ip address can be specified
in a UNC style device name. Implementation of readpage/writepage started.
Version 0.50
------------
Fix intermittent problem with incorrect smb header checking on badly
fragmented tcp responses
Version 0.49
------------
Fixes to setting of allocation size and file size.
Version 0.48
------------
Various 2.5.38 fixes. Now works on 2.5.38
Version 0.47
------------
Prepare for 2.5 kernel merge. Remove ifdefs.
Version 0.46
------------
Socket buffer management fixes. Fix dual free.
Version 0.45
------------
Various big endian fixes for hardlinks and symlinks and also for dfs.
Version 0.44
------------
Various big endian fixes for servers with Unix extensions such as Samba
Version 0.43
------------
Various FindNext fixes for incorrect filenames on large directory searches on big endian
clients. basic posix file i/o tests now work on big endian machines, not just le
Version 0.42
------------
SessionSetup and NegotiateProtocol now work from Big Endian machines.
Various Big Endian fixes found during testing on the Linux on 390. Various fixes for compatability with older
versions of 2.4 kernel (now builds and works again on kernels at least as early as 2.4.7).
Version 0.41
------------
Various minor fixes for Connectathon Posix "basic" file i/o test suite. Directory caching fixed so hardlinked
files now return the correct rumber of links on fstat as they are repeatedly linked and unlinked.
Version 0.40
------------
Implemented "Raw" (i.e. not encapsulated in SPNEGO) NTLMSSP (i.e. the Security Provider Interface used to negotiate
session advanced session authentication). Raw NTLMSSP is preferred by Windows 2000 Professional and Windows XP.
Began implementing support for SPNEGO encapsulation of NTLMSSP based session authentication blobs
(which is the mechanism preferred by Windows 2000 server in the absence of Kerberos).
Version 0.38
------------
Introduced optional mount helper utility mount.cifs and made coreq changes to cifs vfs to enable
it. Fixed a few bugs in the DFS code (e.g. bcc two bytes too short and incorrect uid in PDU).
Version 0.37
------------
Rewrote much of connection and mount/unmount logic to handle bugs with
multiple uses to same share, multiple users to same server etc.
Version 0.36
------------
Fixed major problem with dentry corruption (missing call to dput)
Version 0.35
------------
Rewrite of readdir code to fix bug. Various fixes for bigendian machines.
Begin adding oplock support. Multiusermount and oplockEnabled flags added to /proc/fs/cifs
although corresponding function not fully implemented in the vfs yet
Version 0.34
------------
Fixed dentry caching bug, misc. cleanup
Version 0.33
------------
Fixed 2.5 support to handle build and configure changes as well as misc. 2.5 changes. Now can build
on current 2.5 beta version (2.5.24) of the Linux kernel as well as on 2.4 Linux kernels.
Support for STATUS codes (newer 32 bit NT error codes) added. DFS support begun to be added.
Version 0.32
------------
Unix extensions (symlink, readlink, hardlink, chmod and some chgrp and chown) implemented
and tested against Samba 2.2.5
Version 0.31
------------
1) Fixed lockrange to be correct (it was one byte too short)
2) Fixed GETLK (i.e. the fcntl call to test a range of bytes in a file to see if locked) to correctly
show range as locked when there is a conflict with an existing lock.
3) default file perms are now 2767 (indicating support for mandatory locks) instead of 777 for directories
in most cases. Eventually will offer optional ability to query server for the correct perms.
3) Fixed eventual trap when mounting twice to different shares on the same server when the first succeeded
but the second one was invalid and failed (the second one was incorrectly disconnecting the tcp and smb
session)
4) Fixed error logging of valid mount options
5) Removed logging of password field.
6) Moved negotiate, treeDisconnect and uloggoffX (only tConx and SessSetup remain in connect.c) to cifssmb.c
and cleaned them up and made them more consistent with other cifs functions.
7) Server support for Unix extensions is now fully detected and FindFirst is implemented both ways
(with or without Unix exentions) but FindNext and QueryPathInfo with the Unix extensions are not completed,
nor is the symlink support using the Unix extensions
8) Started adding the readlink and follow_link code
Version 0.3
-----------
Initial drop
#
# Makefile for Linux CIFS VFS client
#
obj-$(CONFIG_CIFS) += cifs.o
cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o
include $(TOPDIR)/Rules.make
This is the CIFS VFS support for Linux. It supports many advanced network filesystem
features such as heirarchical dfs like filesystem, hardlinks, locking and more.
It was designed to comply with the SNIA CIFS Technical Reference (which supersedes
the 1992 X/Open SMB Standard) as well as to perform best practice practical
interoperability with Windows 2000, Windows XP, Samba and equivalent
servers.
For questions or bug reports please contact sfrench@samba.org (sfrench@us.ibm.com)
Build instructions:
==================
extract the kernel from http://www.cifs.bkbits.net/linux-2.5 or
http://www.cifs.bkbits.net/linux-2.4
make menuconfig (or make xconfig)
select cifs from within the network filesystem choices
save and exit
make dep
make modules (or "make" if you did not select CIFS VFS to be built as a module)
Installation instructions:
=========================
If you have built the CIFS vfs as module (successfully)you
simply type "make modules_install" (or if you prefer manually copy the file to
the modules directory e.g. /lib/modules/2.4.10-4GB/kernel/fs/cifs/cifs.o).
If you have built the CIFS vfs into the kernel itself, follow the instructions
for your distribution on how to install a new kernel (usually you
would simply type "make install").
If you do not have the utility mount.cifs (in the Samba 3.0 source tree and on the
CIFS VFS web site) copy it to the directory /sbin (or the same directory in which
mount.smbfs resides). Although no helper software is required, the installation
of mount.cifs is recommended. Eventually the Samba 3.0 utility program "net"
may also be helpful since it may someday provide easier mount syntax for users used
to Windows e.g.
net use <mount point> <UNC name or cifs URL>
and there will likely be other helper programs available ala smbmount to provide
additional optional function in the future. Note that running Winbind on all
of your Linux clients is useful in mapping Uids and Gids consistently to the
proper network user.
Samba Considerations
====================
To get the maximum benefit from the CIFS VFS, we recommend using a server that
supports the SNIA CIFS Unix Extensions standard (e.g. Samba 2.2.5 or Samba 3.0)
but the CIFS vfs works fine with a wide variety of CIFS servers. Note that the
uid, gid and file permissions will display default values if you do not have
a server that supports the Unix extensions for CIFS (such as Samba 2.2.3 or
later). To enable the Unix CIFS Extensions in the Samba server, add the line:
unix extensions = yes
to your smb.conf file on the server. Note that the following smb.conf settings are
also useful (on the Samba server) when the majority of clients are Unix
or Linux:
case sensitive = yes
delete readonly = yes
Some administrators also change the "map archive" and the "create mask" parameters
from their defaults. For more information on these see the manual pages
("man smb.conf") on the Samba server system. Note that the cifs vfs, unlike the
smbfs vfs, does not read the smb.conf on the client system (the few optional settings
are passed in on mount via -o parameters instead).
Use instructions:
================
Once the CIFS VFS support is built into the kernel or installed as a module (cifs.o),
you can use mount syntax like the following to access Samba or Windows servers:
mount -t cifs //9.53.216.11/e$ /mnt -o user=myname,pass=mypassword
after -o the following cifs vfs specific options are supported:
user=<username>
pass=<password>
domain=<domain name>
TCP names (in addition to ip addresses) will be available when the mount helper
(mount.cifs) is complete
Restrictions
============
Servers must support the NTLM SMB dialect (which is the most recent, supported by Samba
and Windows NT, 2000 and XP and many other SMB/CIFS servers) and servers must support
either "pure-TCP" (port 445 TCP/IP CIFS connections) or RFC 1001/1002 support for
"Netbios-Over-TCP/IP." Neither of these is likely to be a problem as most servers
support this. IPv6 support is planned for the future.
Misc /proc/fs/cifs Flags and Debug Info
=======================================
Various experimental features and tracing can be enabled by changing flags in /proc/fs/cifs (after
the cifs module has been installed or built into the kernel, e.g. insmod cifs). To enable
a feature you can set it to 1 e.g. to enable tracing to the kernel message log you can do
"echo 1 > /proc/fs/cifs/cifsFYI" and "echo 1 > /proc/fs/cifs/traceSMB"
Also note that "cat /proc/fs/cifs/DebugData" will display some information about the currently
active sessions and the shares that are mounted. Currently the ntlmv2 enablement and packet
signing will not work since they the implementation is not quite complete, so do not enable
these flags unless you are doing specific testing. Enabling extended security works to
Windows 2000 Workstations and XP but not to Windows 2000 server or Samba since it does not
usually send "raw NTLMSSP" (instead it sends NTLMSSP encapsulated in SPNEGO/GSSAPI, which
support is not complete in the CIFS VFS yet).
version 0.5.3 October 8th, 2002
A Partial List of Known Problems and Missing Features
=====================================================
Contributions are welcome. There are plenty of opportunities
for visible, important contributions to this module. Here
is a partial list of the known problems and missing features:
a) Support for SecurityDescriptors for chmod/chgrp/chown
b) Better pam/winbind integration
c) multi-user mounts - multiplexed sessionsetups over single vc
(ie tcp session) - prettying up needed
d) Kerberos/SPNEGO session setup support - (started)
e)NTLMv2 authentication and MD5-HMAC signing of SMB PDUs - (mostly implemented)
f) oplock support (ie safe CIFS distributed file caching) is not complete.
In addition Directory entry caching relies on a 1 second timer, rather than
using FindNotify or equivalent. - (started)
g) There may be a few additional changes that could be done to take advantage
of recent 2.5 kernel improvements in byte-range locking
h) quota support
i) support for the Linux 2.5 kernel new feature get_xattr and set_xattr
j) finish off the mount helper, mount.cifs - (started)
k) support for memory mapped files only partially works until support for
MS_INVALIDATE implemented. readpage and writepage code not finished (started)
KNOWN BUGS (updated October 8nd, 2002)
====================================
1) symbolic links (Windows reparse points) are recognized but
can not be created remotely. They are implemented for Samba and those that
support the CIFS Unix extensions but Samba has a bug currently handling
symlink text beginning with slash
2) delete of file with read-only attribute set will fail (may be ok)
3) autoreconnection logic is only partially complete.
Misc testing to do
=================
1) check out max path names and max path name components against various server
types.
2) Run POSIX bencharks against cifs
3) Run dbench
4) Finish FSX testing on SMP now that we workaround the Samba bug
This diff is collapsed.
This diff is collapsed.
/*
*
* Copyright (c) International Business Machines Corp., 2000,2002
* Modified by Steve French (sfrench@us.ibm.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define CIFS_DEBUG /* BB temporary */
#ifndef _H_CIFS_DEBUG
#define _H_CIFS_DEBUG
void dump_mem(char *label, void *data, int length);
extern int traceSMB; /* flag which enables the function below */
void dump_smb(struct smb_hdr *, int);
/*
* debug ON
* --------
*/
#ifdef CIFS_DEBUG
/* information message: e.g., configuration, major event */
extern int cifsFYI;
#define cFYI(button,prspec)\
{ if (button && cifsFYI) printk prspec; }
/* debug event message: */
#define cEVENT(button,prspec)\
{ if (button) printk prspec; }
/* error event message: e.g., i/o error */
extern int cifsERROR;
#define cERROR(button, prspec)\
{ if (button && cifsERROR) { printk prspec; if (button > 1) BUG(); } }
/*
* debug OFF
* ---------
*/
#else /* _CIFS_DEBUG */
#define cERROR(button,prspec)
#define cEVENT(button,prspec)
#define cFYI(button, prspec)
#endif /* _CIFS_DEBUG */
/*
* statistics
* ----------
*/
#ifdef _CIFS_STATISTICS
#define INCREMENT(x) ((x)++)
#define DECREMENT(x) ((x)--)
#define HIGHWATERMARK(x,y) x = MAX((x), (y))
#else
#define INCREMENT(x)
#define DECREMENT(x)
#define HIGHWATERMARK(x,y)
#endif /* _CIFS_STATISTICS */
#endif /* _H_CIFS_DEBUG */
/*
* fs/cifs/cifs_fs_sb.h
*
* Copyright (c) International Business Machines Corp., 2002
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
*/
#ifndef _CIFS_FS_SB_H
#define _CIFS_FS_SB_H
struct cifs_sb_info {
struct cifsTconInfo *tcon; /* primary mount */
/* list of implicit mounts beneath this mount point - needed in dfs case */
struct list_head nested_tcon_q;
struct nls_table *local_nls;
};
#endif /* _CIFS_FS_SB_H */
/*
* fs/cifs/cifs_unicode.c
*
* Copyright (c) International Business Machines Corp., 2000,2002
* Modified by Steve French (sfrench@us.ibm.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/fs.h>
#include "cifs_unicode.h"
#include "cifs_uniupr.h"
#include "cifspdu.h"
#include "cifs_debug.h"
/*
* NAME: toUpper()
*
* FUNCTION: Upper case ASCII string (in place) using the current codepage
*
*/
void
toUpper(const struct nls_table *n, char *mixed_string)
{
int i;
char temp;
for (i = 0; i < strlen(mixed_string); i++) {
temp = mixed_string[i];
mixed_string[i] = n->charset2upper[(int) temp];
}
}
/*
* NAME: cifs_strfromUCS()
*
* FUNCTION: Convert little-endian unicode string to character string
*
*/
int
cifs_strfromUCS_le(char *to, const wchar_t * from, /* LITTLE ENDIAN */
int len, const struct nls_table *codepage)
{
int i;
int outlen = 0;
for (i = 0; (i < len) && from[i]; i++) {
int charlen;
/* 2.4.0 kernel or greater */
charlen =
codepage->uni2char(le16_to_cpu(from[i]), &to[outlen],
NLS_MAX_CHARSET_SIZE);
if (charlen > 0) {
outlen += charlen;
} else {
to[outlen++] = '?';
}
}
to[outlen] = 0;
cEVENT(0, ("cifs_strfromUCS returning %d - '%s'\n", outlen, to));
return outlen;
}
/*
* NAME: cifs_strtoUCS()
*
* FUNCTION: Convert character string to unicode string
*
*/
int
cifs_strtoUCS(wchar_t * to, const char *from, int len,
const struct nls_table *codepage)
{
int charlen;
int i;
cEVENT(0, ("cifs_strtoUCS - '%s'\n", from));
for (i = 0; len && *from; i++, from += charlen, len -= charlen) {
/* works for 2.4.0 kernel or later */
charlen = codepage->char2uni(from, len, &to[i]);
if (charlen < 1) {
cERROR(1,
("cifs_strtoUCS: char2uni returned %d.\n",
charlen));
to[i] = cpu_to_le16(0x003f); /* a question mark */
charlen = 1;
}
to[i] = cpu_to_le16(to[i]);
}
cEVENT(0, (" returning %d\n", i));
to[i] = 0;
return i;
}
/*
* NAME: get_UCSname2()
*
* FUNCTION: Allocate and translate to unicode string
*
*/
/*int
get_UCSname2(struct component_name *uniName, struct dentry *dentry,
struct nls_table *nls_tab)
{
int length = dentry->d_name.len;
if (length > 255)
return ENAMETOOLONG;
uniName->name = kmalloc((length + 1) * sizeof (wchar_t), GFP_KERNEL);
if (uniName->name == NULL)
return ENOSPC;
uniName->namlen = cifs_strtoUCS(uniName->name, dentry->d_name.name,
length, nls_tab);
return 0;
} */
/*
* cifs_unicode: Unicode kernel case support
*
* Function:
* Convert a unicode character to upper or lower case using
* compressed tables.
*
* Copyright (c) International Business Machines Corp., 2000,2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* Notes:
* These APIs are based on the C library functions. The semantics
* should match the C functions but with expanded size operands.
*
* The upper/lower functions are based on a table created by mkupr.
* This is a compressed table of upper and lower case conversion.
*
*/
#include <asm/byteorder.h>
#include <linux/types.h>
#include <linux/nls.h>
#define UNIUPR_NOLOWER /* Example to not expand lower case tables */
/* Just define what we want from uniupr.h. We don't want to define the tables
* in each source file.
*/
#ifndef UNICASERANGE_DEFINED
struct UniCaseRange {
wchar_t start;
wchar_t end;
signed char *table;
};
#endif /* UNICASERANGE_DEFINED */
#ifndef UNIUPR_NOUPPER
extern signed char CifsUniUpperTable[512];
extern const struct UniCaseRange CifsUniUpperRange[];
#endif /* UNIUPR_NOUPPER */
#ifndef UNIUPR_NOLOWER
extern signed char UniLowerTable[512];
extern struct UniCaseRange UniLowerRange[];
#endif /* UNIUPR_NOLOWER */
/*
* directory entry argument
*/
struct component_name {
int namlen;
wchar_t *name;
};
#ifdef __KERNEL__
int cifs_strfromUCS_le(char *, const wchar_t *, int, const struct nls_table *);
int cifs_strtoUCS(wchar_t *, const char *, int, const struct nls_table *);
int cifs_UCSname(struct component_name *, struct dentry *,
const struct nls_table *);
void toUpper(const struct nls_table *, char *);
#endif
#define free_UCSname(COMP) kfree((COMP)->name)
/*
* UniStrcat: Concatenate the second string to the first
*
* Returns:
* Address of the first string
*/
static inline wchar_t *
UniStrcat(wchar_t * ucs1, const wchar_t * ucs2)
{
wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */
while (*ucs1++) ; /* To end of first string */
ucs1--; /* Return to the null */
while ((*ucs1++ = *ucs2++)) ; /* copy string 2 over */
return anchor;
}
/*
* UniStrchr: Find a character in a string
*
* Returns:
* Address of first occurance of character in string
* or NULL if the character is not in the string
*/
static inline wchar_t *
UniStrchr(const wchar_t * ucs, wchar_t uc)
{
while ((*ucs != uc) && *ucs)
ucs++;
if (*ucs == uc)
return (wchar_t *) ucs;
return NULL;
}
/*
* UniStrcmp: Compare two strings
*
* Returns:
* < 0: First string is less than second
* = 0: Strings are equal
* > 0: First string is greater than second
*/
static inline int
UniStrcmp(const wchar_t * ucs1, const wchar_t * ucs2)
{
while ((*ucs1 == *ucs2) && *ucs1) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) *ucs2;
}
/*
* UniStrcpy: Copy a string
*/
static inline wchar_t *
UniStrcpy(wchar_t * ucs1, const wchar_t * ucs2)
{
wchar_t *anchor = ucs1; /* save the start of result string */
while ((*ucs1++ = *ucs2++)) ;
return anchor;
}
/*
* UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes)
*/
static inline size_t
UniStrlen(const wchar_t * ucs1)
{
int i = 0;
while (*ucs1++)
i++;
return i;
}
/*
* UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a string (length limited)
*/
static inline size_t
UniStrnlen(const wchar_t * ucs1, int maxlen)
{
int i = 0;
while (*ucs1++) {
i++;
if (i >= maxlen)
break;
}
return i;
}
/*
* UniStrncat: Concatenate length limited string
*/
static inline wchar_t *
UniStrncat(wchar_t * ucs1, const wchar_t * ucs2, size_t n)
{
wchar_t *anchor = ucs1; /* save pointer to string 1 */
while (*ucs1++) ;
ucs1--; /* point to null terminator of s1 */
while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */
ucs1++;
ucs2++;
}
*ucs1 = 0; /* Null terminate the result */
return (anchor);
}
/*
* UniStrncmp: Compare length limited string
*/
static inline int
UniStrncmp(const wchar_t * ucs1, const wchar_t * ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == *ucs2) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) *ucs2;
}
/*
* UniStrncmp_le: Compare length limited string - native to little-endian
*/
static inline int
UniStrncmp_le(const wchar_t * ucs1, const wchar_t * ucs2, size_t n)
{
if (!n)
return 0; /* Null strings are equal */
while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
ucs1++;
ucs2++;
}
return (int) *ucs1 - (int) __le16_to_cpu(*ucs2);
}
/*
* UniStrncpy: Copy length limited string with pad
*/
static inline wchar_t *
UniStrncpy(wchar_t * ucs1, const wchar_t * ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = *ucs2++;
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrncpy_le: Copy length limited string with pad to little-endian
*/
static inline wchar_t *
UniStrncpy_le(wchar_t * ucs1, const wchar_t * ucs2, size_t n)
{
wchar_t *anchor = ucs1;
while (n-- && *ucs2) /* Copy the strings */
*ucs1++ = __le16_to_cpu(*ucs2++);
n++;
while (n--) /* Pad with nulls */
*ucs1++ = 0;
return anchor;
}
/*
* UniStrstr: Find a string in a string
*
* Returns:
* Address of first match found
* NULL if no matching string is found
*/
static inline wchar_t *
UniStrstr(const wchar_t * ucs1, const wchar_t * ucs2)
{
const wchar_t *anchor1 = ucs1;
const wchar_t *anchor2 = ucs2;
while (*ucs1) {
if (*ucs1 == *ucs2) { /* Partial match found */
ucs1++;
ucs2++;
} else {
if (!*ucs2) /* Match found */
return (wchar_t *) anchor1;
ucs1 = ++anchor1; /* No match */
ucs2 = anchor2;
}
}
if (!*ucs2) /* Both end together */
return (wchar_t *) anchor1; /* Match found */
return NULL; /* No match */
}
#ifndef UNIUPR_NOUPPER
/*
* UniToupper: Convert a unicode character to upper case
*/
static inline wchar_t
UniToupper(register wchar_t uc)
{
register const struct UniCaseRange *rp;
if (uc < sizeof (CifsUniUpperTable)) { /* Latin characters */
return uc + CifsUniUpperTable[uc]; /* Use base tables */
} else {
rp = CifsUniUpperRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
}
return uc; /* Past last range */
}
/*
* UniStrupr: Upper case a unicode string
*/
static inline wchar_t *
UniStrupr(register wchar_t * upin)
{
register wchar_t *up;
up = upin;
while (*up) { /* For all characters */
*up = UniToupper(*up);
up++;
}
return upin; /* Return input pointer */
}
#endif /* UNIUPR_NOUPPER */
#ifndef UNIUPR_NOLOWER
/*
* UniTolower: Convert a unicode character to lower case
*/
static inline wchar_t
UniTolower(wchar_t uc)
{
register struct UniCaseRange *rp;
if (uc < sizeof (UniLowerTable)) { /* Latin characters */
return uc + UniLowerTable[uc]; /* Use base tables */
} else {
rp = UniLowerRange; /* Use range tables */
while (rp->start) {
if (uc < rp->start) /* Before start of range */
return uc; /* Uppercase = input */
if (uc <= rp->end) /* In range */
return uc + rp->table[uc - rp->start];
rp++; /* Try next range */
}
}
return uc; /* Past last range */
}
/*
* UniStrlwr: Lower case a unicode string
*/
static inline wchar_t *
UniStrlwr(register wchar_t * upin)
{
register wchar_t *up;
up = upin;
while (*up) { /* For all characters */
*up = UniTolower(*up);
up++;
}
return upin; /* Return input pointer */
}
#endif
This diff is collapsed.
This diff is collapsed.
/*
* fs/cifs/cifsfs.h
*
* Copyright (c) International Business Machines Corp., 2002
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CIFSFS_H
#define _CIFSFS_H
#define ROOT_I 2
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
extern int map_cifs_error(int error_class, int error_code,
int status_codes_negotiated);
extern struct address_space_operations cifs_addr_ops;
/* Functions related to super block operations */
extern struct super_operations cifs_super_ops;
extern void cifs_put_inode(struct inode *);
extern void cifs_read_inode(struct inode *);
extern void cifs_delete_inode(struct inode *);
/* extern void cifs_write_inode(struct inode *); *//* BB not needed yet */
/* Functions related to inodes */
extern struct inode_operations cifs_dir_inode_ops;
extern int cifs_create(struct inode *, struct dentry *, int);
extern struct dentry *cifs_lookup(struct inode *, struct dentry *);
extern int cifs_unlink(struct inode *, struct dentry *);
extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *);
extern int cifs_mkdir(struct inode *, struct dentry *, int);
extern int cifs_rmdir(struct inode *, struct dentry *);
extern int cifs_rename(struct inode *, struct dentry *, struct inode *,
struct dentry *);
extern int cifs_revalidate(struct dentry *);
extern int cifs_setattr(struct dentry *, struct iattr *);
extern struct inode_operations cifs_file_inode_ops;
extern void cifs_truncate_file(struct inode *);
extern struct inode_operations cifs_symlink_inode_ops;
/* Functions related to files and directories */
extern struct file_operations cifs_file_ops;
extern int cifs_open(struct inode *inode, struct file *file);
extern int cifs_close(struct inode *inode, struct file *file);
extern int cifs_closedir(struct inode *inode, struct file *file);
extern ssize_t cifs_read(struct file *file, char *read_data,
size_t read_size, loff_t * poffset);
extern ssize_t cifs_write(struct file *file, const char *write_data,
size_t write_size, loff_t * poffset);
extern int cifs_lock(struct file *, int, struct file_lock *);
extern int cifs_fsync(struct file *, struct dentry *, int);
extern struct file_operations cifs_dir_ops;
extern int cifs_dir_open(struct inode *inode, struct file *file);
extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir);
/* Functions related to dir entries */
extern struct dentry_operations cifs_dentry_ops;
/* Functions related to symlinks */
extern int cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
extern int cifs_readlink(struct dentry *direntry, char *buffer, int buflen);
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
const char *symname);
#endif /* _CIFSSMB_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment