Merge branch 'master' of /pub/scm/linux/kernel/git/torvalds/linux-2.6
This commit is contained in:
commit
f6c4338543
@ -207,8 +207,8 @@ Attributes
|
||||
~~~~~~~~~~
|
||||
struct driver_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct device_driver *, char * buf, size_t count, loff_t off);
|
||||
ssize_t (*store)(struct device_driver *, const char * buf, size_t count, loff_t off);
|
||||
ssize_t (*show)(struct device_driver *driver, char *buf);
|
||||
ssize_t (*store)(struct device_driver *, const char * buf, size_t count);
|
||||
};
|
||||
|
||||
Device drivers can export attributes via their sysfs directories.
|
||||
|
@ -1720,8 +1720,8 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
oprofile.cpu_type= Force an oprofile cpu type
|
||||
This might be useful if you have an older oprofile
|
||||
userland or if you want common events.
|
||||
Format: { archperfmon }
|
||||
archperfmon: [X86] Force use of architectural
|
||||
Format: { arch_perfmon }
|
||||
arch_perfmon: [X86] Force use of architectural
|
||||
perfmon on Intel CPUs instead of the
|
||||
CPU specific event set.
|
||||
|
||||
|
@ -2,3 +2,5 @@
|
||||
- this file
|
||||
mtrr.txt
|
||||
- how to use x86 Memory Type Range Registers to increase performance
|
||||
exception-tables.txt
|
||||
- why and how Linux kernel uses exception tables on x86
|
||||
|
@ -1,123 +1,123 @@
|
||||
Kernel level exception handling in Linux 2.1.8
|
||||
Kernel level exception handling in Linux
|
||||
Commentary by Joerg Pommnitz <joerg@raleigh.ibm.com>
|
||||
|
||||
When a process runs in kernel mode, it often has to access user
|
||||
mode memory whose address has been passed by an untrusted program.
|
||||
When a process runs in kernel mode, it often has to access user
|
||||
mode memory whose address has been passed by an untrusted program.
|
||||
To protect itself the kernel has to verify this address.
|
||||
|
||||
In older versions of Linux this was done with the
|
||||
int verify_area(int type, const void * addr, unsigned long size)
|
||||
In older versions of Linux this was done with the
|
||||
int verify_area(int type, const void * addr, unsigned long size)
|
||||
function (which has since been replaced by access_ok()).
|
||||
|
||||
This function verified that the memory area starting at address
|
||||
This function verified that the memory area starting at address
|
||||
'addr' and of size 'size' was accessible for the operation specified
|
||||
in type (read or write). To do this, verify_read had to look up the
|
||||
virtual memory area (vma) that contained the address addr. In the
|
||||
normal case (correctly working program), this test was successful.
|
||||
in type (read or write). To do this, verify_read had to look up the
|
||||
virtual memory area (vma) that contained the address addr. In the
|
||||
normal case (correctly working program), this test was successful.
|
||||
It only failed for a few buggy programs. In some kernel profiling
|
||||
tests, this normally unneeded verification used up a considerable
|
||||
amount of time.
|
||||
|
||||
To overcome this situation, Linus decided to let the virtual memory
|
||||
To overcome this situation, Linus decided to let the virtual memory
|
||||
hardware present in every Linux-capable CPU handle this test.
|
||||
|
||||
How does this work?
|
||||
|
||||
Whenever the kernel tries to access an address that is currently not
|
||||
accessible, the CPU generates a page fault exception and calls the
|
||||
page fault handler
|
||||
Whenever the kernel tries to access an address that is currently not
|
||||
accessible, the CPU generates a page fault exception and calls the
|
||||
page fault handler
|
||||
|
||||
void do_page_fault(struct pt_regs *regs, unsigned long error_code)
|
||||
|
||||
in arch/i386/mm/fault.c. The parameters on the stack are set up by
|
||||
the low level assembly glue in arch/i386/kernel/entry.S. The parameter
|
||||
regs is a pointer to the saved registers on the stack, error_code
|
||||
in arch/x86/mm/fault.c. The parameters on the stack are set up by
|
||||
the low level assembly glue in arch/x86/kernel/entry_32.S. The parameter
|
||||
regs is a pointer to the saved registers on the stack, error_code
|
||||
contains a reason code for the exception.
|
||||
|
||||
do_page_fault first obtains the unaccessible address from the CPU
|
||||
control register CR2. If the address is within the virtual address
|
||||
space of the process, the fault probably occurred, because the page
|
||||
was not swapped in, write protected or something similar. However,
|
||||
we are interested in the other case: the address is not valid, there
|
||||
is no vma that contains this address. In this case, the kernel jumps
|
||||
to the bad_area label.
|
||||
do_page_fault first obtains the unaccessible address from the CPU
|
||||
control register CR2. If the address is within the virtual address
|
||||
space of the process, the fault probably occurred, because the page
|
||||
was not swapped in, write protected or something similar. However,
|
||||
we are interested in the other case: the address is not valid, there
|
||||
is no vma that contains this address. In this case, the kernel jumps
|
||||
to the bad_area label.
|
||||
|
||||
There it uses the address of the instruction that caused the exception
|
||||
(i.e. regs->eip) to find an address where the execution can continue
|
||||
(fixup). If this search is successful, the fault handler modifies the
|
||||
return address (again regs->eip) and returns. The execution will
|
||||
There it uses the address of the instruction that caused the exception
|
||||
(i.e. regs->eip) to find an address where the execution can continue
|
||||
(fixup). If this search is successful, the fault handler modifies the
|
||||
return address (again regs->eip) and returns. The execution will
|
||||
continue at the address in fixup.
|
||||
|
||||
Where does fixup point to?
|
||||
|
||||
Since we jump to the contents of fixup, fixup obviously points
|
||||
to executable code. This code is hidden inside the user access macros.
|
||||
I have picked the get_user macro defined in include/asm/uaccess.h as an
|
||||
example. The definition is somewhat hard to follow, so let's peek at
|
||||
Since we jump to the contents of fixup, fixup obviously points
|
||||
to executable code. This code is hidden inside the user access macros.
|
||||
I have picked the get_user macro defined in arch/x86/include/asm/uaccess.h
|
||||
as an example. The definition is somewhat hard to follow, so let's peek at
|
||||
the code generated by the preprocessor and the compiler. I selected
|
||||
the get_user call in drivers/char/console.c for a detailed examination.
|
||||
the get_user call in drivers/char/sysrq.c for a detailed examination.
|
||||
|
||||
The original code in console.c line 1405:
|
||||
The original code in sysrq.c line 587:
|
||||
get_user(c, buf);
|
||||
|
||||
The preprocessor output (edited to become somewhat readable):
|
||||
|
||||
(
|
||||
{
|
||||
long __gu_err = - 14 , __gu_val = 0;
|
||||
const __typeof__(*( ( buf ) )) *__gu_addr = ((buf));
|
||||
if (((((0 + current_set[0])->tss.segment) == 0x18 ) ||
|
||||
(((sizeof(*(buf))) <= 0xC0000000UL) &&
|
||||
((unsigned long)(__gu_addr ) <= 0xC0000000UL - (sizeof(*(buf)))))))
|
||||
{
|
||||
long __gu_err = - 14 , __gu_val = 0;
|
||||
const __typeof__(*( ( buf ) )) *__gu_addr = ((buf));
|
||||
if (((((0 + current_set[0])->tss.segment) == 0x18 ) ||
|
||||
(((sizeof(*(buf))) <= 0xC0000000UL) &&
|
||||
((unsigned long)(__gu_addr ) <= 0xC0000000UL - (sizeof(*(buf)))))))
|
||||
do {
|
||||
__gu_err = 0;
|
||||
switch ((sizeof(*(buf)))) {
|
||||
case 1:
|
||||
__asm__ __volatile__(
|
||||
"1: mov" "b" " %2,%" "b" "1\n"
|
||||
"2:\n"
|
||||
".section .fixup,\"ax\"\n"
|
||||
"3: movl %3,%0\n"
|
||||
" xor" "b" " %" "b" "1,%" "b" "1\n"
|
||||
" jmp 2b\n"
|
||||
".section __ex_table,\"a\"\n"
|
||||
" .align 4\n"
|
||||
" .long 1b,3b\n"
|
||||
".text" : "=r"(__gu_err), "=q" (__gu_val): "m"((*(struct __large_struct *)
|
||||
( __gu_addr )) ), "i"(- 14 ), "0"( __gu_err )) ;
|
||||
break;
|
||||
case 2:
|
||||
__gu_err = 0;
|
||||
switch ((sizeof(*(buf)))) {
|
||||
case 1:
|
||||
__asm__ __volatile__(
|
||||
"1: mov" "w" " %2,%" "w" "1\n"
|
||||
"2:\n"
|
||||
".section .fixup,\"ax\"\n"
|
||||
"3: movl %3,%0\n"
|
||||
" xor" "w" " %" "w" "1,%" "w" "1\n"
|
||||
" jmp 2b\n"
|
||||
".section __ex_table,\"a\"\n"
|
||||
" .align 4\n"
|
||||
" .long 1b,3b\n"
|
||||
"1: mov" "b" " %2,%" "b" "1\n"
|
||||
"2:\n"
|
||||
".section .fixup,\"ax\"\n"
|
||||
"3: movl %3,%0\n"
|
||||
" xor" "b" " %" "b" "1,%" "b" "1\n"
|
||||
" jmp 2b\n"
|
||||
".section __ex_table,\"a\"\n"
|
||||
" .align 4\n"
|
||||
" .long 1b,3b\n"
|
||||
".text" : "=r"(__gu_err), "=q" (__gu_val): "m"((*(struct __large_struct *)
|
||||
( __gu_addr )) ), "i"(- 14 ), "0"( __gu_err )) ;
|
||||
break;
|
||||
case 2:
|
||||
__asm__ __volatile__(
|
||||
"1: mov" "w" " %2,%" "w" "1\n"
|
||||
"2:\n"
|
||||
".section .fixup,\"ax\"\n"
|
||||
"3: movl %3,%0\n"
|
||||
" xor" "w" " %" "w" "1,%" "w" "1\n"
|
||||
" jmp 2b\n"
|
||||
".section __ex_table,\"a\"\n"
|
||||
" .align 4\n"
|
||||
" .long 1b,3b\n"
|
||||
".text" : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
|
||||
( __gu_addr )) ), "i"(- 14 ), "0"( __gu_err ));
|
||||
break;
|
||||
case 4:
|
||||
__asm__ __volatile__(
|
||||
"1: mov" "l" " %2,%" "" "1\n"
|
||||
"2:\n"
|
||||
".section .fixup,\"ax\"\n"
|
||||
"3: movl %3,%0\n"
|
||||
" xor" "l" " %" "" "1,%" "" "1\n"
|
||||
" jmp 2b\n"
|
||||
".section __ex_table,\"a\"\n"
|
||||
" .align 4\n" " .long 1b,3b\n"
|
||||
( __gu_addr )) ), "i"(- 14 ), "0"( __gu_err ));
|
||||
break;
|
||||
case 4:
|
||||
__asm__ __volatile__(
|
||||
"1: mov" "l" " %2,%" "" "1\n"
|
||||
"2:\n"
|
||||
".section .fixup,\"ax\"\n"
|
||||
"3: movl %3,%0\n"
|
||||
" xor" "l" " %" "" "1,%" "" "1\n"
|
||||
" jmp 2b\n"
|
||||
".section __ex_table,\"a\"\n"
|
||||
" .align 4\n" " .long 1b,3b\n"
|
||||
".text" : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
|
||||
( __gu_addr )) ), "i"(- 14 ), "0"(__gu_err));
|
||||
break;
|
||||
default:
|
||||
(__gu_val) = __get_user_bad();
|
||||
}
|
||||
} while (0) ;
|
||||
((c)) = (__typeof__(*((buf))))__gu_val;
|
||||
( __gu_addr )) ), "i"(- 14 ), "0"(__gu_err));
|
||||
break;
|
||||
default:
|
||||
(__gu_val) = __get_user_bad();
|
||||
}
|
||||
} while (0) ;
|
||||
((c)) = (__typeof__(*((buf))))__gu_val;
|
||||
__gu_err;
|
||||
}
|
||||
);
|
||||
@ -127,12 +127,12 @@ see what code gcc generates:
|
||||
|
||||
> xorl %edx,%edx
|
||||
> movl current_set,%eax
|
||||
> cmpl $24,788(%eax)
|
||||
> je .L1424
|
||||
> cmpl $24,788(%eax)
|
||||
> je .L1424
|
||||
> cmpl $-1073741825,64(%esp)
|
||||
> ja .L1423
|
||||
> ja .L1423
|
||||
> .L1424:
|
||||
> movl %edx,%eax
|
||||
> movl %edx,%eax
|
||||
> movl 64(%esp),%ebx
|
||||
> #APP
|
||||
> 1: movb (%ebx),%dl /* this is the actual user access */
|
||||
@ -149,17 +149,17 @@ see what code gcc generates:
|
||||
> .L1423:
|
||||
> movzbl %dl,%esi
|
||||
|
||||
The optimizer does a good job and gives us something we can actually
|
||||
understand. Can we? The actual user access is quite obvious. Thanks
|
||||
to the unified address space we can just access the address in user
|
||||
The optimizer does a good job and gives us something we can actually
|
||||
understand. Can we? The actual user access is quite obvious. Thanks
|
||||
to the unified address space we can just access the address in user
|
||||
memory. But what does the .section stuff do?????
|
||||
|
||||
To understand this we have to look at the final kernel:
|
||||
|
||||
> objdump --section-headers vmlinux
|
||||
>
|
||||
>
|
||||
> vmlinux: file format elf32-i386
|
||||
>
|
||||
>
|
||||
> Sections:
|
||||
> Idx Name Size VMA LMA File off Algn
|
||||
> 0 .text 00098f40 c0100000 c0100000 00001000 2**4
|
||||
@ -198,18 +198,18 @@ final kernel executable:
|
||||
|
||||
The whole user memory access is reduced to 10 x86 machine instructions.
|
||||
The instructions bracketed in the .section directives are no longer
|
||||
in the normal execution path. They are located in a different section
|
||||
in the normal execution path. They are located in a different section
|
||||
of the executable file:
|
||||
|
||||
> objdump --disassemble --section=.fixup vmlinux
|
||||
>
|
||||
>
|
||||
> c0199ff5 <.fixup+10b5> movl $0xfffffff2,%eax
|
||||
> c0199ffa <.fixup+10ba> xorb %dl,%dl
|
||||
> c0199ffc <.fixup+10bc> jmp c017e7a7 <do_con_write+e3>
|
||||
|
||||
And finally:
|
||||
> objdump --full-contents --section=__ex_table vmlinux
|
||||
>
|
||||
>
|
||||
> c01aa7c4 93c017c0 e09f19c0 97c017c0 99c017c0 ................
|
||||
> c01aa7d4 f6c217c0 e99f19c0 a5e717c0 f59f19c0 ................
|
||||
> c01aa7e4 080a18c0 01a019c0 0a0a18c0 04a019c0 ................
|
||||
@ -235,8 +235,8 @@ sections in the ELF object file. So the instructions
|
||||
ended up in the .fixup section of the object file and the addresses
|
||||
.long 1b,3b
|
||||
ended up in the __ex_table section of the object file. 1b and 3b
|
||||
are local labels. The local label 1b (1b stands for next label 1
|
||||
backward) is the address of the instruction that might fault, i.e.
|
||||
are local labels. The local label 1b (1b stands for next label 1
|
||||
backward) is the address of the instruction that might fault, i.e.
|
||||
in our case the address of the label 1 is c017e7a5:
|
||||
the original assembly code: > 1: movb (%ebx),%dl
|
||||
and linked in vmlinux : > c017e7a5 <do_con_write+e1> movb (%ebx),%dl
|
||||
@ -254,7 +254,7 @@ The assembly code
|
||||
becomes the value pair
|
||||
> c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5 ................
|
||||
^this is ^this is
|
||||
1b 3b
|
||||
1b 3b
|
||||
c017e7a5,c0199ff5 in the exception table of the kernel.
|
||||
|
||||
So, what actually happens if a fault from kernel mode with no suitable
|
||||
@ -266,9 +266,9 @@ vma occurs?
|
||||
3.) CPU calls do_page_fault
|
||||
4.) do page fault calls search_exception_table (regs->eip == c017e7a5);
|
||||
5.) search_exception_table looks up the address c017e7a5 in the
|
||||
exception table (i.e. the contents of the ELF section __ex_table)
|
||||
exception table (i.e. the contents of the ELF section __ex_table)
|
||||
and returns the address of the associated fault handle code c0199ff5.
|
||||
6.) do_page_fault modifies its own return address to point to the fault
|
||||
6.) do_page_fault modifies its own return address to point to the fault
|
||||
handle code and returns.
|
||||
7.) execution continues in the fault handling code.
|
||||
8.) 8a) EAX becomes -EFAULT (== -14)
|
11
MAINTAINERS
11
MAINTAINERS
@ -3287,11 +3287,11 @@ F: include/linux/ivtv*
|
||||
|
||||
JFS FILESYSTEM
|
||||
P: Dave Kleikamp
|
||||
M: shaggy@austin.ibm.com
|
||||
M: shaggy@linux.vnet.ibm.com
|
||||
L: jfs-discussion@lists.sourceforge.net
|
||||
W: http://jfs.sourceforge.net/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git
|
||||
S: Supported
|
||||
S: Maintained
|
||||
F: Documentation/filesystems/jfs.txt
|
||||
F: fs/jfs/
|
||||
|
||||
@ -4407,7 +4407,7 @@ W: http://www.nongnu.org/orinoco/
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/orinoco/
|
||||
|
||||
OSD LIBRARY
|
||||
OSD LIBRARY and FILESYSTEM
|
||||
P: Boaz Harrosh
|
||||
M: bharrosh@panasas.com
|
||||
P: Benny Halevy
|
||||
@ -4416,6 +4416,9 @@ L: osd-dev@open-osd.org
|
||||
W: http://open-osd.org
|
||||
T: git git://git.open-osd.org/open-osd.git
|
||||
S: Maintained
|
||||
F: drivers/scsi/osd/
|
||||
F: drivers/include/scsi/osd_*
|
||||
F: fs/exofs/
|
||||
|
||||
P54 WIRELESS DRIVER
|
||||
P: Michael Wu
|
||||
@ -5851,7 +5854,7 @@ UBI FILE SYSTEM (UBIFS)
|
||||
P: Artem Bityutskiy
|
||||
M: dedekind@infradead.org
|
||||
P: Adrian Hunter
|
||||
M: ext-adrian.hunter@nokia.com
|
||||
M: adrian.hunter@nokia.com
|
||||
L: linux-mtd@lists.infradead.org
|
||||
T: git git://git.infradead.org/ubifs-2.6.git
|
||||
W: http://www.linux-mtd.infradead.org/doc/ubifs.html
|
||||
|
4
Makefile
4
Makefile
@ -1,7 +1,7 @@
|
||||
VERSION = 2
|
||||
PATCHLEVEL = 6
|
||||
SUBLEVEL = 31
|
||||
EXTRAVERSION = -rc2
|
||||
EXTRAVERSION = -rc3
|
||||
NAME = Man-Eating Seals of Antiquity
|
||||
|
||||
# *DOCUMENTATION*
|
||||
@ -565,7 +565,7 @@ KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
|
||||
KBUILD_CFLAGS += $(call cc-option,-Wno-pointer-sign,)
|
||||
|
||||
# disable invalid "can't wrap" optimizations for signed / pointers
|
||||
KBUILD_CFLAGS += $(call cc-option,-fwrapv)
|
||||
KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow)
|
||||
|
||||
# revert to pre-gcc-4.4 behaviour of .eh_frame
|
||||
KBUILD_CFLAGS += $(call cc-option,-fno-dwarf2-cfi-asm)
|
||||
|
@ -37,6 +37,7 @@ struct thread_info {
|
||||
.task = &tsk, \
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
|
@ -73,7 +73,7 @@ struct thread_info {
|
||||
.task = &tsk, \
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.cpu_domain = domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
|
||||
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
|
||||
|
@ -40,7 +40,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall \
|
||||
} \
|
||||
|
@ -77,7 +77,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
|
@ -29,7 +29,6 @@
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sem.h>
|
||||
#include <linux/msg.h>
|
||||
|
@ -50,8 +50,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#ifndef __ASSEMBLY__
|
||||
#define INIT_THREAD_INFO(tsk) \
|
||||
@ -60,7 +58,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/sem.h>
|
||||
#include <linux/msg.h>
|
||||
#include <linux/shm.h>
|
||||
|
@ -56,8 +56,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
@ -67,7 +65,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -36,7 +36,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -48,7 +48,7 @@ struct thread_info {
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.preempt_count = 0, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/audit.h>
|
||||
|
@ -57,8 +57,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
@ -68,7 +66,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
|
@ -19,6 +19,7 @@ struct thread_info {
|
||||
{ \
|
||||
.task = &tsk, \
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -49,6 +49,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -75,8 +75,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#define INIT_THREAD_INFO(tsk) \
|
||||
{ \
|
||||
@ -84,7 +82,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/signal.h>
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/errno.h>
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/sem.h>
|
||||
#include <linux/msg.h>
|
||||
|
@ -39,8 +39,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#define INIT_THREAD_INFO(tsk) \
|
||||
{ \
|
||||
@ -48,7 +46,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = _TIF_FIXADE, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
@ -65,8 +65,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
@ -76,7 +74,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/errno.h>
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/sem.h>
|
||||
#include <linux/msg.h>
|
||||
#include <linux/shm.h>
|
||||
@ -21,7 +20,6 @@
|
||||
#include <linux/mman.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/tty.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <linux/mman.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/vt_kern.h> /* For unblank_screen() */
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
@ -23,7 +23,7 @@ struct thread_info {
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall \
|
||||
} \
|
||||
|
@ -46,15 +46,13 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#define INIT_THREAD_INFO(tsk) \
|
||||
{ \
|
||||
.task = &tsk, \
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -358,6 +358,7 @@ static struct power_pmu power7_pmu = {
|
||||
.get_constraint = power7_get_constraint,
|
||||
.get_alternatives = power7_get_alternatives,
|
||||
.disable_pmc = power7_disable_pmc,
|
||||
.flags = PPMU_ALT_SIPR,
|
||||
.n_generic = ARRAY_SIZE(power7_generic_events),
|
||||
.generic_events = power7_generic_events,
|
||||
.cache_events = &power7_cache_events,
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/regset.h>
|
||||
|
@ -95,6 +95,11 @@ config S390
|
||||
select HAVE_ARCH_TRACEHOOK
|
||||
select INIT_ALL_POSSIBLE
|
||||
select HAVE_PERF_COUNTERS
|
||||
select GENERIC_ATOMIC64 if !64BIT
|
||||
|
||||
config SCHED_OMIT_FRAME_POINTER
|
||||
bool
|
||||
default y
|
||||
|
||||
source "init/Kconfig"
|
||||
|
||||
@ -116,6 +121,9 @@ config 32BIT
|
||||
bool
|
||||
default y if !64BIT
|
||||
|
||||
config KTIME_SCALAR
|
||||
def_bool 32BIT
|
||||
|
||||
config SMP
|
||||
bool "Symmetric multi-processing support"
|
||||
---help---
|
||||
|
@ -268,7 +268,12 @@ static __inline__ int atomic64_add_unless(atomic64_t *v,
|
||||
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
|
||||
|
||||
#undef __CSG_LOOP
|
||||
#endif
|
||||
|
||||
#else /* __s390x__ */
|
||||
|
||||
#include <asm-generic/atomic64.h>
|
||||
|
||||
#endif /* __s390x__ */
|
||||
|
||||
#define smp_mb__before_atomic_dec() smp_mb()
|
||||
#define smp_mb__after_atomic_dec() smp_mb()
|
||||
|
@ -6,3 +6,5 @@
|
||||
|
||||
static inline void set_perf_counter_pending(void) {}
|
||||
static inline void clear_perf_counter_pending(void) {}
|
||||
|
||||
#define PERF_COUNTER_INDEX_OFFSET 0
|
||||
|
@ -61,7 +61,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -70,6 +70,7 @@ struct shutdown_action {
|
||||
char *name;
|
||||
void (*fn) (struct shutdown_trigger *trigger);
|
||||
int (*init) (void);
|
||||
int init_rc;
|
||||
};
|
||||
|
||||
static char *ipl_type_str(enum ipl_type type)
|
||||
@ -1486,11 +1487,13 @@ static int set_trigger(const char *buf, struct shutdown_trigger *trigger,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
|
||||
if (!shutdown_actions_list[i])
|
||||
continue;
|
||||
if (sysfs_streq(buf, shutdown_actions_list[i]->name)) {
|
||||
trigger->action = shutdown_actions_list[i];
|
||||
return len;
|
||||
if (shutdown_actions_list[i]->init_rc) {
|
||||
return shutdown_actions_list[i]->init_rc;
|
||||
} else {
|
||||
trigger->action = shutdown_actions_list[i];
|
||||
return len;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
@ -1640,8 +1643,8 @@ static void __init shutdown_actions_init(void)
|
||||
for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
|
||||
if (!shutdown_actions_list[i]->init)
|
||||
continue;
|
||||
if (shutdown_actions_list[i]->init())
|
||||
shutdown_actions_list[i] = NULL;
|
||||
shutdown_actions_list[i]->init_rc =
|
||||
shutdown_actions_list[i]->init();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
|
@ -3,6 +3,6 @@
|
||||
#
|
||||
|
||||
lib-y += delay.o string.o uaccess_std.o uaccess_pt.o
|
||||
obj-$(CONFIG_32BIT) += div64.o qrnnd.o
|
||||
obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o
|
||||
lib-$(CONFIG_64BIT) += uaccess_mvcos.o
|
||||
lib-$(CONFIG_SMP) += spinlock.o
|
||||
|
@ -36,9 +36,11 @@ static void __udelay_disabled(unsigned long usecs)
|
||||
cr0 = (cr0_saved & 0xffff00e0) | 0x00000800;
|
||||
__ctl_load(cr0 , 0, 0);
|
||||
mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT;
|
||||
lockdep_off();
|
||||
trace_hardirqs_on();
|
||||
__load_psw_mask(mask);
|
||||
local_irq_disable();
|
||||
lockdep_on();
|
||||
__ctl_load(cr0_saved, 0, 0);
|
||||
local_tick_enable(clock_saved);
|
||||
set_clock_comparator(S390_lowcore.clock_comparator);
|
||||
|
26
arch/s390/lib/ucmpdi2.c
Normal file
26
arch/s390/lib/ucmpdi2.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <linux/module.h>
|
||||
|
||||
union ull_union {
|
||||
unsigned long long ull;
|
||||
struct {
|
||||
unsigned int high;
|
||||
unsigned int low;
|
||||
} ui;
|
||||
};
|
||||
|
||||
int __ucmpdi2(unsigned long long a, unsigned long long b)
|
||||
{
|
||||
union ull_union au = {.ull = a};
|
||||
union ull_union bu = {.ull = b};
|
||||
|
||||
if (au.ui.high < bu.ui.high)
|
||||
return 0;
|
||||
else if (au.ui.high > bu.ui.high)
|
||||
return 2;
|
||||
if (au.ui.low < bu.ui.low)
|
||||
return 0;
|
||||
else if (au.ui.low > bu.ui.low)
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(__ucmpdi2);
|
@ -22,7 +22,6 @@
|
||||
#include <linux/compat.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -51,7 +51,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <linux/mman.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
|
@ -54,8 +54,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#define INIT_THREAD_INFO(tsk) \
|
||||
{ \
|
||||
@ -64,7 +62,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -125,8 +125,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
@ -135,7 +133,7 @@ struct thread_info {
|
||||
.task = &tsk, \
|
||||
.flags = ((unsigned long)ASI_P) << TI_FLAG_CURRENT_DS_SHIFT, \
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
}, \
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/regset.h>
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/audit.h>
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/string.h>
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/kdebug.h>
|
||||
|
||||
#include <asm/delay.h>
|
||||
|
@ -224,7 +224,12 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
|
||||
if (!strcmp(type, "domain-services-port"))
|
||||
bus_id_name = "ds";
|
||||
|
||||
if (strlen(bus_id_name) >= BUS_ID_SIZE - 4) {
|
||||
/*
|
||||
* 20 char is the old driver-core name size limit, which is no more.
|
||||
* This check can probably be removed after review and possible
|
||||
* adaption of the vio users name length handling.
|
||||
*/
|
||||
if (strlen(bus_id_name) >= 20 - 4) {
|
||||
printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n",
|
||||
bus_id_name);
|
||||
return NULL;
|
||||
|
@ -32,7 +32,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -19,7 +19,10 @@
|
||||
*
|
||||
* Atomically reads the value of @v.
|
||||
*/
|
||||
#define atomic_read(v) ((v)->counter)
|
||||
static inline int atomic_read(const atomic_t *v)
|
||||
{
|
||||
return v->counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* atomic_set - set atomic variable
|
||||
@ -28,7 +31,10 @@
|
||||
*
|
||||
* Atomically sets the value of @v to @i.
|
||||
*/
|
||||
#define atomic_set(v, i) (((v)->counter) = (i))
|
||||
static inline void atomic_set(atomic_t *v, int i)
|
||||
{
|
||||
v->counter = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* atomic_add - add integer to atomic variable
|
||||
@ -200,8 +206,15 @@ static inline int atomic_sub_return(int i, atomic_t *v)
|
||||
return atomic_add_return(-i, v);
|
||||
}
|
||||
|
||||
#define atomic_cmpxchg(v, old, new) (cmpxchg(&((v)->counter), (old), (new)))
|
||||
#define atomic_xchg(v, new) (xchg(&((v)->counter), (new)))
|
||||
static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
|
||||
{
|
||||
return cmpxchg(&v->counter, old, new);
|
||||
}
|
||||
|
||||
static inline int atomic_xchg(atomic_t *v, int new)
|
||||
{
|
||||
return xchg(&v->counter, new);
|
||||
}
|
||||
|
||||
/**
|
||||
* atomic_add_unless - add unless the number is already a given value
|
||||
@ -250,45 +263,12 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
|
||||
/* An 64bit atomic type */
|
||||
|
||||
typedef struct {
|
||||
unsigned long long counter;
|
||||
u64 __aligned(8) counter;
|
||||
} atomic64_t;
|
||||
|
||||
#define ATOMIC64_INIT(val) { (val) }
|
||||
|
||||
/**
|
||||
* atomic64_read - read atomic64 variable
|
||||
* @ptr: pointer of type atomic64_t
|
||||
*
|
||||
* Atomically reads the value of @v.
|
||||
* Doesn't imply a read memory barrier.
|
||||
*/
|
||||
#define __atomic64_read(ptr) ((ptr)->counter)
|
||||
|
||||
static inline unsigned long long
|
||||
cmpxchg8b(unsigned long long *ptr, unsigned long long old, unsigned long long new)
|
||||
{
|
||||
asm volatile(
|
||||
|
||||
LOCK_PREFIX "cmpxchg8b (%[ptr])\n"
|
||||
|
||||
: "=A" (old)
|
||||
|
||||
: [ptr] "D" (ptr),
|
||||
"A" (old),
|
||||
"b" (ll_low(new)),
|
||||
"c" (ll_high(new))
|
||||
|
||||
: "memory");
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline unsigned long long
|
||||
atomic64_cmpxchg(atomic64_t *ptr, unsigned long long old_val,
|
||||
unsigned long long new_val)
|
||||
{
|
||||
return cmpxchg8b(&ptr->counter, old_val, new_val);
|
||||
}
|
||||
extern u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val);
|
||||
|
||||
/**
|
||||
* atomic64_xchg - xchg atomic64 variable
|
||||
@ -298,18 +278,7 @@ atomic64_cmpxchg(atomic64_t *ptr, unsigned long long old_val,
|
||||
* Atomically xchgs the value of @ptr to @new_val and returns
|
||||
* the old value.
|
||||
*/
|
||||
|
||||
static inline unsigned long long
|
||||
atomic64_xchg(atomic64_t *ptr, unsigned long long new_val)
|
||||
{
|
||||
unsigned long long old_val;
|
||||
|
||||
do {
|
||||
old_val = atomic_read(ptr);
|
||||
} while (atomic64_cmpxchg(ptr, old_val, new_val) != old_val);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
extern u64 atomic64_xchg(atomic64_t *ptr, u64 new_val);
|
||||
|
||||
/**
|
||||
* atomic64_set - set atomic64 variable
|
||||
@ -318,10 +287,7 @@ atomic64_xchg(atomic64_t *ptr, unsigned long long new_val)
|
||||
*
|
||||
* Atomically sets the value of @ptr to @new_val.
|
||||
*/
|
||||
static inline void atomic64_set(atomic64_t *ptr, unsigned long long new_val)
|
||||
{
|
||||
atomic64_xchg(ptr, new_val);
|
||||
}
|
||||
extern void atomic64_set(atomic64_t *ptr, u64 new_val);
|
||||
|
||||
/**
|
||||
* atomic64_read - read atomic64 variable
|
||||
@ -329,17 +295,30 @@ static inline void atomic64_set(atomic64_t *ptr, unsigned long long new_val)
|
||||
*
|
||||
* Atomically reads the value of @ptr and returns it.
|
||||
*/
|
||||
static inline unsigned long long atomic64_read(atomic64_t *ptr)
|
||||
static inline u64 atomic64_read(atomic64_t *ptr)
|
||||
{
|
||||
unsigned long long curr_val;
|
||||
u64 res;
|
||||
|
||||
do {
|
||||
curr_val = __atomic64_read(ptr);
|
||||
} while (atomic64_cmpxchg(ptr, curr_val, curr_val) != curr_val);
|
||||
/*
|
||||
* Note, we inline this atomic64_t primitive because
|
||||
* it only clobbers EAX/EDX and leaves the others
|
||||
* untouched. We also (somewhat subtly) rely on the
|
||||
* fact that cmpxchg8b returns the current 64-bit value
|
||||
* of the memory location we are touching:
|
||||
*/
|
||||
asm volatile(
|
||||
"mov %%ebx, %%eax\n\t"
|
||||
"mov %%ecx, %%edx\n\t"
|
||||
LOCK_PREFIX "cmpxchg8b %1\n"
|
||||
: "=&A" (res)
|
||||
: "m" (*ptr)
|
||||
);
|
||||
|
||||
return curr_val;
|
||||
return res;
|
||||
}
|
||||
|
||||
extern u64 atomic64_read(atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_add_return - add and return
|
||||
* @delta: integer value to add
|
||||
@ -347,34 +326,14 @@ static inline unsigned long long atomic64_read(atomic64_t *ptr)
|
||||
*
|
||||
* Atomically adds @delta to @ptr and returns @delta + *@ptr
|
||||
*/
|
||||
static inline unsigned long long
|
||||
atomic64_add_return(unsigned long long delta, atomic64_t *ptr)
|
||||
{
|
||||
unsigned long long old_val, new_val;
|
||||
extern u64 atomic64_add_return(u64 delta, atomic64_t *ptr);
|
||||
|
||||
do {
|
||||
old_val = atomic_read(ptr);
|
||||
new_val = old_val + delta;
|
||||
|
||||
} while (atomic64_cmpxchg(ptr, old_val, new_val) != old_val);
|
||||
|
||||
return new_val;
|
||||
}
|
||||
|
||||
static inline long atomic64_sub_return(unsigned long long delta, atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_add_return(-delta, ptr);
|
||||
}
|
||||
|
||||
static inline long atomic64_inc_return(atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_add_return(1, ptr);
|
||||
}
|
||||
|
||||
static inline long atomic64_dec_return(atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_sub_return(1, ptr);
|
||||
}
|
||||
/*
|
||||
* Other variants with different arithmetic operators:
|
||||
*/
|
||||
extern u64 atomic64_sub_return(u64 delta, atomic64_t *ptr);
|
||||
extern u64 atomic64_inc_return(atomic64_t *ptr);
|
||||
extern u64 atomic64_dec_return(atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_add - add integer to atomic64 variable
|
||||
@ -383,10 +342,7 @@ static inline long atomic64_dec_return(atomic64_t *ptr)
|
||||
*
|
||||
* Atomically adds @delta to @ptr.
|
||||
*/
|
||||
static inline void atomic64_add(unsigned long long delta, atomic64_t *ptr)
|
||||
{
|
||||
atomic64_add_return(delta, ptr);
|
||||
}
|
||||
extern void atomic64_add(u64 delta, atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_sub - subtract the atomic64 variable
|
||||
@ -395,10 +351,7 @@ static inline void atomic64_add(unsigned long long delta, atomic64_t *ptr)
|
||||
*
|
||||
* Atomically subtracts @delta from @ptr.
|
||||
*/
|
||||
static inline void atomic64_sub(unsigned long long delta, atomic64_t *ptr)
|
||||
{
|
||||
atomic64_add(-delta, ptr);
|
||||
}
|
||||
extern void atomic64_sub(u64 delta, atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_sub_and_test - subtract value from variable and test result
|
||||
@ -409,13 +362,7 @@ static inline void atomic64_sub(unsigned long long delta, atomic64_t *ptr)
|
||||
* true if the result is zero, or false for all
|
||||
* other cases.
|
||||
*/
|
||||
static inline int
|
||||
atomic64_sub_and_test(unsigned long long delta, atomic64_t *ptr)
|
||||
{
|
||||
unsigned long long old_val = atomic64_sub_return(delta, ptr);
|
||||
|
||||
return old_val == 0;
|
||||
}
|
||||
extern int atomic64_sub_and_test(u64 delta, atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_inc - increment atomic64 variable
|
||||
@ -423,10 +370,7 @@ atomic64_sub_and_test(unsigned long long delta, atomic64_t *ptr)
|
||||
*
|
||||
* Atomically increments @ptr by 1.
|
||||
*/
|
||||
static inline void atomic64_inc(atomic64_t *ptr)
|
||||
{
|
||||
atomic64_add(1, ptr);
|
||||
}
|
||||
extern void atomic64_inc(atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_dec - decrement atomic64 variable
|
||||
@ -434,10 +378,7 @@ static inline void atomic64_inc(atomic64_t *ptr)
|
||||
*
|
||||
* Atomically decrements @ptr by 1.
|
||||
*/
|
||||
static inline void atomic64_dec(atomic64_t *ptr)
|
||||
{
|
||||
atomic64_sub(1, ptr);
|
||||
}
|
||||
extern void atomic64_dec(atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_dec_and_test - decrement and test
|
||||
@ -447,10 +388,7 @@ static inline void atomic64_dec(atomic64_t *ptr)
|
||||
* returns true if the result is 0, or false for all other
|
||||
* cases.
|
||||
*/
|
||||
static inline int atomic64_dec_and_test(atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_sub_and_test(1, ptr);
|
||||
}
|
||||
extern int atomic64_dec_and_test(atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_inc_and_test - increment and test
|
||||
@ -460,10 +398,7 @@ static inline int atomic64_dec_and_test(atomic64_t *ptr)
|
||||
* and returns true if the result is zero, or false for all
|
||||
* other cases.
|
||||
*/
|
||||
static inline int atomic64_inc_and_test(atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_sub_and_test(-1, ptr);
|
||||
}
|
||||
extern int atomic64_inc_and_test(atomic64_t *ptr);
|
||||
|
||||
/**
|
||||
* atomic64_add_negative - add and test if negative
|
||||
@ -474,13 +409,7 @@ static inline int atomic64_inc_and_test(atomic64_t *ptr)
|
||||
* if the result is negative, or false when
|
||||
* result is greater than or equal to zero.
|
||||
*/
|
||||
static inline int
|
||||
atomic64_add_negative(unsigned long long delta, atomic64_t *ptr)
|
||||
{
|
||||
long long old_val = atomic64_add_return(delta, ptr);
|
||||
|
||||
return old_val < 0;
|
||||
}
|
||||
extern int atomic64_add_negative(u64 delta, atomic64_t *ptr);
|
||||
|
||||
#include <asm-generic/atomic-long.h>
|
||||
#endif /* _ASM_X86_ATOMIC_32_H */
|
||||
|
@ -18,7 +18,10 @@
|
||||
*
|
||||
* Atomically reads the value of @v.
|
||||
*/
|
||||
#define atomic_read(v) ((v)->counter)
|
||||
static inline int atomic_read(const atomic_t *v)
|
||||
{
|
||||
return v->counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* atomic_set - set atomic variable
|
||||
@ -27,7 +30,10 @@
|
||||
*
|
||||
* Atomically sets the value of @v to @i.
|
||||
*/
|
||||
#define atomic_set(v, i) (((v)->counter) = (i))
|
||||
static inline void atomic_set(atomic_t *v, int i)
|
||||
{
|
||||
v->counter = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* atomic_add - add integer to atomic variable
|
||||
@ -192,7 +198,10 @@ static inline int atomic_sub_return(int i, atomic_t *v)
|
||||
* Atomically reads the value of @v.
|
||||
* Doesn't imply a read memory barrier.
|
||||
*/
|
||||
#define atomic64_read(v) ((v)->counter)
|
||||
static inline long atomic64_read(const atomic64_t *v)
|
||||
{
|
||||
return v->counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* atomic64_set - set atomic64 variable
|
||||
@ -201,7 +210,10 @@ static inline int atomic_sub_return(int i, atomic_t *v)
|
||||
*
|
||||
* Atomically sets the value of @v to @i.
|
||||
*/
|
||||
#define atomic64_set(v, i) (((v)->counter) = (i))
|
||||
static inline void atomic64_set(atomic64_t *v, long i)
|
||||
{
|
||||
v->counter = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* atomic64_add - add integer to atomic64 variable
|
||||
@ -355,11 +367,25 @@ static inline long atomic64_sub_return(long i, atomic64_t *v)
|
||||
#define atomic64_inc_return(v) (atomic64_add_return(1, (v)))
|
||||
#define atomic64_dec_return(v) (atomic64_sub_return(1, (v)))
|
||||
|
||||
#define atomic64_cmpxchg(v, old, new) (cmpxchg(&((v)->counter), (old), (new)))
|
||||
#define atomic64_xchg(v, new) (xchg(&((v)->counter), new))
|
||||
static inline long atomic64_cmpxchg(atomic64_t *v, long old, long new)
|
||||
{
|
||||
return cmpxchg(&v->counter, old, new);
|
||||
}
|
||||
|
||||
#define atomic_cmpxchg(v, old, new) (cmpxchg(&((v)->counter), (old), (new)))
|
||||
#define atomic_xchg(v, new) (xchg(&((v)->counter), (new)))
|
||||
static inline long atomic64_xchg(atomic64_t *v, long new)
|
||||
{
|
||||
return xchg(&v->counter, new);
|
||||
}
|
||||
|
||||
static inline long atomic_cmpxchg(atomic_t *v, int old, int new)
|
||||
{
|
||||
return cmpxchg(&v->counter, old, new);
|
||||
}
|
||||
|
||||
static inline long atomic_xchg(atomic_t *v, int new)
|
||||
{
|
||||
return xchg(&v->counter, new);
|
||||
}
|
||||
|
||||
/**
|
||||
* atomic_add_unless - add unless the number is a given value
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
extern int kstack_depth_to_print;
|
||||
|
||||
int x86_is_stack_id(int id, char *name);
|
||||
|
||||
/* Generic stack tracer with callbacks */
|
||||
|
||||
struct stacktrace_ops {
|
||||
|
@ -49,7 +49,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -652,7 +652,8 @@ static int es7000_mps_oem_check_cluster(struct mpc_table *mpc, char *oem,
|
||||
return ret && es7000_apic_is_cluster();
|
||||
}
|
||||
|
||||
struct apic apic_es7000_cluster = {
|
||||
/* We've been warned by a false positive warning.Use __refdata to keep calm. */
|
||||
struct apic __refdata apic_es7000_cluster = {
|
||||
|
||||
.name = "es7000",
|
||||
.probe = probe_es7000,
|
||||
|
@ -493,7 +493,8 @@ static void numaq_setup_portio_remap(void)
|
||||
(u_long) xquad_portio, (u_long) num_quads*XQUAD_PORTIO_QUAD);
|
||||
}
|
||||
|
||||
struct apic apic_numaq = {
|
||||
/* Use __refdata to keep false positive warning calm. */
|
||||
struct apic __refdata apic_numaq = {
|
||||
|
||||
.name = "NUMAQ",
|
||||
.probe = probe_numaq,
|
||||
|
@ -1561,6 +1561,7 @@ void callchain_store(struct perf_callchain_entry *entry, u64 ip)
|
||||
|
||||
static DEFINE_PER_CPU(struct perf_callchain_entry, irq_entry);
|
||||
static DEFINE_PER_CPU(struct perf_callchain_entry, nmi_entry);
|
||||
static DEFINE_PER_CPU(int, in_nmi_frame);
|
||||
|
||||
|
||||
static void
|
||||
@ -1576,7 +1577,9 @@ static void backtrace_warning(void *data, char *msg)
|
||||
|
||||
static int backtrace_stack(void *data, char *name)
|
||||
{
|
||||
/* Process all stacks: */
|
||||
per_cpu(in_nmi_frame, smp_processor_id()) =
|
||||
x86_is_stack_id(NMI_STACK, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1584,6 +1587,9 @@ static void backtrace_address(void *data, unsigned long addr, int reliable)
|
||||
{
|
||||
struct perf_callchain_entry *entry = data;
|
||||
|
||||
if (per_cpu(in_nmi_frame, smp_processor_id()))
|
||||
return;
|
||||
|
||||
if (reliable)
|
||||
callchain_store(entry, addr);
|
||||
}
|
||||
|
@ -19,6 +19,12 @@
|
||||
|
||||
#include "dumpstack.h"
|
||||
|
||||
/* Just a stub for now */
|
||||
int x86_is_stack_id(int id, char *name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dump_trace(struct task_struct *task, struct pt_regs *regs,
|
||||
unsigned long *stack, unsigned long bp,
|
||||
const struct stacktrace_ops *ops, void *data)
|
||||
|
@ -19,10 +19,8 @@
|
||||
|
||||
#include "dumpstack.h"
|
||||
|
||||
static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
|
||||
unsigned *usedp, char **idp)
|
||||
{
|
||||
static char ids[][8] = {
|
||||
|
||||
static char x86_stack_ids[][8] = {
|
||||
[DEBUG_STACK - 1] = "#DB",
|
||||
[NMI_STACK - 1] = "NMI",
|
||||
[DOUBLEFAULT_STACK - 1] = "#DF",
|
||||
@ -33,6 +31,15 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
|
||||
N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2] = "#DB[?]"
|
||||
#endif
|
||||
};
|
||||
|
||||
int x86_is_stack_id(int id, char *name)
|
||||
{
|
||||
return x86_stack_ids[id - 1] == name;
|
||||
}
|
||||
|
||||
static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
|
||||
unsigned *usedp, char **idp)
|
||||
{
|
||||
unsigned k;
|
||||
|
||||
/*
|
||||
@ -61,7 +68,7 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
|
||||
if (*usedp & (1U << k))
|
||||
break;
|
||||
*usedp |= 1U << k;
|
||||
*idp = ids[k];
|
||||
*idp = x86_stack_ids[k];
|
||||
return (unsigned long *)end;
|
||||
}
|
||||
/*
|
||||
@ -81,12 +88,13 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
|
||||
do {
|
||||
++j;
|
||||
end -= EXCEPTION_STKSZ;
|
||||
ids[j][4] = '1' + (j - N_EXCEPTION_STACKS);
|
||||
x86_stack_ids[j][4] = '1' +
|
||||
(j - N_EXCEPTION_STACKS);
|
||||
} while (stack < end - EXCEPTION_STKSZ);
|
||||
if (*usedp & (1U << j))
|
||||
break;
|
||||
*usedp |= 1U << j;
|
||||
*idp = ids[j];
|
||||
*idp = x86_stack_ids[j];
|
||||
return (unsigned long *)end;
|
||||
}
|
||||
#endif
|
||||
|
@ -60,7 +60,7 @@ static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift)
|
||||
"adc %5,%%edx ; "
|
||||
: "=A" (product), "=r" (tmp1), "=r" (tmp2)
|
||||
: "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) );
|
||||
#elif __x86_64__
|
||||
#elif defined(__x86_64__)
|
||||
__asm__ (
|
||||
"mul %%rdx ; shrd $32,%%rdx,%%rax"
|
||||
: "=a" (product) : "0" (delta), "d" ((u64)mul_frac) );
|
||||
|
@ -10,6 +10,7 @@ lib-y += usercopy_$(BITS).o getuser.o putuser.o
|
||||
lib-y += memcpy_$(BITS).o
|
||||
|
||||
ifeq ($(CONFIG_X86_32),y)
|
||||
obj-y += atomic64_32.o
|
||||
lib-y += checksum_32.o
|
||||
lib-y += strstr_32.o
|
||||
lib-y += semaphore_32.o string_32.o
|
||||
|
230
arch/x86/lib/atomic64_32.c
Normal file
230
arch/x86/lib/atomic64_32.c
Normal file
@ -0,0 +1,230 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cmpxchg.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
static noinline u64 cmpxchg8b(u64 *ptr, u64 old, u64 new)
|
||||
{
|
||||
u32 low = new;
|
||||
u32 high = new >> 32;
|
||||
|
||||
asm volatile(
|
||||
LOCK_PREFIX "cmpxchg8b %1\n"
|
||||
: "+A" (old), "+m" (*ptr)
|
||||
: "b" (low), "c" (high)
|
||||
);
|
||||
return old;
|
||||
}
|
||||
|
||||
u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val)
|
||||
{
|
||||
return cmpxchg8b(&ptr->counter, old_val, new_val);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_cmpxchg);
|
||||
|
||||
/**
|
||||
* atomic64_xchg - xchg atomic64 variable
|
||||
* @ptr: pointer to type atomic64_t
|
||||
* @new_val: value to assign
|
||||
*
|
||||
* Atomically xchgs the value of @ptr to @new_val and returns
|
||||
* the old value.
|
||||
*/
|
||||
u64 atomic64_xchg(atomic64_t *ptr, u64 new_val)
|
||||
{
|
||||
/*
|
||||
* Try first with a (possibly incorrect) assumption about
|
||||
* what we have there. We'll do two loops most likely,
|
||||
* but we'll get an ownership MESI transaction straight away
|
||||
* instead of a read transaction followed by a
|
||||
* flush-for-ownership transaction:
|
||||
*/
|
||||
u64 old_val, real_val = 0;
|
||||
|
||||
do {
|
||||
old_val = real_val;
|
||||
|
||||
real_val = atomic64_cmpxchg(ptr, old_val, new_val);
|
||||
|
||||
} while (real_val != old_val);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_xchg);
|
||||
|
||||
/**
|
||||
* atomic64_set - set atomic64 variable
|
||||
* @ptr: pointer to type atomic64_t
|
||||
* @new_val: value to assign
|
||||
*
|
||||
* Atomically sets the value of @ptr to @new_val.
|
||||
*/
|
||||
void atomic64_set(atomic64_t *ptr, u64 new_val)
|
||||
{
|
||||
atomic64_xchg(ptr, new_val);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_set);
|
||||
|
||||
/**
|
||||
EXPORT_SYMBOL(atomic64_read);
|
||||
* atomic64_add_return - add and return
|
||||
* @delta: integer value to add
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically adds @delta to @ptr and returns @delta + *@ptr
|
||||
*/
|
||||
noinline u64 atomic64_add_return(u64 delta, atomic64_t *ptr)
|
||||
{
|
||||
/*
|
||||
* Try first with a (possibly incorrect) assumption about
|
||||
* what we have there. We'll do two loops most likely,
|
||||
* but we'll get an ownership MESI transaction straight away
|
||||
* instead of a read transaction followed by a
|
||||
* flush-for-ownership transaction:
|
||||
*/
|
||||
u64 old_val, new_val, real_val = 0;
|
||||
|
||||
do {
|
||||
old_val = real_val;
|
||||
new_val = old_val + delta;
|
||||
|
||||
real_val = atomic64_cmpxchg(ptr, old_val, new_val);
|
||||
|
||||
} while (real_val != old_val);
|
||||
|
||||
return new_val;
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_add_return);
|
||||
|
||||
u64 atomic64_sub_return(u64 delta, atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_add_return(-delta, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_sub_return);
|
||||
|
||||
u64 atomic64_inc_return(atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_add_return(1, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_inc_return);
|
||||
|
||||
u64 atomic64_dec_return(atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_sub_return(1, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_dec_return);
|
||||
|
||||
/**
|
||||
* atomic64_add - add integer to atomic64 variable
|
||||
* @delta: integer value to add
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically adds @delta to @ptr.
|
||||
*/
|
||||
void atomic64_add(u64 delta, atomic64_t *ptr)
|
||||
{
|
||||
atomic64_add_return(delta, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_add);
|
||||
|
||||
/**
|
||||
* atomic64_sub - subtract the atomic64 variable
|
||||
* @delta: integer value to subtract
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically subtracts @delta from @ptr.
|
||||
*/
|
||||
void atomic64_sub(u64 delta, atomic64_t *ptr)
|
||||
{
|
||||
atomic64_add(-delta, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_sub);
|
||||
|
||||
/**
|
||||
* atomic64_sub_and_test - subtract value from variable and test result
|
||||
* @delta: integer value to subtract
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically subtracts @delta from @ptr and returns
|
||||
* true if the result is zero, or false for all
|
||||
* other cases.
|
||||
*/
|
||||
int atomic64_sub_and_test(u64 delta, atomic64_t *ptr)
|
||||
{
|
||||
u64 new_val = atomic64_sub_return(delta, ptr);
|
||||
|
||||
return new_val == 0;
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_sub_and_test);
|
||||
|
||||
/**
|
||||
* atomic64_inc - increment atomic64 variable
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically increments @ptr by 1.
|
||||
*/
|
||||
void atomic64_inc(atomic64_t *ptr)
|
||||
{
|
||||
atomic64_add(1, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_inc);
|
||||
|
||||
/**
|
||||
* atomic64_dec - decrement atomic64 variable
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically decrements @ptr by 1.
|
||||
*/
|
||||
void atomic64_dec(atomic64_t *ptr)
|
||||
{
|
||||
atomic64_sub(1, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_dec);
|
||||
|
||||
/**
|
||||
* atomic64_dec_and_test - decrement and test
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically decrements @ptr by 1 and
|
||||
* returns true if the result is 0, or false for all other
|
||||
* cases.
|
||||
*/
|
||||
int atomic64_dec_and_test(atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_sub_and_test(1, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_dec_and_test);
|
||||
|
||||
/**
|
||||
* atomic64_inc_and_test - increment and test
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically increments @ptr by 1
|
||||
* and returns true if the result is zero, or false for all
|
||||
* other cases.
|
||||
*/
|
||||
int atomic64_inc_and_test(atomic64_t *ptr)
|
||||
{
|
||||
return atomic64_sub_and_test(-1, ptr);
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_inc_and_test);
|
||||
|
||||
/**
|
||||
* atomic64_add_negative - add and test if negative
|
||||
* @delta: integer value to add
|
||||
* @ptr: pointer to type atomic64_t
|
||||
*
|
||||
* Atomically adds @delta to @ptr and returns true
|
||||
* if the result is negative, or false when
|
||||
* result is greater than or equal to zero.
|
||||
*/
|
||||
int atomic64_add_negative(u64 delta, atomic64_t *ptr)
|
||||
{
|
||||
s64 new_val = atomic64_add_return(delta, ptr);
|
||||
|
||||
return new_val < 0;
|
||||
}
|
||||
EXPORT_SYMBOL(atomic64_add_negative);
|
@ -751,7 +751,7 @@ survive:
|
||||
|
||||
if (retval == -ENOMEM && is_global_init(current)) {
|
||||
up_read(¤t->mm->mmap_sem);
|
||||
congestion_wait(WRITE, HZ/50);
|
||||
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
||||
goto survive;
|
||||
}
|
||||
|
||||
|
@ -697,7 +697,7 @@ show_signal_msg(struct pt_regs *regs, unsigned long error_code,
|
||||
if (!printk_ratelimit())
|
||||
return;
|
||||
|
||||
printk(KERN_CONT "%s%s[%d]: segfault at %lx ip %p sp %p error %lx",
|
||||
printk("%s%s[%d]: segfault at %lx ip %p sp %p error %lx",
|
||||
task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
|
||||
tsk->comm, task_pid_nr(tsk), address,
|
||||
(void *)regs->ip, (void *)regs->sp, error_code);
|
||||
|
@ -390,7 +390,7 @@ static int __init p4_init(char **cpu_type)
|
||||
static int force_arch_perfmon;
|
||||
static int force_cpu_type(const char *str, struct kernel_param *kp)
|
||||
{
|
||||
if (!strcmp(str, "archperfmon")) {
|
||||
if (!strcmp(str, "arch_perfmon")) {
|
||||
force_arch_perfmon = 1;
|
||||
printk(KERN_INFO "oprofile: forcing architectural perfmon\n");
|
||||
}
|
||||
|
@ -80,8 +80,6 @@ struct thread_info {
|
||||
|
||||
/*
|
||||
* macros/functions for gaining access to the thread information structure
|
||||
*
|
||||
* preempt_count needs to be 1 initially, until the scheduler is functional.
|
||||
*/
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
@ -92,7 +90,7 @@ struct thread_info {
|
||||
.exec_domain = &default_exec_domain, \
|
||||
.flags = 0, \
|
||||
.cpu = 0, \
|
||||
.preempt_count = 1, \
|
||||
.preempt_count = INIT_PREEMPT_COUNT, \
|
||||
.addr_limit = KERNEL_DS, \
|
||||
.restart_block = { \
|
||||
.fn = do_no_restart_syscall, \
|
||||
|
@ -2311,7 +2311,7 @@ cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
|
||||
goto queue_fail;
|
||||
|
||||
cfqq = cic_to_cfqq(cic, is_sync);
|
||||
if (!cfqq) {
|
||||
if (!cfqq || cfqq == &cfqd->oom_cfqq) {
|
||||
cfqq = cfq_get_queue(cfqd, is_sync, cic->ioc, gfp_mask);
|
||||
cic_set_cfqq(cic, cfqq, is_sync);
|
||||
}
|
||||
|
@ -680,3 +680,4 @@ int __init blk_scsi_ioctl_init(void)
|
||||
blk_set_cmd_filter_defaults(&blk_default_cmd_filter);
|
||||
return 0;
|
||||
}
|
||||
fs_initcall(blk_scsi_ioctl_init);
|
||||
|
@ -513,6 +513,7 @@ static const struct pci_device_id ahci_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
|
||||
{ PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
|
||||
{ PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */
|
||||
{ PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */
|
||||
{ PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */
|
||||
{ PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */
|
||||
|
@ -3392,17 +3392,27 @@ int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel)
|
||||
|
||||
static int ata_dev_set_mode(struct ata_device *dev)
|
||||
{
|
||||
struct ata_port *ap = dev->link->ap;
|
||||
struct ata_eh_context *ehc = &dev->link->eh_context;
|
||||
const bool nosetxfer = dev->horkage & ATA_HORKAGE_NOSETXFER;
|
||||
const char *dev_err_whine = "";
|
||||
int ign_dev_err = 0;
|
||||
unsigned int err_mask;
|
||||
unsigned int err_mask = 0;
|
||||
int rc;
|
||||
|
||||
dev->flags &= ~ATA_DFLAG_PIO;
|
||||
if (dev->xfer_shift == ATA_SHIFT_PIO)
|
||||
dev->flags |= ATA_DFLAG_PIO;
|
||||
|
||||
err_mask = ata_dev_set_xfermode(dev);
|
||||
if (nosetxfer && ap->flags & ATA_FLAG_SATA && ata_id_is_sata(dev->id))
|
||||
dev_err_whine = " (SET_XFERMODE skipped)";
|
||||
else {
|
||||
if (nosetxfer)
|
||||
ata_dev_printk(dev, KERN_WARNING,
|
||||
"NOSETXFER but PATA detected - can't "
|
||||
"skip SETXFER, might malfunction\n");
|
||||
err_mask = ata_dev_set_xfermode(dev);
|
||||
}
|
||||
|
||||
if (err_mask & ~AC_ERR_DEV)
|
||||
goto fail;
|
||||
@ -4297,6 +4307,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
|
||||
/* Devices which aren't very happy with higher link speeds */
|
||||
{ "WD My Book", NULL, ATA_HORKAGE_1_5_GBPS, },
|
||||
|
||||
/*
|
||||
* Devices which choke on SETXFER. Applies only if both the
|
||||
* device and controller are SATA.
|
||||
*/
|
||||
{ "PIONEER DVD-RW DVRTD08", "1.00", ATA_HORKAGE_NOSETXFER },
|
||||
|
||||
/* End Marker */
|
||||
{ }
|
||||
};
|
||||
|
@ -2517,6 +2517,10 @@ int ata_eh_reset(struct ata_link *link, int classify,
|
||||
|
||||
ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
|
||||
rc = ata_do_reset(link, reset, classes, deadline, true);
|
||||
if (rc) {
|
||||
failed_link = link;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (verbose)
|
||||
|
@ -312,11 +312,12 @@ err_ide_ioremap:
|
||||
static int __devexit pata_at91_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ata_host *host = dev_get_drvdata(&pdev->dev);
|
||||
struct at91_ide_info *info = host->private_data;
|
||||
struct at91_ide_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
if (!host)
|
||||
return 0;
|
||||
info = host->private_data;
|
||||
|
||||
ata_host_detach(host);
|
||||
|
||||
|
@ -428,6 +428,9 @@ int devres_release_all(struct device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* Looks like an uninitialized device structure */
|
||||
if (WARN_ON(dev->devres_head.next == NULL))
|
||||
return -ENODEV;
|
||||
spin_lock_irqsave(&dev->devres_lock, flags);
|
||||
return release_nodes(dev, dev->devres_head.next, &dev->devres_head,
|
||||
flags);
|
||||
|
@ -217,8 +217,10 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
|
||||
ret_count = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (offset > fw->size)
|
||||
return 0;
|
||||
if (offset > fw->size) {
|
||||
ret_count = 0;
|
||||
goto out;
|
||||
}
|
||||
if (count > fw->size - offset)
|
||||
count = fw->size - offset;
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
@ -298,6 +298,22 @@ config BLK_DEV_NBD
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config BLK_DEV_OSD
|
||||
tristate "OSD object-as-blkdev support"
|
||||
depends on SCSI_OSD_ULD
|
||||
---help---
|
||||
Saying Y or M here will allow the exporting of a single SCSI
|
||||
OSD (object-based storage) object as a Linux block device.
|
||||
|
||||
For example, if you create a 2G object on an OSD device,
|
||||
you can then use this module to present that 2G object as
|
||||
a Linux block device.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called osdblk.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config BLK_DEV_SX8
|
||||
tristate "Promise SATA SX8 support"
|
||||
depends on PCI
|
||||
|
@ -23,6 +23,7 @@ obj-$(CONFIG_XILINX_SYSACE) += xsysace.o
|
||||
obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o
|
||||
obj-$(CONFIG_MG_DISK) += mg_disk.o
|
||||
obj-$(CONFIG_SUNVDC) += sunvdc.o
|
||||
obj-$(CONFIG_BLK_DEV_OSD) += osdblk.o
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_UMEM) += umem.o
|
||||
obj-$(CONFIG_BLK_DEV_NBD) += nbd.o
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/fs.h>
|
||||
|
@ -61,7 +61,6 @@
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/loop.h>
|
||||
|
701
drivers/block/osdblk.c
Normal file
701
drivers/block/osdblk.c
Normal file
@ -0,0 +1,701 @@
|
||||
|
||||
/*
|
||||
osdblk.c -- Export a single SCSI OSD object as a Linux block device
|
||||
|
||||
|
||||
Copyright 2009 Red Hat, Inc.
|
||||
|
||||
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.
|
||||
|
||||
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; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
|
||||
Instructions for use
|
||||
--------------------
|
||||
|
||||
1) Map a Linux block device to an existing OSD object.
|
||||
|
||||
In this example, we will use partition id 1234, object id 5678,
|
||||
OSD device /dev/osd1.
|
||||
|
||||
$ echo "1234 5678 /dev/osd1" > /sys/class/osdblk/add
|
||||
|
||||
|
||||
2) List all active blkdev<->object mappings.
|
||||
|
||||
In this example, we have performed step #1 twice, creating two blkdevs,
|
||||
mapped to two separate OSD objects.
|
||||
|
||||
$ cat /sys/class/osdblk/list
|
||||
0 174 1234 5678 /dev/osd1
|
||||
1 179 1994 897123 /dev/osd0
|
||||
|
||||
The columns, in order, are:
|
||||
- blkdev unique id
|
||||
- blkdev assigned major
|
||||
- OSD object partition id
|
||||
- OSD object id
|
||||
- OSD device
|
||||
|
||||
|
||||
3) Remove an active blkdev<->object mapping.
|
||||
|
||||
In this example, we remove the mapping with blkdev unique id 1.
|
||||
|
||||
$ echo 1 > /sys/class/osdblk/remove
|
||||
|
||||
|
||||
NOTE: The actual creation and deletion of OSD objects is outside the scope
|
||||
of this driver.
|
||||
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <scsi/osd_initiator.h>
|
||||
#include <scsi/osd_attributes.h>
|
||||
#include <scsi/osd_sec.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
|
||||
#define DRV_NAME "osdblk"
|
||||
#define PFX DRV_NAME ": "
|
||||
|
||||
/* #define _OSDBLK_DEBUG */
|
||||
#ifdef _OSDBLK_DEBUG
|
||||
#define OSDBLK_DEBUG(fmt, a...) \
|
||||
printk(KERN_NOTICE "osdblk @%s:%d: " fmt, __func__, __LINE__, ##a)
|
||||
#else
|
||||
#define OSDBLK_DEBUG(fmt, a...) \
|
||||
do { if (0) printk(fmt, ##a); } while (0)
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Jeff Garzik <jeff@garzik.org>");
|
||||
MODULE_DESCRIPTION("block device inside an OSD object osdblk.ko");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct osdblk_device;
|
||||
|
||||
enum {
|
||||
OSDBLK_MINORS_PER_MAJOR = 256, /* max minors per blkdev */
|
||||
OSDBLK_MAX_REQ = 32, /* max parallel requests */
|
||||
OSDBLK_OP_TIMEOUT = 4 * 60, /* sync OSD req timeout */
|
||||
};
|
||||
|
||||
struct osdblk_request {
|
||||
struct request *rq; /* blk layer request */
|
||||
struct bio *bio; /* cloned bio */
|
||||
struct osdblk_device *osdev; /* associated blkdev */
|
||||
};
|
||||
|
||||
struct osdblk_device {
|
||||
int id; /* blkdev unique id */
|
||||
|
||||
int major; /* blkdev assigned major */
|
||||
struct gendisk *disk; /* blkdev's gendisk and rq */
|
||||
struct request_queue *q;
|
||||
|
||||
struct osd_dev *osd; /* associated OSD */
|
||||
|
||||
char name[32]; /* blkdev name, e.g. osdblk34 */
|
||||
|
||||
spinlock_t lock; /* queue lock */
|
||||
|
||||
struct osd_obj_id obj; /* OSD partition, obj id */
|
||||
uint8_t obj_cred[OSD_CAP_LEN]; /* OSD cred */
|
||||
|
||||
struct osdblk_request req[OSDBLK_MAX_REQ]; /* request table */
|
||||
|
||||
struct list_head node;
|
||||
|
||||
char osd_path[0]; /* OSD device path */
|
||||
};
|
||||
|
||||
static struct class *class_osdblk; /* /sys/class/osdblk */
|
||||
static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */
|
||||
static LIST_HEAD(osdblkdev_list);
|
||||
|
||||
static struct block_device_operations osdblk_bd_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct osd_attr g_attr_logical_length = ATTR_DEF(
|
||||
OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8);
|
||||
|
||||
static void osdblk_make_credential(u8 cred_a[OSD_CAP_LEN],
|
||||
const struct osd_obj_id *obj)
|
||||
{
|
||||
osd_sec_init_nosec_doall_caps(cred_a, obj, false, true);
|
||||
}
|
||||
|
||||
/* copied from exofs; move to libosd? */
|
||||
/*
|
||||
* Perform a synchronous OSD operation. copied from exofs; move to libosd?
|
||||
*/
|
||||
static int osd_sync_op(struct osd_request *or, int timeout, uint8_t *credential)
|
||||
{
|
||||
int ret;
|
||||
|
||||
or->timeout = timeout;
|
||||
ret = osd_finalize_request(or, 0, credential, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = osd_execute_request(or);
|
||||
|
||||
/* osd_req_decode_sense(or, ret); */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform an asynchronous OSD operation. copied from exofs; move to libosd?
|
||||
*/
|
||||
static int osd_async_op(struct osd_request *or, osd_req_done_fn *async_done,
|
||||
void *caller_context, u8 *cred)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = osd_finalize_request(or, 0, cred, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = osd_execute_request_async(or, async_done, caller_context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* copied from exofs; move to libosd? */
|
||||
static int extract_attr_from_req(struct osd_request *or, struct osd_attr *attr)
|
||||
{
|
||||
struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */
|
||||
void *iter = NULL;
|
||||
int nelem;
|
||||
|
||||
do {
|
||||
nelem = 1;
|
||||
osd_req_decode_get_attr_list(or, &cur_attr, &nelem, &iter);
|
||||
if ((cur_attr.attr_page == attr->attr_page) &&
|
||||
(cur_attr.attr_id == attr->attr_id)) {
|
||||
attr->len = cur_attr.len;
|
||||
attr->val_ptr = cur_attr.val_ptr;
|
||||
return 0;
|
||||
}
|
||||
} while (iter);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int osdblk_get_obj_size(struct osdblk_device *osdev, u64 *size_out)
|
||||
{
|
||||
struct osd_request *or;
|
||||
struct osd_attr attr;
|
||||
int ret;
|
||||
|
||||
/* start request */
|
||||
or = osd_start_request(osdev->osd, GFP_KERNEL);
|
||||
if (!or)
|
||||
return -ENOMEM;
|
||||
|
||||
/* create a get-attributes(length) request */
|
||||
osd_req_get_attributes(or, &osdev->obj);
|
||||
|
||||
osd_req_add_get_attr_list(or, &g_attr_logical_length, 1);
|
||||
|
||||
/* execute op synchronously */
|
||||
ret = osd_sync_op(or, OSDBLK_OP_TIMEOUT, osdev->obj_cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* extract length from returned attribute info */
|
||||
attr = g_attr_logical_length;
|
||||
ret = extract_attr_from_req(or, &attr);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
*size_out = get_unaligned_be64(attr.val_ptr);
|
||||
|
||||
out:
|
||||
osd_end_request(or);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void osdblk_osd_complete(struct osd_request *or, void *private)
|
||||
{
|
||||
struct osdblk_request *orq = private;
|
||||
struct osd_sense_info osi;
|
||||
int ret = osd_req_decode_sense(or, &osi);
|
||||
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
OSDBLK_DEBUG("osdblk_osd_complete with err=%d\n", ret);
|
||||
}
|
||||
|
||||
/* complete OSD request */
|
||||
osd_end_request(or);
|
||||
|
||||
/* complete request passed to osdblk by block layer */
|
||||
__blk_end_request_all(orq->rq, ret);
|
||||
}
|
||||
|
||||
static void bio_chain_put(struct bio *chain)
|
||||
{
|
||||
struct bio *tmp;
|
||||
|
||||
while (chain) {
|
||||
tmp = chain;
|
||||
chain = chain->bi_next;
|
||||
|
||||
bio_put(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bio *bio_chain_clone(struct bio *old_chain, gfp_t gfpmask)
|
||||
{
|
||||
struct bio *tmp, *new_chain = NULL, *tail = NULL;
|
||||
|
||||
while (old_chain) {
|
||||
tmp = bio_kmalloc(gfpmask, old_chain->bi_max_vecs);
|
||||
if (!tmp)
|
||||
goto err_out;
|
||||
|
||||
__bio_clone(tmp, old_chain);
|
||||
tmp->bi_bdev = NULL;
|
||||
gfpmask &= ~__GFP_WAIT;
|
||||
tmp->bi_next = NULL;
|
||||
|
||||
if (!new_chain)
|
||||
new_chain = tail = tmp;
|
||||
else {
|
||||
tail->bi_next = tmp;
|
||||
tail = tmp;
|
||||
}
|
||||
|
||||
old_chain = old_chain->bi_next;
|
||||
}
|
||||
|
||||
return new_chain;
|
||||
|
||||
err_out:
|
||||
OSDBLK_DEBUG("bio_chain_clone with err\n");
|
||||
bio_chain_put(new_chain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void osdblk_rq_fn(struct request_queue *q)
|
||||
{
|
||||
struct osdblk_device *osdev = q->queuedata;
|
||||
|
||||
while (1) {
|
||||
struct request *rq;
|
||||
struct osdblk_request *orq;
|
||||
struct osd_request *or;
|
||||
struct bio *bio;
|
||||
bool do_write, do_flush;
|
||||
|
||||
/* peek at request from block layer */
|
||||
rq = blk_fetch_request(q);
|
||||
if (!rq)
|
||||
break;
|
||||
|
||||
/* filter out block requests we don't understand */
|
||||
if (!blk_fs_request(rq) && !blk_barrier_rq(rq)) {
|
||||
blk_end_request_all(rq, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* deduce our operation (read, write, flush) */
|
||||
/* I wish the block layer simplified cmd_type/cmd_flags/cmd[]
|
||||
* into a clearly defined set of RPC commands:
|
||||
* read, write, flush, scsi command, power mgmt req,
|
||||
* driver-specific, etc.
|
||||
*/
|
||||
|
||||
do_flush = (rq->special == (void *) 0xdeadbeefUL);
|
||||
do_write = (rq_data_dir(rq) == WRITE);
|
||||
|
||||
if (!do_flush) { /* osd_flush does not use a bio */
|
||||
/* a bio clone to be passed down to OSD request */
|
||||
bio = bio_chain_clone(rq->bio, GFP_ATOMIC);
|
||||
if (!bio)
|
||||
break;
|
||||
} else
|
||||
bio = NULL;
|
||||
|
||||
/* alloc internal OSD request, for OSD command execution */
|
||||
or = osd_start_request(osdev->osd, GFP_ATOMIC);
|
||||
if (!or) {
|
||||
bio_chain_put(bio);
|
||||
OSDBLK_DEBUG("osd_start_request with err\n");
|
||||
break;
|
||||
}
|
||||
|
||||
orq = &osdev->req[rq->tag];
|
||||
orq->rq = rq;
|
||||
orq->bio = bio;
|
||||
orq->osdev = osdev;
|
||||
|
||||
/* init OSD command: flush, write or read */
|
||||
if (do_flush)
|
||||
osd_req_flush_object(or, &osdev->obj,
|
||||
OSD_CDB_FLUSH_ALL, 0, 0);
|
||||
else if (do_write)
|
||||
osd_req_write(or, &osdev->obj, blk_rq_pos(rq) * 512ULL,
|
||||
bio, blk_rq_bytes(rq));
|
||||
else
|
||||
osd_req_read(or, &osdev->obj, blk_rq_pos(rq) * 512ULL,
|
||||
bio, blk_rq_bytes(rq));
|
||||
|
||||
OSDBLK_DEBUG("%s 0x%x bytes at 0x%llx\n",
|
||||
do_flush ? "flush" : do_write ?
|
||||
"write" : "read", blk_rq_bytes(rq),
|
||||
blk_rq_pos(rq) * 512ULL);
|
||||
|
||||
/* begin OSD command execution */
|
||||
if (osd_async_op(or, osdblk_osd_complete, orq,
|
||||
osdev->obj_cred)) {
|
||||
osd_end_request(or);
|
||||
blk_requeue_request(q, rq);
|
||||
bio_chain_put(bio);
|
||||
OSDBLK_DEBUG("osd_execute_request_async with err\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* remove the special 'flush' marker, now that the command
|
||||
* is executing
|
||||
*/
|
||||
rq->special = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void osdblk_prepare_flush(struct request_queue *q, struct request *rq)
|
||||
{
|
||||
/* add driver-specific marker, to indicate that this request
|
||||
* is a flush command
|
||||
*/
|
||||
rq->special = (void *) 0xdeadbeefUL;
|
||||
}
|
||||
|
||||
static void osdblk_free_disk(struct osdblk_device *osdev)
|
||||
{
|
||||
struct gendisk *disk = osdev->disk;
|
||||
|
||||
if (!disk)
|
||||
return;
|
||||
|
||||
if (disk->flags & GENHD_FL_UP)
|
||||
del_gendisk(disk);
|
||||
if (disk->queue)
|
||||
blk_cleanup_queue(disk->queue);
|
||||
put_disk(disk);
|
||||
}
|
||||
|
||||
static int osdblk_init_disk(struct osdblk_device *osdev)
|
||||
{
|
||||
struct gendisk *disk;
|
||||
struct request_queue *q;
|
||||
int rc;
|
||||
u64 obj_size = 0;
|
||||
|
||||
/* contact OSD, request size info about the object being mapped */
|
||||
rc = osdblk_get_obj_size(osdev, &obj_size);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* create gendisk info */
|
||||
disk = alloc_disk(OSDBLK_MINORS_PER_MAJOR);
|
||||
if (!disk)
|
||||
return -ENOMEM;
|
||||
|
||||
sprintf(disk->disk_name, DRV_NAME "%d", osdev->id);
|
||||
disk->major = osdev->major;
|
||||
disk->first_minor = 0;
|
||||
disk->fops = &osdblk_bd_ops;
|
||||
disk->private_data = osdev;
|
||||
|
||||
/* init rq */
|
||||
q = blk_init_queue(osdblk_rq_fn, &osdev->lock);
|
||||
if (!q) {
|
||||
put_disk(disk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* switch queue to TCQ mode; allocate tag map */
|
||||
rc = blk_queue_init_tags(q, OSDBLK_MAX_REQ, NULL);
|
||||
if (rc) {
|
||||
blk_cleanup_queue(q);
|
||||
put_disk(disk);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set our limits to the lower device limits, because osdblk cannot
|
||||
* sleep when allocating a lower-request and therefore cannot be
|
||||
* bouncing.
|
||||
*/
|
||||
blk_queue_stack_limits(q, osd_request_queue(osdev->osd));
|
||||
|
||||
blk_queue_prep_rq(q, blk_queue_start_tag);
|
||||
blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH, osdblk_prepare_flush);
|
||||
|
||||
disk->queue = q;
|
||||
|
||||
q->queuedata = osdev;
|
||||
|
||||
osdev->disk = disk;
|
||||
osdev->q = q;
|
||||
|
||||
/* finally, announce the disk to the world */
|
||||
set_capacity(disk, obj_size / 512ULL);
|
||||
add_disk(disk);
|
||||
|
||||
printk(KERN_INFO "%s: Added of size 0x%llx\n",
|
||||
disk->disk_name, (unsigned long long)obj_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* /sys/class/osdblk/
|
||||
* add map OSD object to blkdev
|
||||
* remove unmap OSD object
|
||||
* list show mappings
|
||||
*******************************************************************/
|
||||
|
||||
static void class_osdblk_release(struct class *cls)
|
||||
{
|
||||
kfree(cls);
|
||||
}
|
||||
|
||||
static ssize_t class_osdblk_list(struct class *c, char *data)
|
||||
{
|
||||
int n = 0;
|
||||
struct list_head *tmp;
|
||||
|
||||
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
||||
|
||||
list_for_each(tmp, &osdblkdev_list) {
|
||||
struct osdblk_device *osdev;
|
||||
|
||||
osdev = list_entry(tmp, struct osdblk_device, node);
|
||||
|
||||
n += sprintf(data+n, "%d %d %llu %llu %s\n",
|
||||
osdev->id,
|
||||
osdev->major,
|
||||
osdev->obj.partition,
|
||||
osdev->obj.id,
|
||||
osdev->osd_path);
|
||||
}
|
||||
|
||||
mutex_unlock(&ctl_mutex);
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t class_osdblk_add(struct class *c, const char *buf, size_t count)
|
||||
{
|
||||
struct osdblk_device *osdev;
|
||||
ssize_t rc;
|
||||
int irc, new_id = 0;
|
||||
struct list_head *tmp;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
|
||||
/* new osdblk_device object */
|
||||
osdev = kzalloc(sizeof(*osdev) + strlen(buf) + 1, GFP_KERNEL);
|
||||
if (!osdev) {
|
||||
rc = -ENOMEM;
|
||||
goto err_out_mod;
|
||||
}
|
||||
|
||||
/* static osdblk_device initialization */
|
||||
spin_lock_init(&osdev->lock);
|
||||
INIT_LIST_HEAD(&osdev->node);
|
||||
|
||||
/* generate unique id: find highest unique id, add one */
|
||||
|
||||
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
||||
|
||||
list_for_each(tmp, &osdblkdev_list) {
|
||||
struct osdblk_device *osdev;
|
||||
|
||||
osdev = list_entry(tmp, struct osdblk_device, node);
|
||||
if (osdev->id > new_id)
|
||||
new_id = osdev->id + 1;
|
||||
}
|
||||
|
||||
osdev->id = new_id;
|
||||
|
||||
/* add to global list */
|
||||
list_add_tail(&osdev->node, &osdblkdev_list);
|
||||
|
||||
mutex_unlock(&ctl_mutex);
|
||||
|
||||
/* parse add command */
|
||||
if (sscanf(buf, "%llu %llu %s", &osdev->obj.partition, &osdev->obj.id,
|
||||
osdev->osd_path) != 3) {
|
||||
rc = -EINVAL;
|
||||
goto err_out_slot;
|
||||
}
|
||||
|
||||
/* initialize rest of new object */
|
||||
sprintf(osdev->name, DRV_NAME "%d", osdev->id);
|
||||
|
||||
/* contact requested OSD */
|
||||
osdev->osd = osduld_path_lookup(osdev->osd_path);
|
||||
if (IS_ERR(osdev->osd)) {
|
||||
rc = PTR_ERR(osdev->osd);
|
||||
goto err_out_slot;
|
||||
}
|
||||
|
||||
/* build OSD credential */
|
||||
osdblk_make_credential(osdev->obj_cred, &osdev->obj);
|
||||
|
||||
/* register our block device */
|
||||
irc = register_blkdev(0, osdev->name);
|
||||
if (irc < 0) {
|
||||
rc = irc;
|
||||
goto err_out_osd;
|
||||
}
|
||||
|
||||
osdev->major = irc;
|
||||
|
||||
/* set up and announce blkdev mapping */
|
||||
rc = osdblk_init_disk(osdev);
|
||||
if (rc)
|
||||
goto err_out_blkdev;
|
||||
|
||||
return count;
|
||||
|
||||
err_out_blkdev:
|
||||
unregister_blkdev(osdev->major, osdev->name);
|
||||
err_out_osd:
|
||||
osduld_put_device(osdev->osd);
|
||||
err_out_slot:
|
||||
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
||||
list_del_init(&osdev->node);
|
||||
mutex_unlock(&ctl_mutex);
|
||||
|
||||
kfree(osdev);
|
||||
err_out_mod:
|
||||
OSDBLK_DEBUG("Error adding device %s\n", buf);
|
||||
module_put(THIS_MODULE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t class_osdblk_remove(struct class *c, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct osdblk_device *osdev = NULL;
|
||||
int target_id, rc;
|
||||
unsigned long ul;
|
||||
struct list_head *tmp;
|
||||
|
||||
rc = strict_strtoul(buf, 10, &ul);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* convert to int; abort if we lost anything in the conversion */
|
||||
target_id = (int) ul;
|
||||
if (target_id != ul)
|
||||
return -EINVAL;
|
||||
|
||||
/* remove object from list immediately */
|
||||
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
||||
|
||||
list_for_each(tmp, &osdblkdev_list) {
|
||||
osdev = list_entry(tmp, struct osdblk_device, node);
|
||||
if (osdev->id == target_id) {
|
||||
list_del_init(&osdev->node);
|
||||
break;
|
||||
}
|
||||
osdev = NULL;
|
||||
}
|
||||
|
||||
mutex_unlock(&ctl_mutex);
|
||||
|
||||
if (!osdev)
|
||||
return -ENOENT;
|
||||
|
||||
/* clean up and free blkdev and associated OSD connection */
|
||||
osdblk_free_disk(osdev);
|
||||
unregister_blkdev(osdev->major, osdev->name);
|
||||
osduld_put_device(osdev->osd);
|
||||
kfree(osdev);
|
||||
|
||||
/* release module ref */
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct class_attribute class_osdblk_attrs[] = {
|
||||
__ATTR(add, 0200, NULL, class_osdblk_add),
|
||||
__ATTR(remove, 0200, NULL, class_osdblk_remove),
|
||||
__ATTR(list, 0444, class_osdblk_list, NULL),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static int osdblk_sysfs_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* create control files in sysfs
|
||||
* /sys/class/osdblk/...
|
||||
*/
|
||||
class_osdblk = kzalloc(sizeof(*class_osdblk), GFP_KERNEL);
|
||||
if (!class_osdblk)
|
||||
return -ENOMEM;
|
||||
|
||||
class_osdblk->name = DRV_NAME;
|
||||
class_osdblk->owner = THIS_MODULE;
|
||||
class_osdblk->class_release = class_osdblk_release;
|
||||
class_osdblk->class_attrs = class_osdblk_attrs;
|
||||
|
||||
ret = class_register(class_osdblk);
|
||||
if (ret) {
|
||||
kfree(class_osdblk);
|
||||
class_osdblk = NULL;
|
||||
printk(PFX "failed to create class osdblk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void osdblk_sysfs_cleanup(void)
|
||||
{
|
||||
if (class_osdblk)
|
||||
class_destroy(class_osdblk);
|
||||
class_osdblk = NULL;
|
||||
}
|
||||
|
||||
static int __init osdblk_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osdblk_sysfs_init();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit osdblk_exit(void)
|
||||
{
|
||||
osdblk_sysfs_cleanup();
|
||||
}
|
||||
|
||||
module_init(osdblk_init);
|
||||
module_exit(osdblk_exit);
|
||||
|
@ -1372,8 +1372,10 @@ try_next_bio:
|
||||
wakeup = (pd->write_congestion_on > 0
|
||||
&& pd->bio_queue_size <= pd->write_congestion_off);
|
||||
spin_unlock(&pd->lock);
|
||||
if (wakeup)
|
||||
clear_bdi_congested(&pd->disk->queue->backing_dev_info, WRITE);
|
||||
if (wakeup) {
|
||||
clear_bdi_congested(&pd->disk->queue->backing_dev_info,
|
||||
BLK_RW_ASYNC);
|
||||
}
|
||||
|
||||
pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
|
||||
pkt_set_state(pkt, PACKET_WAITING_STATE);
|
||||
@ -2592,10 +2594,10 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio)
|
||||
spin_lock(&pd->lock);
|
||||
if (pd->write_congestion_on > 0
|
||||
&& pd->bio_queue_size >= pd->write_congestion_on) {
|
||||
set_bdi_congested(&q->backing_dev_info, WRITE);
|
||||
set_bdi_congested(&q->backing_dev_info, BLK_RW_ASYNC);
|
||||
do {
|
||||
spin_unlock(&pd->lock);
|
||||
congestion_wait(WRITE, HZ);
|
||||
congestion_wait(BLK_RW_ASYNC, HZ);
|
||||
spin_lock(&pd->lock);
|
||||
} while(pd->bio_queue_size > pd->write_congestion_off);
|
||||
}
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -81,6 +81,7 @@ static char *serial_version = "4.30";
|
||||
#include <linux/mm.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
|
@ -633,6 +633,7 @@
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fcntl.h>
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
@ -122,6 +122,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/timer.h>
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user