diff options
370 files changed, 16414 insertions, 4334 deletions
| @@ -3741,10 +3741,11 @@ D: Mylex DAC960 PCI RAID driver | |||
| 3741 | D: Miscellaneous kernel fixes | 3741 | D: Miscellaneous kernel fixes |
| 3742 | 3742 | ||
| 3743 | N: Alessandro Zummo | 3743 | N: Alessandro Zummo |
| 3744 | E: azummo@ita.flashnet.it | 3744 | E: a.zummo@towertech.it |
| 3745 | W: http://freepage.logicom.it/azummo/ | ||
| 3746 | D: CMI8330 support is sb_card.c | 3745 | D: CMI8330 support is sb_card.c |
| 3747 | D: ISAPnP fixes in sb_card.c | 3746 | D: ISAPnP fixes in sb_card.c |
| 3747 | D: ZyXEL omni.net lcd plus driver | ||
| 3748 | D: RTC subsystem | ||
| 3748 | S: Italy | 3749 | S: Italy |
| 3749 | 3750 | ||
| 3750 | N: Marc Zyngier | 3751 | N: Marc Zyngier |
diff --git a/Documentation/pnp.txt b/Documentation/pnp.txt index af0f6eabfa1c..9529c9c9fd59 100644 --- a/Documentation/pnp.txt +++ b/Documentation/pnp.txt | |||
| @@ -115,6 +115,9 @@ pnp_unregister_protocol | |||
| 115 | pnp_register_driver | 115 | pnp_register_driver |
| 116 | - adds a PnP driver to the Plug and Play Layer | 116 | - adds a PnP driver to the Plug and Play Layer |
| 117 | - this includes driver model integration | 117 | - this includes driver model integration |
| 118 | - returns zero for success or a negative error number for failure; count | ||
| 119 | calls to the .add() method if you need to know how many devices bind to | ||
| 120 | the driver | ||
| 118 | 121 | ||
| 119 | pnp_unregister_driver | 122 | pnp_unregister_driver |
| 120 | - removes a PnP driver from the Plug and Play Layer | 123 | - removes a PnP driver from the Plug and Play Layer |
diff --git a/Documentation/robust-futex-ABI.txt b/Documentation/robust-futex-ABI.txt new file mode 100644 index 000000000000..8529a17ffaa1 --- /dev/null +++ b/Documentation/robust-futex-ABI.txt | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | Started by Paul Jackson <pj@sgi.com> | ||
| 2 | |||
| 3 | The robust futex ABI | ||
| 4 | -------------------- | ||
| 5 | |||
| 6 | Robust_futexes provide a mechanism that is used in addition to normal | ||
| 7 | futexes, for kernel assist of cleanup of held locks on task exit. | ||
| 8 | |||
| 9 | The interesting data as to what futexes a thread is holding is kept on a | ||
| 10 | linked list in user space, where it can be updated efficiently as locks | ||
| 11 | are taken and dropped, without kernel intervention. The only additional | ||
| 12 | kernel intervention required for robust_futexes above and beyond what is | ||
| 13 | required for futexes is: | ||
| 14 | |||
| 15 | 1) a one time call, per thread, to tell the kernel where its list of | ||
| 16 | held robust_futexes begins, and | ||
| 17 | 2) internal kernel code at exit, to handle any listed locks held | ||
| 18 | by the exiting thread. | ||
| 19 | |||
| 20 | The existing normal futexes already provide a "Fast Userspace Locking" | ||
| 21 | mechanism, which handles uncontested locking without needing a system | ||
| 22 | call, and handles contested locking by maintaining a list of waiting | ||
| 23 | threads in the kernel. Options on the sys_futex(2) system call support | ||
| 24 | waiting on a particular futex, and waking up the next waiter on a | ||
| 25 | particular futex. | ||
| 26 | |||
| 27 | For robust_futexes to work, the user code (typically in a library such | ||
| 28 | as glibc linked with the application) has to manage and place the | ||
| 29 | necessary list elements exactly as the kernel expects them. If it fails | ||
| 30 | to do so, then improperly listed locks will not be cleaned up on exit, | ||
| 31 | probably causing deadlock or other such failure of the other threads | ||
| 32 | waiting on the same locks. | ||
| 33 | |||
| 34 | A thread that anticipates possibly using robust_futexes should first | ||
| 35 | issue the system call: | ||
| 36 | |||
| 37 | asmlinkage long | ||
| 38 | sys_set_robust_list(struct robust_list_head __user *head, size_t len); | ||
| 39 | |||
| 40 | The pointer 'head' points to a structure in the threads address space | ||
| 41 | consisting of three words. Each word is 32 bits on 32 bit arch's, or 64 | ||
| 42 | bits on 64 bit arch's, and local byte order. Each thread should have | ||
| 43 | its own thread private 'head'. | ||
| 44 | |||
| 45 | If a thread is running in 32 bit compatibility mode on a 64 native arch | ||
| 46 | kernel, then it can actually have two such structures - one using 32 bit | ||
| 47 | words for 32 bit compatibility mode, and one using 64 bit words for 64 | ||
| 48 | bit native mode. The kernel, if it is a 64 bit kernel supporting 32 bit | ||
| 49 | compatibility mode, will attempt to process both lists on each task | ||
| 50 | exit, if the corresponding sys_set_robust_list() call has been made to | ||
| 51 | setup that list. | ||
| 52 | |||
| 53 | The first word in the memory structure at 'head' contains a | ||
| 54 | pointer to a single linked list of 'lock entries', one per lock, | ||
| 55 | as described below. If the list is empty, the pointer will point | ||
| 56 | to itself, 'head'. The last 'lock entry' points back to the 'head'. | ||
| 57 | |||
| 58 | The second word, called 'offset', specifies the offset from the | ||
| 59 | address of the associated 'lock entry', plus or minus, of what will | ||
| 60 | be called the 'lock word', from that 'lock entry'. The 'lock word' | ||
| 61 | is always a 32 bit word, unlike the other words above. The 'lock | ||
| 62 | word' holds 3 flag bits in the upper 3 bits, and the thread id (TID) | ||
| 63 | of the thread holding the lock in the bottom 29 bits. See further | ||
| 64 | below for a description of the flag bits. | ||
| 65 | |||
| 66 | The third word, called 'list_op_pending', contains transient copy of | ||
| 67 | the address of the 'lock entry', during list insertion and removal, | ||
| 68 | and is needed to correctly resolve races should a thread exit while | ||
| 69 | in the middle of a locking or unlocking operation. | ||
| 70 | |||
| 71 | Each 'lock entry' on the single linked list starting at 'head' consists | ||
| 72 | of just a single word, pointing to the next 'lock entry', or back to | ||
| 73 | 'head' if there are no more entries. In addition, nearby to each 'lock | ||
| 74 | entry', at an offset from the 'lock entry' specified by the 'offset' | ||
| 75 | word, is one 'lock word'. | ||
| 76 | |||
| 77 | The 'lock word' is always 32 bits, and is intended to be the same 32 bit | ||
| 78 | lock variable used by the futex mechanism, in conjunction with | ||
| 79 | robust_futexes. The kernel will only be able to wakeup the next thread | ||
| 80 | waiting for a lock on a threads exit if that next thread used the futex | ||
| 81 | mechanism to register the address of that 'lock word' with the kernel. | ||
| 82 | |||
| 83 | For each futex lock currently held by a thread, if it wants this | ||
| 84 | robust_futex support for exit cleanup of that lock, it should have one | ||
| 85 | 'lock entry' on this list, with its associated 'lock word' at the | ||
| 86 | specified 'offset'. Should a thread die while holding any such locks, | ||
| 87 | the kernel will walk this list, mark any such locks with a bit | ||
| 88 | indicating their holder died, and wakeup the next thread waiting for | ||
| 89 | that lock using the futex mechanism. | ||
| 90 | |||
| 91 | When a thread has invoked the above system call to indicate it | ||
| 92 | anticipates using robust_futexes, the kernel stores the passed in 'head' | ||
| 93 | pointer for that task. The task may retrieve that value later on by | ||
| 94 | using the system call: | ||
| 95 | |||
| 96 | asmlinkage long | ||
| 97 | sys_get_robust_list(int pid, struct robust_list_head __user **head_ptr, | ||
| 98 | size_t __user *len_ptr); | ||
| 99 | |||
| 100 | It is anticipated that threads will use robust_futexes embedded in | ||
| 101 | larger, user level locking structures, one per lock. The kernel | ||
| 102 | robust_futex mechanism doesn't care what else is in that structure, so | ||
| 103 | long as the 'offset' to the 'lock word' is the same for all | ||
| 104 | robust_futexes used by that thread. The thread should link those locks | ||
| 105 | it currently holds using the 'lock entry' pointers. It may also have | ||
| 106 | other links between the locks, such as the reverse side of a double | ||
| 107 | linked list, but that doesn't matter to the kernel. | ||
| 108 | |||
| 109 | By keeping its locks linked this way, on a list starting with a 'head' | ||
| 110 | pointer known to the kernel, the kernel can provide to a thread the | ||
| 111 | essential service available for robust_futexes, which is to help clean | ||
| 112 | up locks held at the time of (a perhaps unexpectedly) exit. | ||
| 113 | |||
| 114 | Actual locking and unlocking, during normal operations, is handled | ||
| 115 | entirely by user level code in the contending threads, and by the | ||
| 116 | existing futex mechanism to wait for, and wakeup, locks. The kernels | ||
| 117 | only essential involvement in robust_futexes is to remember where the | ||
| 118 | list 'head' is, and to walk the list on thread exit, handling locks | ||
| 119 | still held by the departing thread, as described below. | ||
| 120 | |||
| 121 | There may exist thousands of futex lock structures in a threads shared | ||
| 122 | memory, on various data structures, at a given point in time. Only those | ||
| 123 | lock structures for locks currently held by that thread should be on | ||
| 124 | that thread's robust_futex linked lock list a given time. | ||
| 125 | |||
| 126 | A given futex lock structure in a user shared memory region may be held | ||
| 127 | at different times by any of the threads with access to that region. The | ||
| 128 | thread currently holding such a lock, if any, is marked with the threads | ||
| 129 | TID in the lower 29 bits of the 'lock word'. | ||
| 130 | |||
| 131 | When adding or removing a lock from its list of held locks, in order for | ||
| 132 | the kernel to correctly handle lock cleanup regardless of when the task | ||
| 133 | exits (perhaps it gets an unexpected signal 9 in the middle of | ||
| 134 | manipulating this list), the user code must observe the following | ||
| 135 | protocol on 'lock entry' insertion and removal: | ||
| 136 | |||
| 137 | On insertion: | ||
| 138 | 1) set the 'list_op_pending' word to the address of the 'lock word' | ||
| 139 | to be inserted, | ||
| 140 | 2) acquire the futex lock, | ||
| 141 | 3) add the lock entry, with its thread id (TID) in the bottom 29 bits | ||
| 142 | of the 'lock word', to the linked list starting at 'head', and | ||
| 143 | 4) clear the 'list_op_pending' word. | ||
| 144 | |||
| 145 | On removal: | ||
| 146 | 1) set the 'list_op_pending' word to the address of the 'lock word' | ||
| 147 | to be removed, | ||
| 148 | 2) remove the lock entry for this lock from the 'head' list, | ||
| 149 | 2) release the futex lock, and | ||
| 150 | 2) clear the 'lock_op_pending' word. | ||
| 151 | |||
| 152 | On exit, the kernel will consider the address stored in | ||
| 153 | 'list_op_pending' and the address of each 'lock word' found by walking | ||
| 154 | the list starting at 'head'. For each such address, if the bottom 29 | ||
| 155 | bits of the 'lock word' at offset 'offset' from that address equals the | ||
| 156 | exiting threads TID, then the kernel will do two things: | ||
| 157 | |||
| 158 | 1) if bit 31 (0x80000000) is set in that word, then attempt a futex | ||
| 159 | wakeup on that address, which will waken the next thread that has | ||
| 160 | used to the futex mechanism to wait on that address, and | ||
| 161 | 2) atomically set bit 30 (0x40000000) in the 'lock word'. | ||
| 162 | |||
| 163 | In the above, bit 31 was set by futex waiters on that lock to indicate | ||
| 164 | they were waiting, and bit 30 is set by the kernel to indicate that the | ||
| 165 | lock owner died holding the lock. | ||
| 166 | |||
| 167 | The kernel exit code will silently stop scanning the list further if at | ||
| 168 | any point: | ||
| 169 | |||
| 170 | 1) the 'head' pointer or an subsequent linked list pointer | ||
| 171 | is not a valid address of a user space word | ||
| 172 | 2) the calculated location of the 'lock word' (address plus | ||
| 173 | 'offset') is not the valud address of a 32 bit user space | ||
| 174 | word | ||
| 175 | 3) if the list contains more than 1 million (subject to | ||
| 176 | future kernel configuration changes) elements. | ||
| 177 | |||
| 178 | When the kernel sees a list entry whose 'lock word' doesn't have the | ||
| 179 | current threads TID in the lower 29 bits, it does nothing with that | ||
| 180 | entry, and goes on to the next entry. | ||
| 181 | |||
| 182 | Bit 29 (0x20000000) of the 'lock word' is reserved for future use. | ||
diff --git a/Documentation/robust-futexes.txt b/Documentation/robust-futexes.txt new file mode 100644 index 000000000000..df82d75245a0 --- /dev/null +++ b/Documentation/robust-futexes.txt | |||
| @@ -0,0 +1,218 @@ | |||
| 1 | Started by: Ingo Molnar <mingo@redhat.com> | ||
| 2 | |||
| 3 | Background | ||
| 4 | ---------- | ||
| 5 | |||
| 6 | what are robust futexes? To answer that, we first need to understand | ||
| 7 | what futexes are: normal futexes are special types of locks that in the | ||
| 8 | noncontended case can be acquired/released from userspace without having | ||
| 9 | to enter the kernel. | ||
| 10 | |||
| 11 | A futex is in essence a user-space address, e.g. a 32-bit lock variable | ||
| 12 | field. If userspace notices contention (the lock is already owned and | ||
| 13 | someone else wants to grab it too) then the lock is marked with a value | ||
| 14 | that says "there's a waiter pending", and the sys_futex(FUTEX_WAIT) | ||
| 15 | syscall is used to wait for the other guy to release it. The kernel | ||
| 16 | creates a 'futex queue' internally, so that it can later on match up the | ||
| 17 | waiter with the waker - without them having to know about each other. | ||
| 18 | When the owner thread releases the futex, it notices (via the variable | ||
| 19 | value) that there were waiter(s) pending, and does the | ||
| 20 | sys_futex(FUTEX_WAKE) syscall to wake them up. Once all waiters have | ||
| 21 | taken and released the lock, the futex is again back to 'uncontended' | ||
| 22 | state, and there's no in-kernel state associated with it. The kernel | ||
| 23 | completely forgets that there ever was a futex at that address. This | ||
| 24 | method makes futexes very lightweight and scalable. | ||
| 25 | |||
| 26 | "Robustness" is about dealing with crashes while holding a lock: if a | ||
| 27 | process exits prematurely while holding a pthread_mutex_t lock that is | ||
| 28 | also shared with some other process (e.g. yum segfaults while holding a | ||
| 29 | pthread_mutex_t, or yum is kill -9-ed), then waiters for that lock need | ||
| 30 | to be notified that the last owner of the lock exited in some irregular | ||
| 31 | way. | ||
| 32 | |||
| 33 | To solve such types of problems, "robust mutex" userspace APIs were | ||
| 34 | created: pthread_mutex_lock() returns an error value if the owner exits | ||
| 35 | prematurely - and the new owner can decide whether the data protected by | ||
| 36 | the lock can be recovered safely. | ||
| 37 | |||
| 38 | There is a big conceptual problem with futex based mutexes though: it is | ||
| 39 | the kernel that destroys the owner task (e.g. due to a SEGFAULT), but | ||
| 40 | the kernel cannot help with the cleanup: if there is no 'futex queue' | ||
| 41 | (and in most cases there is none, futexes being fast lightweight locks) | ||
| 42 | then the kernel has no information to clean up after the held lock! | ||
| 43 | Userspace has no chance to clean up after the lock either - userspace is | ||
| 44 | the one that crashes, so it has no opportunity to clean up. Catch-22. | ||
| 45 | |||
| 46 | In practice, when e.g. yum is kill -9-ed (or segfaults), a system reboot | ||
| 47 | is needed to release that futex based lock. This is one of the leading | ||
| 48 | bugreports against yum. | ||
| 49 | |||
| 50 | To solve this problem, the traditional approach was to extend the vma | ||
| 51 | (virtual memory area descriptor) concept to have a notion of 'pending | ||
| 52 | robust futexes attached to this area'. This approach requires 3 new | ||
| 53 | syscall variants to sys_futex(): FUTEX_REGISTER, FUTEX_DEREGISTER and | ||
| 54 | FUTEX_RECOVER. At do_exit() time, all vmas are searched to see whether | ||
| 55 | they have a robust_head set. This approach has two fundamental problems | ||
| 56 | left: | ||
| 57 | |||
| 58 | - it has quite complex locking and race scenarios. The vma-based | ||
| 59 | approach had been pending for years, but they are still not completely | ||
| 60 | reliable. | ||
| 61 | |||
| 62 | - they have to scan _every_ vma at sys_exit() time, per thread! | ||
| 63 | |||
| 64 | The second disadvantage is a real killer: pthread_exit() takes around 1 | ||
| 65 | microsecond on Linux, but with thousands (or tens of thousands) of vmas | ||
| 66 | every pthread_exit() takes a millisecond or more, also totally | ||
| 67 | destroying the CPU's L1 and L2 caches! | ||
| 68 | |||
| 69 | This is very much noticeable even for normal process sys_exit_group() | ||
| 70 | calls: the kernel has to do the vma scanning unconditionally! (this is | ||
| 71 | because the kernel has no knowledge about how many robust futexes there | ||
| 72 | are to be cleaned up, because a robust futex might have been registered | ||
| 73 | in another task, and the futex variable might have been simply mmap()-ed | ||
| 74 | into this process's address space). | ||
| 75 | |||
| 76 | This huge overhead forced the creation of CONFIG_FUTEX_ROBUST so that | ||
| 77 | normal kernels can turn it off, but worse than that: the overhead makes | ||
| 78 | robust futexes impractical for any type of generic Linux distribution. | ||
| 79 | |||
| 80 | So something had to be done. | ||
| 81 | |||
| 82 | New approach to robust futexes | ||
| 83 | ------------------------------ | ||
| 84 | |||
| 85 | At the heart of this new approach there is a per-thread private list of | ||
| 86 | robust locks that userspace is holding (maintained by glibc) - which | ||
| 87 | userspace list is registered with the kernel via a new syscall [this | ||
| 88 | registration happens at most once per thread lifetime]. At do_exit() | ||
| 89 | time, the kernel checks this user-space list: are there any robust futex | ||
| 90 | locks to be cleaned up? | ||
| 91 | |||
| 92 | In the common case, at do_exit() time, there is no list registered, so | ||
| 93 | the cost of robust futexes is just a simple current->robust_list != NULL | ||
| 94 | comparison. If the thread has registered a list, then normally the list | ||
| 95 | is empty. If the thread/process crashed or terminated in some incorrect | ||
| 96 | way then the list might be non-empty: in this case the kernel carefully | ||
| 97 | walks the list [not trusting it], and marks all locks that are owned by | ||
| 98 | this thread with the FUTEX_OWNER_DEAD bit, and wakes up one waiter (if | ||
| 99 | any). | ||
| 100 | |||
| 101 | The list is guaranteed to be private and per-thread at do_exit() time, | ||
| 102 | so it can be accessed by the kernel in a lockless way. | ||
| 103 | |||
| 104 | There is one race possible though: since adding to and removing from the | ||
| 105 | list is done after the futex is acquired by glibc, there is a few | ||
| 106 | instructions window for the thread (or process) to die there, leaving | ||
| 107 | the futex hung. To protect against this possibility, userspace (glibc) | ||
| 108 | also maintains a simple per-thread 'list_op_pending' field, to allow the | ||
| 109 | kernel to clean up if the thread dies after acquiring the lock, but just | ||
| 110 | before it could have added itself to the list. Glibc sets this | ||
| 111 | list_op_pending field before it tries to acquire the futex, and clears | ||
| 112 | it after the list-add (or list-remove) has finished. | ||
| 113 | |||
| 114 | That's all that is needed - all the rest of robust-futex cleanup is done | ||
| 115 | in userspace [just like with the previous patches]. | ||
| 116 | |||
| 117 | Ulrich Drepper has implemented the necessary glibc support for this new | ||
| 118 | mechanism, which fully enables robust mutexes. | ||
| 119 | |||
| 120 | Key differences of this userspace-list based approach, compared to the | ||
| 121 | vma based method: | ||
| 122 | |||
| 123 | - it's much, much faster: at thread exit time, there's no need to loop | ||
| 124 | over every vma (!), which the VM-based method has to do. Only a very | ||
| 125 | simple 'is the list empty' op is done. | ||
| 126 | |||
| 127 | - no VM changes are needed - 'struct address_space' is left alone. | ||
| 128 | |||
| 129 | - no registration of individual locks is needed: robust mutexes dont | ||
| 130 | need any extra per-lock syscalls. Robust mutexes thus become a very | ||
| 131 | lightweight primitive - so they dont force the application designer | ||
| 132 | to do a hard choice between performance and robustness - robust | ||
| 133 | mutexes are just as fast. | ||
| 134 | |||
| 135 | - no per-lock kernel allocation happens. | ||
| 136 | |||
| 137 | - no resource limits are needed. | ||
| 138 | |||
| 139 | - no kernel-space recovery call (FUTEX_RECOVER) is needed. | ||
| 140 | |||
| 141 | - the implementation and the locking is "obvious", and there are no | ||
| 142 | interactions with the VM. | ||
| 143 | |||
| 144 | Performance | ||
| 145 | ----------- | ||
| 146 | |||
| 147 | I have benchmarked the time needed for the kernel to process a list of 1 | ||
| 148 | million (!) held locks, using the new method [on a 2GHz CPU]: | ||
| 149 | |||
| 150 | - with FUTEX_WAIT set [contended mutex]: 130 msecs | ||
| 151 | - without FUTEX_WAIT set [uncontended mutex]: 30 msecs | ||
| 152 | |||
| 153 | I have also measured an approach where glibc does the lock notification | ||
| 154 | [which it currently does for !pshared robust mutexes], and that took 256 | ||
| 155 | msecs - clearly slower, due to the 1 million FUTEX_WAKE syscalls | ||
| 156 | userspace had to do. | ||
| 157 | |||
| 158 | (1 million held locks are unheard of - we expect at most a handful of | ||
| 159 | locks to be held at a time. Nevertheless it's nice to know that this | ||
| 160 | approach scales nicely.) | ||
| 161 | |||
| 162 | Implementation details | ||
| 163 | ---------------------- | ||
| 164 | |||
| 165 | The patch adds two new syscalls: one to register the userspace list, and | ||
| 166 | one to query the registered list pointer: | ||
| 167 | |||
| 168 | asmlinkage long | ||
| 169 | sys_set_robust_list(struct robust_list_head __user *head, | ||
| 170 | size_t len); | ||
| 171 | |||
| 172 | asmlinkage long | ||
| 173 | sys_get_robust_list(int pid, struct robust_list_head __user **head_ptr, | ||
| 174 | size_t __user *len_ptr); | ||
| 175 | |||
| 176 | List registration is very fast: the pointer is simply stored in | ||
| 177 | current->robust_list. [Note that in the future, if robust futexes become | ||
| 178 | widespread, we could extend sys_clone() to register a robust-list head | ||
| 179 | for new threads, without the need of another syscall.] | ||
| 180 | |||
| 181 | So there is virtually zero overhead for tasks not using robust futexes, | ||
| 182 | and even for robust futex users, there is only one extra syscall per | ||
| 183 | thread lifetime, and the cleanup operation, if it happens, is fast and | ||
| 184 | straightforward. The kernel doesnt have any internal distinction between | ||
| 185 | robust and normal futexes. | ||
| 186 | |||
| 187 | If a futex is found to be held at exit time, the kernel sets the | ||
| 188 | following bit of the futex word: | ||
| 189 | |||
| 190 | #define FUTEX_OWNER_DIED 0x40000000 | ||
| 191 | |||
| 192 | and wakes up the next futex waiter (if any). User-space does the rest of | ||
| 193 | the cleanup. | ||
| 194 | |||
| 195 | Otherwise, robust futexes are acquired by glibc by putting the TID into | ||
| 196 | the futex field atomically. Waiters set the FUTEX_WAITERS bit: | ||
| 197 | |||
| 198 | #define FUTEX_WAITERS 0x80000000 | ||
| 199 | |||
| 200 | and the remaining bits are for the TID. | ||
| 201 | |||
| 202 | Testing, architecture support | ||
| 203 | ----------------------------- | ||
| 204 | |||
| 205 | i've tested the new syscalls on x86 and x86_64, and have made sure the | ||
| 206 | parsing of the userspace list is robust [ ;-) ] even if the list is | ||
| 207 | deliberately corrupted. | ||
| 208 | |||
| 209 | i386 and x86_64 syscalls are wired up at the moment, and Ulrich has | ||
| 210 | tested the new glibc code (on x86_64 and i386), and it works for his | ||
| 211 | robust-mutex testcases. | ||
| 212 | |||
| 213 | All other architectures should build just fine too - but they wont have | ||
| 214 | the new syscalls yet. | ||
| 215 | |||
| 216 | Architectures need to implement the new futex_atomic_cmpxchg_inatomic() | ||
| 217 | inline function before writing up the syscalls (that function returns | ||
| 218 | -ENOSYS right now). | ||
diff --git a/Documentation/rpc-cache.txt b/Documentation/rpc-cache.txt index 2b5d4434fa5a..5f757c8cf979 100644 --- a/Documentation/rpc-cache.txt +++ b/Documentation/rpc-cache.txt | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | This document gives a brief introduction to the caching | 1 | This document gives a brief introduction to the caching |
| 2 | mechanisms in the sunrpc layer that is used, in particular, | 2 | mechanisms in the sunrpc layer that is used, in particular, |
| 3 | for NFS authentication. | 3 | for NFS authentication. |
| 4 | 4 | ||
| @@ -25,25 +25,17 @@ The common code handles such things as: | |||
| 25 | - supporting 'NEGATIVE' as well as positive entries | 25 | - supporting 'NEGATIVE' as well as positive entries |
| 26 | - allowing an EXPIRED time on cache items, and removing | 26 | - allowing an EXPIRED time on cache items, and removing |
| 27 | items after they expire, and are no longe in-use. | 27 | items after they expire, and are no longe in-use. |
| 28 | |||
| 29 | Future code extensions are expect to handle | ||
| 30 | - making requests to user-space to fill in cache entries | 28 | - making requests to user-space to fill in cache entries |
| 31 | - allowing user-space to directly set entries in the cache | 29 | - allowing user-space to directly set entries in the cache |
| 32 | - delaying RPC requests that depend on as-yet incomplete | 30 | - delaying RPC requests that depend on as-yet incomplete |
| 33 | cache entries, and replaying those requests when the cache entry | 31 | cache entries, and replaying those requests when the cache entry |
| 34 | is complete. | 32 | is complete. |
| 35 | - maintaining last-access times on cache entries | 33 | - clean out old entries as they expire. |
| 36 | - clean out old entries when the caches become full | ||
| 37 | |||
| 38 | The code for performing a cache lookup is also common, but in the form | ||
| 39 | of a template. i.e. a #define. | ||
| 40 | Each cache defines a lookup function by using the DefineCacheLookup | ||
| 41 | macro, or the simpler DefineSimpleCacheLookup macro | ||
| 42 | 34 | ||
| 43 | Creating a Cache | 35 | Creating a Cache |
| 44 | ---------------- | 36 | ---------------- |
| 45 | 37 | ||
| 46 | 1/ A cache needs a datum to cache. This is in the form of a | 38 | 1/ A cache needs a datum to store. This is in the form of a |
| 47 | structure definition that must contain a | 39 | structure definition that must contain a |
| 48 | struct cache_head | 40 | struct cache_head |
| 49 | as an element, usually the first. | 41 | as an element, usually the first. |
| @@ -51,35 +43,69 @@ Creating a Cache | |||
| 51 | Each cache element is reference counted and contains | 43 | Each cache element is reference counted and contains |
| 52 | expiry and update times for use in cache management. | 44 | expiry and update times for use in cache management. |
| 53 | 2/ A cache needs a "cache_detail" structure that | 45 | 2/ A cache needs a "cache_detail" structure that |
| 54 | describes the cache. This stores the hash table, and some | 46 | describes the cache. This stores the hash table, some |
| 55 | parameters for cache management. | 47 | parameters for cache management, and some operations detailing how |
| 56 | 3/ A cache needs a lookup function. This is created using | 48 | to work with particular cache items. |
| 57 | the DefineCacheLookup macro. This lookup function is used both | 49 | The operations requires are: |
| 58 | to find entries and to update entries. The normal mode for | 50 | struct cache_head *alloc(void) |
| 59 | updating an entry is to replace the old entry with a new | 51 | This simply allocates appropriate memory and returns |
| 60 | entry. However it is possible to allow update-in-place | 52 | a pointer to the cache_detail embedded within the |
| 61 | for those caches where it makes sense (no atomicity issues | 53 | structure |
| 62 | or indirect reference counting issue) | 54 | void cache_put(struct kref *) |
| 63 | 4/ A cache needs to be registered using cache_register(). This | 55 | This is called when the last reference to an item is |
| 64 | includes in on a list of caches that will be regularly | 56 | is dropped. The pointer passed is to the 'ref' field |
| 65 | cleaned to discard old data. For this to work, some | 57 | in the cache_head. cache_put should release any |
| 66 | thread must periodically call cache_clean | 58 | references create by 'cache_init' and, if CACHE_VALID |
| 67 | 59 | is set, any references created by cache_update. | |
| 60 | It should then release the memory allocated by | ||
| 61 | 'alloc'. | ||
| 62 | int match(struct cache_head *orig, struct cache_head *new) | ||
| 63 | test if the keys in the two structures match. Return | ||
| 64 | 1 if they do, 0 if they don't. | ||
| 65 | void init(struct cache_head *orig, struct cache_head *new) | ||
| 66 | Set the 'key' fields in 'new' from 'orig'. This may | ||
| 67 | include taking references to shared objects. | ||
| 68 | void update(struct cache_head *orig, struct cache_head *new) | ||
| 69 | Set the 'content' fileds in 'new' from 'orig'. | ||
| 70 | int cache_show(struct seq_file *m, struct cache_detail *cd, | ||
| 71 | struct cache_head *h) | ||
| 72 | Optional. Used to provide a /proc file that lists the | ||
| 73 | contents of a cache. This should show one item, | ||
| 74 | usually on just one line. | ||
| 75 | int cache_request(struct cache_detail *cd, struct cache_head *h, | ||
| 76 | char **bpp, int *blen) | ||
| 77 | Format a request to be send to user-space for an item | ||
| 78 | to be instantiated. *bpp is a buffer of size *blen. | ||
| 79 | bpp should be moved forward over the encoded message, | ||
| 80 | and *blen should be reduced to show how much free | ||
| 81 | space remains. Return 0 on success or <0 if not | ||
| 82 | enough room or other problem. | ||
| 83 | int cache_parse(struct cache_detail *cd, char *buf, int len) | ||
| 84 | A message from user space has arrived to fill out a | ||
| 85 | cache entry. It is in 'buf' of length 'len'. | ||
| 86 | cache_parse should parse this, find the item in the | ||
| 87 | cache with sunrpc_cache_lookup, and update the item | ||
| 88 | with sunrpc_cache_update. | ||
| 89 | |||
| 90 | |||
| 91 | 3/ A cache needs to be registered using cache_register(). This | ||
| 92 | includes it on a list of caches that will be regularly | ||
| 93 | cleaned to discard old data. | ||
| 94 | |||
| 68 | Using a cache | 95 | Using a cache |
| 69 | ------------- | 96 | ------------- |
| 70 | 97 | ||
| 71 | To find a value in a cache, call the lookup function passing it a the | 98 | To find a value in a cache, call sunrpc_cache_lookup passing a pointer |
| 72 | datum which contains key, and possibly content, and a flag saying | 99 | to the cache_head in a sample item with the 'key' fields filled in. |
| 73 | whether to update the cache with new data from the datum. Depending | 100 | This will be passed to ->match to identify the target entry. If no |
| 74 | on how the cache lookup function was defined, it may take an extra | 101 | entry is found, a new entry will be create, added to the cache, and |
| 75 | argument to identify the particular cache in question. | 102 | marked as not containing valid data. |
| 76 | 103 | ||
| 77 | Except in cases of kmalloc failure, the lookup function | 104 | The item returned is typically passed to cache_check which will check |
| 78 | will return a new datum which will store the key and | 105 | if the data is valid, and may initiate an up-call to get fresh data. |
| 79 | may contain valid content, or may not. | 106 | cache_check will return -ENOENT in the entry is negative or if an up |
| 80 | This datum is typically passed to cache_check which determines the | 107 | call is needed but not possible, -EAGAIN if an upcall is pending, |
| 81 | validity of the datum and may later initiate an upcall to fill | 108 | or 0 if the data is valid; |
| 82 | in the data. | ||
| 83 | 109 | ||
| 84 | cache_check can be passed a "struct cache_req *". This structure is | 110 | cache_check can be passed a "struct cache_req *". This structure is |
| 85 | typically embedded in the actual request and can be used to create a | 111 | typically embedded in the actual request and can be used to create a |
| @@ -90,6 +116,13 @@ item does become valid, the deferred copy of the request will be | |||
| 90 | revisited (->revisit). It is expected that this method will | 116 | revisited (->revisit). It is expected that this method will |
| 91 | reschedule the request for processing. | 117 | reschedule the request for processing. |
| 92 | 118 | ||
| 119 | The value returned by sunrpc_cache_lookup can also be passed to | ||
| 120 | sunrpc_cache_update to set the content for the item. A second item is | ||
| 121 | passed which should hold the content. If the item found by _lookup | ||
| 122 | has valid data, then it is discarded and a new item is created. This | ||
| 123 | saves any user of an item from worrying about content changing while | ||
| 124 | it is being inspected. If the item found by _lookup does not contain | ||
| 125 | valid data, then the content is copied across and CACHE_VALID is set. | ||
| 93 | 126 | ||
| 94 | Populating a cache | 127 | Populating a cache |
| 95 | ------------------ | 128 | ------------------ |
| @@ -114,8 +147,8 @@ should be create or updated to have the given content, and the | |||
| 114 | expiry time should be set on that item. | 147 | expiry time should be set on that item. |
| 115 | 148 | ||
| 116 | Reading from a channel is a bit more interesting. When a cache | 149 | Reading from a channel is a bit more interesting. When a cache |
| 117 | lookup fail, or when it suceeds but finds an entry that may soon | 150 | lookup fails, or when it succeeds but finds an entry that may soon |
| 118 | expiry, a request is lodged for that cache item to be updated by | 151 | expire, a request is lodged for that cache item to be updated by |
| 119 | user-space. These requests appear in the channel file. | 152 | user-space. These requests appear in the channel file. |
| 120 | 153 | ||
| 121 | Successive reads will return successive requests. | 154 | Successive reads will return successive requests. |
| @@ -130,7 +163,7 @@ Thus a user-space helper is likely to: | |||
| 130 | write a response | 163 | write a response |
| 131 | loop. | 164 | loop. |
| 132 | 165 | ||
| 133 | If it dies and needs to be restarted, any requests that have not be | 166 | If it dies and needs to be restarted, any requests that have not been |
| 134 | answered will still appear in the file and will be read by the new | 167 | answered will still appear in the file and will be read by the new |
| 135 | instance of the helper. | 168 | instance of the helper. |
| 136 | 169 | ||
| @@ -142,10 +175,9 @@ Each cache should also define a "cache_request" method which | |||
| 142 | takes a cache item and encodes a request into the buffer | 175 | takes a cache item and encodes a request into the buffer |
| 143 | provided. | 176 | provided. |
| 144 | 177 | ||
| 145 | |||
| 146 | Note: If a cache has no active readers on the channel, and has had not | 178 | Note: If a cache has no active readers on the channel, and has had not |
| 147 | active readers for more than 60 seconds, further requests will not be | 179 | active readers for more than 60 seconds, further requests will not be |
| 148 | added to the channel but instead all looks that do not find a valid | 180 | added to the channel but instead all lookups that do not find a valid |
| 149 | entry will fail. This is partly for backward compatibility: The | 181 | entry will fail. This is partly for backward compatibility: The |
| 150 | previous nfs exports table was deemed to be authoritative and a | 182 | previous nfs exports table was deemed to be authoritative and a |
| 151 | failed lookup meant a definite 'no'. | 183 | failed lookup meant a definite 'no'. |
| @@ -154,18 +186,17 @@ request/response format | |||
| 154 | ----------------------- | 186 | ----------------------- |
| 155 | 187 | ||
| 156 | While each cache is free to use it's own format for requests | 188 | While each cache is free to use it's own format for requests |
| 157 | and responses over channel, the following is recommended are | 189 | and responses over channel, the following is recommended as |
| 158 | appropriate and support routines are available to help: | 190 | appropriate and support routines are available to help: |
| 159 | Each request or response record should be printable ASCII | 191 | Each request or response record should be printable ASCII |
| 160 | with precisely one newline character which should be at the end. | 192 | with precisely one newline character which should be at the end. |
| 161 | Fields within the record should be separated by spaces, normally one. | 193 | Fields within the record should be separated by spaces, normally one. |
| 162 | If spaces, newlines, or nul characters are needed in a field they | 194 | If spaces, newlines, or nul characters are needed in a field they |
| 163 | much be quotes. two mechanisms are available: | 195 | much be quoted. two mechanisms are available: |
| 164 | 1/ If a field begins '\x' then it must contain an even number of | 196 | 1/ If a field begins '\x' then it must contain an even number of |
| 165 | hex digits, and pairs of these digits provide the bytes in the | 197 | hex digits, and pairs of these digits provide the bytes in the |
| 166 | field. | 198 | field. |
| 167 | 2/ otherwise a \ in the field must be followed by 3 octal digits | 199 | 2/ otherwise a \ in the field must be followed by 3 octal digits |
| 168 | which give the code for a byte. Other characters are treated | 200 | which give the code for a byte. Other characters are treated |
| 169 | as them selves. At the very least, space, newlines nul, and | 201 | as them selves. At the very least, space, newline, nul, and |
| 170 | '\' must be quoted in this way. | 202 | '\' must be quoted in this way. |
| 171 | |||
diff --git a/MAINTAINERS b/MAINTAINERS index f27846734b06..e5b051f0e27e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -2233,6 +2233,12 @@ M: p_gortmaker@yahoo.com | |||
| 2233 | L: linux-kernel@vger.kernel.org | 2233 | L: linux-kernel@vger.kernel.org |
| 2234 | S: Maintained | 2234 | S: Maintained |
| 2235 | 2235 | ||
| 2236 | REAL TIME CLOCK (RTC) SUBSYSTEM | ||
| 2237 | P: Alessandro Zummo | ||
| 2238 | M: a.zummo@towertech.it | ||
| 2239 | L: linux-kernel@vger.kernel.org | ||
| 2240 | S: Maintained | ||
| 2241 | |||
| 2236 | REISERFS FILE SYSTEM | 2242 | REISERFS FILE SYSTEM |
| 2237 | P: Hans Reiser | 2243 | P: Hans Reiser |
| 2238 | M: reiserfs-dev@namesys.com | 2244 | M: reiserfs-dev@namesys.com |
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index b4e5f8ff2b25..dd8769670596 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <linux/root_dev.h> | 34 | #include <linux/root_dev.h> |
| 35 | #include <linux/initrd.h> | 35 | #include <linux/initrd.h> |
| 36 | #include <linux/eisa.h> | 36 | #include <linux/eisa.h> |
| 37 | #include <linux/pfn.h> | ||
| 37 | #ifdef CONFIG_MAGIC_SYSRQ | 38 | #ifdef CONFIG_MAGIC_SYSRQ |
| 38 | #include <linux/sysrq.h> | 39 | #include <linux/sysrq.h> |
| 39 | #include <linux/reboot.h> | 40 | #include <linux/reboot.h> |
| @@ -42,7 +43,7 @@ | |||
| 42 | #include <asm/setup.h> | 43 | #include <asm/setup.h> |
| 43 | #include <asm/io.h> | 44 | #include <asm/io.h> |
| 44 | 45 | ||
| 45 | extern struct notifier_block *panic_notifier_list; | 46 | extern struct atomic_notifier_head panic_notifier_list; |
| 46 | static int alpha_panic_event(struct notifier_block *, unsigned long, void *); | 47 | static int alpha_panic_event(struct notifier_block *, unsigned long, void *); |
| 47 | static struct notifier_block alpha_panic_block = { | 48 | static struct notifier_block alpha_panic_block = { |
| 48 | alpha_panic_event, | 49 | alpha_panic_event, |
| @@ -241,9 +242,6 @@ reserve_std_resources(void) | |||
| 241 | request_resource(io, standard_io_resources+i); | 242 | request_resource(io, standard_io_resources+i); |
| 242 | } | 243 | } |
| 243 | 244 | ||
| 244 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 245 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 246 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 247 | #define PFN_MAX PFN_DOWN(0x80000000) | 245 | #define PFN_MAX PFN_DOWN(0x80000000) |
| 248 | #define for_each_mem_cluster(memdesc, cluster, i) \ | 246 | #define for_each_mem_cluster(memdesc, cluster, i) \ |
| 249 | for ((cluster) = (memdesc)->cluster, (i) = 0; \ | 247 | for ((cluster) = (memdesc)->cluster, (i) = 0; \ |
| @@ -472,11 +470,6 @@ page_is_ram(unsigned long pfn) | |||
| 472 | return 0; | 470 | return 0; |
| 473 | } | 471 | } |
| 474 | 472 | ||
| 475 | #undef PFN_UP | ||
| 476 | #undef PFN_DOWN | ||
| 477 | #undef PFN_PHYS | ||
| 478 | #undef PFN_MAX | ||
| 479 | |||
| 480 | void __init | 473 | void __init |
| 481 | setup_arch(char **cmdline_p) | 474 | setup_arch(char **cmdline_p) |
| 482 | { | 475 | { |
| @@ -507,7 +500,8 @@ setup_arch(char **cmdline_p) | |||
| 507 | } | 500 | } |
| 508 | 501 | ||
| 509 | /* Register a call for panic conditions. */ | 502 | /* Register a call for panic conditions. */ |
| 510 | notifier_chain_register(&panic_notifier_list, &alpha_panic_block); | 503 | atomic_notifier_chain_register(&panic_notifier_list, |
| 504 | &alpha_panic_block); | ||
| 511 | 505 | ||
| 512 | #ifdef CONFIG_ALPHA_GENERIC | 506 | #ifdef CONFIG_ALPHA_GENERIC |
| 513 | /* Assume that we've booted from SRM if we haven't booted from MILO. | 507 | /* Assume that we've booted from SRM if we haven't booted from MILO. |
diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index 6d5251254f68..bf6b65c81bef 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/bootmem.h> | 13 | #include <linux/bootmem.h> |
| 14 | #include <linux/swap.h> | 14 | #include <linux/swap.h> |
| 15 | #include <linux/initrd.h> | 15 | #include <linux/initrd.h> |
| 16 | #include <linux/pfn.h> | ||
| 16 | 17 | ||
| 17 | #include <asm/hwrpb.h> | 18 | #include <asm/hwrpb.h> |
| 18 | #include <asm/pgalloc.h> | 19 | #include <asm/pgalloc.h> |
| @@ -27,9 +28,6 @@ bootmem_data_t node_bdata[MAX_NUMNODES]; | |||
| 27 | #define DBGDCONT(args...) | 28 | #define DBGDCONT(args...) |
| 28 | #endif | 29 | #endif |
| 29 | 30 | ||
| 30 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 31 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 32 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 33 | #define for_each_mem_cluster(memdesc, cluster, i) \ | 31 | #define for_each_mem_cluster(memdesc, cluster, i) \ |
| 34 | for ((cluster) = (memdesc)->cluster, (i) = 0; \ | 32 | for ((cluster) = (memdesc)->cluster, (i) = 0; \ |
| 35 | (i) < (memdesc)->numclusters; (i)++, (cluster)++) | 33 | (i) < (memdesc)->numclusters; (i)++, (cluster)++) |
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index bf2e72698d02..9731b3f826ab 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -8,6 +8,7 @@ mainmenu "Linux Kernel Configuration" | |||
| 8 | config ARM | 8 | config ARM |
| 9 | bool | 9 | bool |
| 10 | default y | 10 | default y |
| 11 | select RTC_LIB | ||
| 11 | help | 12 | help |
| 12 | The ARM series is a line of low-power-consumption RISC chip designs | 13 | The ARM series is a line of low-power-consumption RISC chip designs |
| 13 | licensed by ARM Ltd and targeted at embedded applications and | 14 | licensed by ARM Ltd and targeted at embedded applications and |
| @@ -839,6 +840,8 @@ source "drivers/usb/Kconfig" | |||
| 839 | 840 | ||
| 840 | source "drivers/mmc/Kconfig" | 841 | source "drivers/mmc/Kconfig" |
| 841 | 842 | ||
| 843 | source "drivers/rtc/Kconfig" | ||
| 844 | |||
| 842 | endmenu | 845 | endmenu |
| 843 | 846 | ||
| 844 | source "fs/Kconfig" | 847 | source "fs/Kconfig" |
diff --git a/arch/arm/common/rtctime.c b/arch/arm/common/rtctime.c index e851d86c212c..35c9a64ac14c 100644 --- a/arch/arm/common/rtctime.c +++ b/arch/arm/common/rtctime.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/capability.h> | 20 | #include <linux/capability.h> |
| 21 | #include <linux/device.h> | 21 | #include <linux/device.h> |
| 22 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
| 23 | #include <linux/rtc.h> | ||
| 23 | 24 | ||
| 24 | #include <asm/rtc.h> | 25 | #include <asm/rtc.h> |
| 25 | #include <asm/semaphore.h> | 26 | #include <asm/semaphore.h> |
| @@ -42,89 +43,6 @@ static struct rtc_ops *rtc_ops; | |||
| 42 | 43 | ||
| 43 | #define rtc_epoch 1900UL | 44 | #define rtc_epoch 1900UL |
| 44 | 45 | ||
| 45 | static const unsigned char days_in_month[] = { | ||
| 46 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
| 47 | }; | ||
| 48 | |||
| 49 | #define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) | ||
| 50 | #define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) | ||
| 51 | |||
| 52 | static int month_days(unsigned int month, unsigned int year) | ||
| 53 | { | ||
| 54 | return days_in_month[month] + (LEAP_YEAR(year) && month == 1); | ||
| 55 | } | ||
| 56 | |||
| 57 | /* | ||
| 58 | * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. | ||
| 59 | */ | ||
| 60 | void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) | ||
| 61 | { | ||
| 62 | int days, month, year; | ||
| 63 | |||
| 64 | days = time / 86400; | ||
| 65 | time -= days * 86400; | ||
| 66 | |||
| 67 | tm->tm_wday = (days + 4) % 7; | ||
| 68 | |||
| 69 | year = 1970 + days / 365; | ||
| 70 | days -= (year - 1970) * 365 | ||
| 71 | + LEAPS_THRU_END_OF(year - 1) | ||
| 72 | - LEAPS_THRU_END_OF(1970 - 1); | ||
| 73 | if (days < 0) { | ||
| 74 | year -= 1; | ||
| 75 | days += 365 + LEAP_YEAR(year); | ||
| 76 | } | ||
| 77 | tm->tm_year = year - 1900; | ||
| 78 | tm->tm_yday = days + 1; | ||
| 79 | |||
| 80 | for (month = 0; month < 11; month++) { | ||
| 81 | int newdays; | ||
| 82 | |||
| 83 | newdays = days - month_days(month, year); | ||
| 84 | if (newdays < 0) | ||
| 85 | break; | ||
| 86 | days = newdays; | ||
| 87 | } | ||
| 88 | tm->tm_mon = month; | ||
| 89 | tm->tm_mday = days + 1; | ||
| 90 | |||
| 91 | tm->tm_hour = time / 3600; | ||
| 92 | time -= tm->tm_hour * 3600; | ||
| 93 | tm->tm_min = time / 60; | ||
| 94 | tm->tm_sec = time - tm->tm_min * 60; | ||
| 95 | } | ||
| 96 | EXPORT_SYMBOL(rtc_time_to_tm); | ||
| 97 | |||
| 98 | /* | ||
| 99 | * Does the rtc_time represent a valid date/time? | ||
| 100 | */ | ||
| 101 | int rtc_valid_tm(struct rtc_time *tm) | ||
| 102 | { | ||
| 103 | if (tm->tm_year < 70 || | ||
| 104 | tm->tm_mon >= 12 || | ||
| 105 | tm->tm_mday < 1 || | ||
| 106 | tm->tm_mday > month_days(tm->tm_mon, tm->tm_year + 1900) || | ||
| 107 | tm->tm_hour >= 24 || | ||
| 108 | tm->tm_min >= 60 || | ||
| 109 | tm->tm_sec >= 60) | ||
| 110 | return -EINVAL; | ||
| 111 | |||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | EXPORT_SYMBOL(rtc_valid_tm); | ||
| 115 | |||
| 116 | /* | ||
| 117 | * Convert Gregorian date to seconds since 01-01-1970 00:00:00. | ||
| 118 | */ | ||
| 119 | int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) | ||
| 120 | { | ||
| 121 | *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, | ||
| 122 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
| 123 | |||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | EXPORT_SYMBOL(rtc_tm_to_time); | ||
| 127 | |||
| 128 | /* | 46 | /* |
| 129 | * Calculate the next alarm time given the requested alarm time mask | 47 | * Calculate the next alarm time given the requested alarm time mask |
| 130 | * and the current time. | 48 | * and the current time. |
| @@ -151,13 +69,13 @@ void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc | |||
| 151 | } | 69 | } |
| 152 | } | 70 | } |
| 153 | 71 | ||
| 154 | static inline int rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm) | 72 | static inline int rtc_arm_read_time(struct rtc_ops *ops, struct rtc_time *tm) |
| 155 | { | 73 | { |
| 156 | memset(tm, 0, sizeof(struct rtc_time)); | 74 | memset(tm, 0, sizeof(struct rtc_time)); |
| 157 | return ops->read_time(tm); | 75 | return ops->read_time(tm); |
| 158 | } | 76 | } |
| 159 | 77 | ||
| 160 | static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm) | 78 | static inline int rtc_arm_set_time(struct rtc_ops *ops, struct rtc_time *tm) |
| 161 | { | 79 | { |
| 162 | int ret; | 80 | int ret; |
| 163 | 81 | ||
| @@ -168,7 +86,7 @@ static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm) | |||
| 168 | return ret; | 86 | return ret; |
| 169 | } | 87 | } |
| 170 | 88 | ||
| 171 | static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) | 89 | static inline int rtc_arm_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) |
| 172 | { | 90 | { |
| 173 | int ret = -EINVAL; | 91 | int ret = -EINVAL; |
| 174 | if (ops->read_alarm) { | 92 | if (ops->read_alarm) { |
| @@ -178,7 +96,7 @@ static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) | |||
| 178 | return ret; | 96 | return ret; |
| 179 | } | 97 | } |
| 180 | 98 | ||
| 181 | static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) | 99 | static inline int rtc_arm_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) |
| 182 | { | 100 | { |
| 183 | int ret = -EINVAL; | 101 | int ret = -EINVAL; |
| 184 | if (ops->set_alarm) | 102 | if (ops->set_alarm) |
| @@ -266,7 +184,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
| 266 | 184 | ||
| 267 | switch (cmd) { | 185 | switch (cmd) { |
| 268 | case RTC_ALM_READ: | 186 | case RTC_ALM_READ: |
| 269 | ret = rtc_read_alarm(ops, &alrm); | 187 | ret = rtc_arm_read_alarm(ops, &alrm); |
| 270 | if (ret) | 188 | if (ret) |
| 271 | break; | 189 | break; |
| 272 | ret = copy_to_user(uarg, &alrm.time, sizeof(tm)); | 190 | ret = copy_to_user(uarg, &alrm.time, sizeof(tm)); |
| @@ -288,11 +206,11 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
| 288 | alrm.time.tm_wday = -1; | 206 | alrm.time.tm_wday = -1; |
| 289 | alrm.time.tm_yday = -1; | 207 | alrm.time.tm_yday = -1; |
| 290 | alrm.time.tm_isdst = -1; | 208 | alrm.time.tm_isdst = -1; |
| 291 | ret = rtc_set_alarm(ops, &alrm); | 209 | ret = rtc_arm_set_alarm(ops, &alrm); |
| 292 | break; | 210 | break; |
| 293 | 211 | ||
| 294 | case RTC_RD_TIME: | 212 | case RTC_RD_TIME: |
| 295 | ret = rtc_read_time(ops, &tm); | 213 | ret = rtc_arm_read_time(ops, &tm); |
| 296 | if (ret) | 214 | if (ret) |
| 297 | break; | 215 | break; |
| 298 | ret = copy_to_user(uarg, &tm, sizeof(tm)); | 216 | ret = copy_to_user(uarg, &tm, sizeof(tm)); |
| @@ -310,7 +228,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
| 310 | ret = -EFAULT; | 228 | ret = -EFAULT; |
| 311 | break; | 229 | break; |
| 312 | } | 230 | } |
| 313 | ret = rtc_set_time(ops, &tm); | 231 | ret = rtc_arm_set_time(ops, &tm); |
| 314 | break; | 232 | break; |
| 315 | 233 | ||
| 316 | case RTC_EPOCH_SET: | 234 | case RTC_EPOCH_SET: |
| @@ -341,11 +259,11 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
| 341 | ret = -EFAULT; | 259 | ret = -EFAULT; |
| 342 | break; | 260 | break; |
| 343 | } | 261 | } |
| 344 | ret = rtc_set_alarm(ops, &alrm); | 262 | ret = rtc_arm_set_alarm(ops, &alrm); |
| 345 | break; | 263 | break; |
| 346 | 264 | ||
| 347 | case RTC_WKALM_RD: | 265 | case RTC_WKALM_RD: |
| 348 | ret = rtc_read_alarm(ops, &alrm); | 266 | ret = rtc_arm_read_alarm(ops, &alrm); |
| 349 | if (ret) | 267 | if (ret) |
| 350 | break; | 268 | break; |
| 351 | ret = copy_to_user(uarg, &alrm, sizeof(alrm)); | 269 | ret = copy_to_user(uarg, &alrm, sizeof(alrm)); |
| @@ -435,7 +353,7 @@ static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eo | |||
| 435 | struct rtc_time tm; | 353 | struct rtc_time tm; |
| 436 | char *p = page; | 354 | char *p = page; |
| 437 | 355 | ||
| 438 | if (rtc_read_time(ops, &tm) == 0) { | 356 | if (rtc_arm_read_time(ops, &tm) == 0) { |
| 439 | p += sprintf(p, | 357 | p += sprintf(p, |
| 440 | "rtc_time\t: %02d:%02d:%02d\n" | 358 | "rtc_time\t: %02d:%02d:%02d\n" |
| 441 | "rtc_date\t: %04d-%02d-%02d\n" | 359 | "rtc_date\t: %04d-%02d-%02d\n" |
| @@ -445,7 +363,7 @@ static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eo | |||
| 445 | rtc_epoch); | 363 | rtc_epoch); |
| 446 | } | 364 | } |
| 447 | 365 | ||
| 448 | if (rtc_read_alarm(ops, &alrm) == 0) { | 366 | if (rtc_arm_read_alarm(ops, &alrm) == 0) { |
| 449 | p += sprintf(p, "alrm_time\t: "); | 367 | p += sprintf(p, "alrm_time\t: "); |
| 450 | if ((unsigned int)alrm.time.tm_hour <= 24) | 368 | if ((unsigned int)alrm.time.tm_hour <= 24) |
| 451 | p += sprintf(p, "%02d:", alrm.time.tm_hour); | 369 | p += sprintf(p, "%02d:", alrm.time.tm_hour); |
diff --git a/arch/arm/mach-integrator/time.c b/arch/arm/mach-integrator/time.c index 3c22c16b38bf..bc07f52a6fd7 100644 --- a/arch/arm/mach-integrator/time.c +++ b/arch/arm/mach-integrator/time.c | |||
| @@ -40,13 +40,13 @@ static int integrator_set_rtc(void) | |||
| 40 | return 1; | 40 | return 1; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | static int rtc_read_alarm(struct rtc_wkalrm *alrm) | 43 | static int integrator_rtc_read_alarm(struct rtc_wkalrm *alrm) |
| 44 | { | 44 | { |
| 45 | rtc_time_to_tm(readl(rtc_base + RTC_MR), &alrm->time); | 45 | rtc_time_to_tm(readl(rtc_base + RTC_MR), &alrm->time); |
| 46 | return 0; | 46 | return 0; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | static inline int rtc_set_alarm(struct rtc_wkalrm *alrm) | 49 | static inline int integrator_rtc_set_alarm(struct rtc_wkalrm *alrm) |
| 50 | { | 50 | { |
| 51 | unsigned long time; | 51 | unsigned long time; |
| 52 | int ret; | 52 | int ret; |
| @@ -62,7 +62,7 @@ static inline int rtc_set_alarm(struct rtc_wkalrm *alrm) | |||
| 62 | return ret; | 62 | return ret; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | static int rtc_read_time(struct rtc_time *tm) | 65 | static int integrator_rtc_read_time(struct rtc_time *tm) |
| 66 | { | 66 | { |
| 67 | rtc_time_to_tm(readl(rtc_base + RTC_DR), tm); | 67 | rtc_time_to_tm(readl(rtc_base + RTC_DR), tm); |
| 68 | return 0; | 68 | return 0; |
| @@ -76,7 +76,7 @@ static int rtc_read_time(struct rtc_time *tm) | |||
| 76 | * edge of the 1Hz clock, we must write the time one second | 76 | * edge of the 1Hz clock, we must write the time one second |
| 77 | * in advance. | 77 | * in advance. |
| 78 | */ | 78 | */ |
| 79 | static inline int rtc_set_time(struct rtc_time *tm) | 79 | static inline int integrator_rtc_set_time(struct rtc_time *tm) |
| 80 | { | 80 | { |
| 81 | unsigned long time; | 81 | unsigned long time; |
| 82 | int ret; | 82 | int ret; |
| @@ -90,10 +90,10 @@ static inline int rtc_set_time(struct rtc_time *tm) | |||
| 90 | 90 | ||
| 91 | static struct rtc_ops rtc_ops = { | 91 | static struct rtc_ops rtc_ops = { |
| 92 | .owner = THIS_MODULE, | 92 | .owner = THIS_MODULE, |
| 93 | .read_time = rtc_read_time, | 93 | .read_time = integrator_rtc_read_time, |
| 94 | .set_time = rtc_set_time, | 94 | .set_time = integrator_rtc_set_time, |
| 95 | .read_alarm = rtc_read_alarm, | 95 | .read_alarm = integrator_rtc_read_alarm, |
| 96 | .set_alarm = rtc_set_alarm, | 96 | .set_alarm = integrator_rtc_set_alarm, |
| 97 | }; | 97 | }; |
| 98 | 98 | ||
| 99 | static irqreturn_t arm_rtc_interrupt(int irq, void *dev_id, | 99 | static irqreturn_t arm_rtc_interrupt(int irq, void *dev_id, |
diff --git a/arch/arm/mach-omap1/board-netstar.c b/arch/arm/mach-omap1/board-netstar.c index 60d5f8a3339c..7520e602d7a2 100644 --- a/arch/arm/mach-omap1/board-netstar.c +++ b/arch/arm/mach-omap1/board-netstar.c | |||
| @@ -141,7 +141,7 @@ static int __init netstar_late_init(void) | |||
| 141 | /* TODO: Setup front panel switch here */ | 141 | /* TODO: Setup front panel switch here */ |
| 142 | 142 | ||
| 143 | /* Setup panic notifier */ | 143 | /* Setup panic notifier */ |
| 144 | notifier_chain_register(&panic_notifier_list, &panic_block); | 144 | atomic_notifier_chain_register(&panic_notifier_list, &panic_block); |
| 145 | 145 | ||
| 146 | return 0; | 146 | return 0; |
| 147 | } | 147 | } |
diff --git a/arch/arm/mach-omap1/board-voiceblue.c b/arch/arm/mach-omap1/board-voiceblue.c index bfd5fdd1a875..52e4a9d69642 100644 --- a/arch/arm/mach-omap1/board-voiceblue.c +++ b/arch/arm/mach-omap1/board-voiceblue.c | |||
| @@ -235,7 +235,7 @@ static struct notifier_block panic_block = { | |||
| 235 | static int __init voiceblue_setup(void) | 235 | static int __init voiceblue_setup(void) |
| 236 | { | 236 | { |
| 237 | /* Setup panic notifier */ | 237 | /* Setup panic notifier */ |
| 238 | notifier_chain_register(&panic_notifier_list, &panic_block); | 238 | atomic_notifier_chain_register(&panic_notifier_list, &panic_block); |
| 239 | 239 | ||
| 240 | return 0; | 240 | return 0; |
| 241 | } | 241 | } |
diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c index 9b48a90aefce..5efa84749f37 100644 --- a/arch/arm/mach-pxa/generic.c +++ b/arch/arm/mach-pxa/generic.c | |||
| @@ -319,6 +319,11 @@ void __init pxa_set_ficp_info(struct pxaficp_platform_data *info) | |||
| 319 | pxaficp_device.dev.platform_data = info; | 319 | pxaficp_device.dev.platform_data = info; |
| 320 | } | 320 | } |
| 321 | 321 | ||
| 322 | static struct platform_device pxartc_device = { | ||
| 323 | .name = "sa1100-rtc", | ||
| 324 | .id = -1, | ||
| 325 | }; | ||
| 326 | |||
| 322 | static struct platform_device *devices[] __initdata = { | 327 | static struct platform_device *devices[] __initdata = { |
| 323 | &pxamci_device, | 328 | &pxamci_device, |
| 324 | &udc_device, | 329 | &udc_device, |
| @@ -329,6 +334,7 @@ static struct platform_device *devices[] __initdata = { | |||
| 329 | &pxaficp_device, | 334 | &pxaficp_device, |
| 330 | &i2c_device, | 335 | &i2c_device, |
| 331 | &i2s_device, | 336 | &i2s_device, |
| 337 | &pxartc_device, | ||
| 332 | }; | 338 | }; |
| 333 | 339 | ||
| 334 | static int __init pxa_init(void) | 340 | static int __init pxa_init(void) |
diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 2abdc419e984..9ea71551fc04 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c | |||
| @@ -324,6 +324,11 @@ void sa11x0_set_irda_data(struct irda_platform_data *irda) | |||
| 324 | sa11x0ir_device.dev.platform_data = irda; | 324 | sa11x0ir_device.dev.platform_data = irda; |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | static struct platform_device sa11x0rtc_device = { | ||
| 328 | .name = "sa1100-rtc", | ||
| 329 | .id = -1, | ||
| 330 | }; | ||
| 331 | |||
| 327 | static struct platform_device *sa11x0_devices[] __initdata = { | 332 | static struct platform_device *sa11x0_devices[] __initdata = { |
| 328 | &sa11x0udc_device, | 333 | &sa11x0udc_device, |
| 329 | &sa11x0uart1_device, | 334 | &sa11x0uart1_device, |
| @@ -333,6 +338,7 @@ static struct platform_device *sa11x0_devices[] __initdata = { | |||
| 333 | &sa11x0pcmcia_device, | 338 | &sa11x0pcmcia_device, |
| 334 | &sa11x0fb_device, | 339 | &sa11x0fb_device, |
| 335 | &sa11x0mtd_device, | 340 | &sa11x0mtd_device, |
| 341 | &sa11x0rtc_device, | ||
| 336 | }; | 342 | }; |
| 337 | 343 | ||
| 338 | static int __init sa1100_init(void) | 344 | static int __init sa1100_init(void) |
diff --git a/arch/arm26/mm/init.c b/arch/arm26/mm/init.c index e3ecaa453747..7da8a5205678 100644 --- a/arch/arm26/mm/init.c +++ b/arch/arm26/mm/init.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/initrd.h> | 23 | #include <linux/initrd.h> |
| 24 | #include <linux/bootmem.h> | 24 | #include <linux/bootmem.h> |
| 25 | #include <linux/blkdev.h> | 25 | #include <linux/blkdev.h> |
| 26 | #include <linux/pfn.h> | ||
| 26 | 27 | ||
| 27 | #include <asm/segment.h> | 28 | #include <asm/segment.h> |
| 28 | #include <asm/mach-types.h> | 29 | #include <asm/mach-types.h> |
| @@ -101,12 +102,6 @@ struct node_info { | |||
| 101 | int bootmap_pages; | 102 | int bootmap_pages; |
| 102 | }; | 103 | }; |
| 103 | 104 | ||
| 104 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 105 | #define PFN_UP(x) (PAGE_ALIGN(x) >> PAGE_SHIFT) | ||
| 106 | #define PFN_SIZE(x) ((x) >> PAGE_SHIFT) | ||
| 107 | #define PFN_RANGE(s,e) PFN_SIZE(PAGE_ALIGN((unsigned long)(e)) - \ | ||
| 108 | (((unsigned long)(s)) & PAGE_MASK)) | ||
| 109 | |||
| 110 | /* | 105 | /* |
| 111 | * FIXME: We really want to avoid allocating the bootmap bitmap | 106 | * FIXME: We really want to avoid allocating the bootmap bitmap |
| 112 | * over the top of the initrd. Hopefully, this is located towards | 107 | * over the top of the initrd. Hopefully, this is located towards |
diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c index 1ba57efff60d..619a6eefd893 100644 --- a/arch/cris/kernel/setup.c +++ b/arch/cris/kernel/setup.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/seq_file.h> | 18 | #include <linux/seq_file.h> |
| 19 | #include <linux/tty.h> | 19 | #include <linux/tty.h> |
| 20 | #include <linux/utsname.h> | 20 | #include <linux/utsname.h> |
| 21 | #include <linux/pfn.h> | ||
| 21 | 22 | ||
| 22 | #include <asm/setup.h> | 23 | #include <asm/setup.h> |
| 23 | 24 | ||
| @@ -88,10 +89,6 @@ setup_arch(char **cmdline_p) | |||
| 88 | init_mm.end_data = (unsigned long) &_edata; | 89 | init_mm.end_data = (unsigned long) &_edata; |
| 89 | init_mm.brk = (unsigned long) &_end; | 90 | init_mm.brk = (unsigned long) &_end; |
| 90 | 91 | ||
| 91 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 92 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 93 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 94 | |||
| 95 | /* min_low_pfn points to the start of DRAM, start_pfn points | 92 | /* min_low_pfn points to the start of DRAM, start_pfn points |
| 96 | * to the first DRAM pages after the kernel, and max_low_pfn | 93 | * to the first DRAM pages after the kernel, and max_low_pfn |
| 97 | * to the end of DRAM. | 94 | * to the end of DRAM. |
diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index f7db71d0b913..f17bd1d2707e 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig | |||
| @@ -231,6 +231,15 @@ config SCHED_SMT | |||
| 231 | cost of slightly increased overhead in some places. If unsure say | 231 | cost of slightly increased overhead in some places. If unsure say |
| 232 | N here. | 232 | N here. |
| 233 | 233 | ||
| 234 | config SCHED_MC | ||
| 235 | bool "Multi-core scheduler support" | ||
| 236 | depends on SMP | ||
| 237 | default y | ||
| 238 | help | ||
| 239 | Multi-core scheduler support improves the CPU scheduler's decision | ||
| 240 | making when dealing with multi-core CPU chips at a cost of slightly | ||
| 241 | increased overhead in some places. If unsure say N here. | ||
| 242 | |||
| 234 | source "kernel/Kconfig.preempt" | 243 | source "kernel/Kconfig.preempt" |
| 235 | 244 | ||
| 236 | config X86_UP_APIC | 245 | config X86_UP_APIC |
diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S index 2ac40c8244c4..0000a2674537 100644 --- a/arch/i386/boot/video.S +++ b/arch/i386/boot/video.S | |||
| @@ -1924,6 +1924,7 @@ skip10: movb %ah, %al | |||
| 1924 | ret | 1924 | ret |
| 1925 | 1925 | ||
| 1926 | store_edid: | 1926 | store_edid: |
| 1927 | #ifdef CONFIG_FB_FIRMWARE_EDID | ||
| 1927 | pushw %es # just save all registers | 1928 | pushw %es # just save all registers |
| 1928 | pushw %ax | 1929 | pushw %ax |
| 1929 | pushw %bx | 1930 | pushw %bx |
| @@ -1954,6 +1955,7 @@ store_edid: | |||
| 1954 | popw %bx | 1955 | popw %bx |
| 1955 | popw %ax | 1956 | popw %ax |
| 1956 | popw %es | 1957 | popw %es |
| 1958 | #endif | ||
| 1957 | ret | 1959 | ret |
| 1958 | 1960 | ||
| 1959 | # VIDEO_SELECT-only variables | 1961 | # VIDEO_SELECT-only variables |
diff --git a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c index 7e3d6b6a4e96..a06a49075f10 100644 --- a/arch/i386/kernel/cpu/common.c +++ b/arch/i386/kernel/cpu/common.c | |||
| @@ -266,7 +266,7 @@ static void __init early_cpu_detect(void) | |||
| 266 | void __cpuinit generic_identify(struct cpuinfo_x86 * c) | 266 | void __cpuinit generic_identify(struct cpuinfo_x86 * c) |
| 267 | { | 267 | { |
| 268 | u32 tfms, xlvl; | 268 | u32 tfms, xlvl; |
| 269 | int junk; | 269 | int ebx; |
| 270 | 270 | ||
| 271 | if (have_cpuid_p()) { | 271 | if (have_cpuid_p()) { |
| 272 | /* Get vendor name */ | 272 | /* Get vendor name */ |
| @@ -282,7 +282,7 @@ void __cpuinit generic_identify(struct cpuinfo_x86 * c) | |||
| 282 | /* Intel-defined flags: level 0x00000001 */ | 282 | /* Intel-defined flags: level 0x00000001 */ |
| 283 | if ( c->cpuid_level >= 0x00000001 ) { | 283 | if ( c->cpuid_level >= 0x00000001 ) { |
| 284 | u32 capability, excap; | 284 | u32 capability, excap; |
| 285 | cpuid(0x00000001, &tfms, &junk, &excap, &capability); | 285 | cpuid(0x00000001, &tfms, &ebx, &excap, &capability); |
| 286 | c->x86_capability[0] = capability; | 286 | c->x86_capability[0] = capability; |
| 287 | c->x86_capability[4] = excap; | 287 | c->x86_capability[4] = excap; |
| 288 | c->x86 = (tfms >> 8) & 15; | 288 | c->x86 = (tfms >> 8) & 15; |
| @@ -292,6 +292,11 @@ void __cpuinit generic_identify(struct cpuinfo_x86 * c) | |||
| 292 | if (c->x86 >= 0x6) | 292 | if (c->x86 >= 0x6) |
| 293 | c->x86_model += ((tfms >> 16) & 0xF) << 4; | 293 | c->x86_model += ((tfms >> 16) & 0xF) << 4; |
| 294 | c->x86_mask = tfms & 15; | 294 | c->x86_mask = tfms & 15; |
| 295 | #ifdef CONFIG_SMP | ||
| 296 | c->apicid = phys_pkg_id((ebx >> 24) & 0xFF, 0); | ||
| 297 | #else | ||
| 298 | c->apicid = (ebx >> 24) & 0xFF; | ||
| 299 | #endif | ||
| 295 | } else { | 300 | } else { |
| 296 | /* Have CPUID level 0 only - unheard of */ | 301 | /* Have CPUID level 0 only - unheard of */ |
| 297 | c->x86 = 4; | 302 | c->x86 = 4; |
| @@ -474,7 +479,6 @@ void __cpuinit detect_ht(struct cpuinfo_x86 *c) | |||
| 474 | 479 | ||
| 475 | cpuid(1, &eax, &ebx, &ecx, &edx); | 480 | cpuid(1, &eax, &ebx, &ecx, &edx); |
| 476 | 481 | ||
| 477 | c->apicid = phys_pkg_id((ebx >> 24) & 0xFF, 0); | ||
| 478 | 482 | ||
| 479 | if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY)) | 483 | if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY)) |
| 480 | return; | 484 | return; |
diff --git a/arch/i386/kernel/cpu/intel_cacheinfo.c b/arch/i386/kernel/cpu/intel_cacheinfo.c index ce61921369e5..9df87b03612c 100644 --- a/arch/i386/kernel/cpu/intel_cacheinfo.c +++ b/arch/i386/kernel/cpu/intel_cacheinfo.c | |||
| @@ -173,6 +173,10 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) | |||
| 173 | unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */ | 173 | unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */ |
| 174 | unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */ | 174 | unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */ |
| 175 | unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */ | 175 | unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */ |
| 176 | unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb; | ||
| 177 | #ifdef CONFIG_SMP | ||
| 178 | unsigned int cpu = (c == &boot_cpu_data) ? 0 : (c - cpu_data); | ||
| 179 | #endif | ||
| 176 | 180 | ||
| 177 | if (c->cpuid_level > 3) { | 181 | if (c->cpuid_level > 3) { |
| 178 | static int is_initialized; | 182 | static int is_initialized; |
| @@ -205,9 +209,15 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) | |||
| 205 | break; | 209 | break; |
| 206 | case 2: | 210 | case 2: |
| 207 | new_l2 = this_leaf.size/1024; | 211 | new_l2 = this_leaf.size/1024; |
| 212 | num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; | ||
| 213 | index_msb = get_count_order(num_threads_sharing); | ||
| 214 | l2_id = c->apicid >> index_msb; | ||
| 208 | break; | 215 | break; |
| 209 | case 3: | 216 | case 3: |
| 210 | new_l3 = this_leaf.size/1024; | 217 | new_l3 = this_leaf.size/1024; |
| 218 | num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; | ||
| 219 | index_msb = get_count_order(num_threads_sharing); | ||
| 220 | l3_id = c->apicid >> index_msb; | ||
| 211 | break; | 221 | break; |
| 212 | default: | 222 | default: |
| 213 | break; | 223 | break; |
| @@ -215,11 +225,19 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) | |||
| 215 | } | 225 | } |
| 216 | } | 226 | } |
| 217 | } | 227 | } |
| 218 | if (c->cpuid_level > 1) { | 228 | /* |
| 229 | * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for | ||
| 230 | * trace cache | ||
| 231 | */ | ||
| 232 | if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) { | ||
| 219 | /* supports eax=2 call */ | 233 | /* supports eax=2 call */ |
| 220 | int i, j, n; | 234 | int i, j, n; |
| 221 | int regs[4]; | 235 | int regs[4]; |
| 222 | unsigned char *dp = (unsigned char *)regs; | 236 | unsigned char *dp = (unsigned char *)regs; |
| 237 | int only_trace = 0; | ||
| 238 | |||
| 239 | if (num_cache_leaves != 0 && c->x86 == 15) | ||
| 240 | only_trace = 1; | ||
| 223 | 241 | ||
| 224 | /* Number of times to iterate */ | 242 | /* Number of times to iterate */ |
| 225 | n = cpuid_eax(2) & 0xFF; | 243 | n = cpuid_eax(2) & 0xFF; |
| @@ -241,6 +259,8 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) | |||
| 241 | while (cache_table[k].descriptor != 0) | 259 | while (cache_table[k].descriptor != 0) |
| 242 | { | 260 | { |
| 243 | if (cache_table[k].descriptor == des) { | 261 | if (cache_table[k].descriptor == des) { |
| 262 | if (only_trace && cache_table[k].cache_type != LVL_TRACE) | ||
| 263 | break; | ||
| 244 | switch (cache_table[k].cache_type) { | 264 | switch (cache_table[k].cache_type) { |
| 245 | case LVL_1_INST: | 265 | case LVL_1_INST: |
| 246 | l1i += cache_table[k].size; | 266 | l1i += cache_table[k].size; |
| @@ -266,34 +286,45 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) | |||
| 266 | } | 286 | } |
| 267 | } | 287 | } |
| 268 | } | 288 | } |
| 289 | } | ||
| 269 | 290 | ||
| 270 | if (new_l1d) | 291 | if (new_l1d) |
| 271 | l1d = new_l1d; | 292 | l1d = new_l1d; |
| 272 | 293 | ||
| 273 | if (new_l1i) | 294 | if (new_l1i) |
| 274 | l1i = new_l1i; | 295 | l1i = new_l1i; |
| 275 | 296 | ||
| 276 | if (new_l2) | 297 | if (new_l2) { |
| 277 | l2 = new_l2; | 298 | l2 = new_l2; |
| 299 | #ifdef CONFIG_SMP | ||
| 300 | cpu_llc_id[cpu] = l2_id; | ||
| 301 | #endif | ||
| 302 | } | ||
| 278 | 303 | ||
| 279 | if (new_l3) | 304 | if (new_l3) { |
| 280 | l3 = new_l3; | 305 | l3 = new_l3; |
| 306 | #ifdef CONFIG_SMP | ||
| 307 | cpu_llc_id[cpu] = l3_id; | ||
| 308 | #endif | ||
| 309 | } | ||
| 281 | 310 | ||
| 282 | if ( trace ) | 311 | if (trace) |
| 283 | printk (KERN_INFO "CPU: Trace cache: %dK uops", trace); | 312 | printk (KERN_INFO "CPU: Trace cache: %dK uops", trace); |
| 284 | else if ( l1i ) | 313 | else if ( l1i ) |
| 285 | printk (KERN_INFO "CPU: L1 I cache: %dK", l1i); | 314 | printk (KERN_INFO "CPU: L1 I cache: %dK", l1i); |
| 286 | if ( l1d ) | ||
| 287 | printk(", L1 D cache: %dK\n", l1d); | ||
| 288 | else | ||
| 289 | printk("\n"); | ||
| 290 | if ( l2 ) | ||
| 291 | printk(KERN_INFO "CPU: L2 cache: %dK\n", l2); | ||
| 292 | if ( l3 ) | ||
| 293 | printk(KERN_INFO "CPU: L3 cache: %dK\n", l3); | ||
| 294 | 315 | ||
| 295 | c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d)); | 316 | if (l1d) |
| 296 | } | 317 | printk(", L1 D cache: %dK\n", l1d); |
| 318 | else | ||
| 319 | printk("\n"); | ||
| 320 | |||
| 321 | if (l2) | ||
| 322 | printk(KERN_INFO "CPU: L2 cache: %dK\n", l2); | ||
| 323 | |||
| 324 | if (l3) | ||
| 325 | printk(KERN_INFO "CPU: L3 cache: %dK\n", l3); | ||
| 326 | |||
| 327 | c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d)); | ||
| 297 | 328 | ||
| 298 | return l2; | 329 | return l2; |
| 299 | } | 330 | } |
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 6917daa159ab..8c08660b4e5d 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c | |||
| @@ -46,6 +46,7 @@ | |||
| 46 | #include <linux/kexec.h> | 46 | #include <linux/kexec.h> |
| 47 | #include <linux/crash_dump.h> | 47 | #include <linux/crash_dump.h> |
| 48 | #include <linux/dmi.h> | 48 | #include <linux/dmi.h> |
| 49 | #include <linux/pfn.h> | ||
| 49 | 50 | ||
| 50 | #include <video/edid.h> | 51 | #include <video/edid.h> |
| 51 | 52 | ||
diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 82371d83bfa9..a6969903f2d6 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c | |||
| @@ -72,6 +72,9 @@ int phys_proc_id[NR_CPUS] __read_mostly = {[0 ... NR_CPUS-1] = BAD_APICID}; | |||
| 72 | /* Core ID of each logical CPU */ | 72 | /* Core ID of each logical CPU */ |
| 73 | int cpu_core_id[NR_CPUS] __read_mostly = {[0 ... NR_CPUS-1] = BAD_APICID}; | 73 | int cpu_core_id[NR_CPUS] __read_mostly = {[0 ... NR_CPUS-1] = BAD_APICID}; |
| 74 | 74 | ||
| 75 | /* Last level cache ID of each logical CPU */ | ||
| 76 | int cpu_llc_id[NR_CPUS] __cpuinitdata = {[0 ... NR_CPUS-1] = BAD_APICID}; | ||
| 77 | |||
| 75 | /* representing HT siblings of each logical CPU */ | 78 | /* representing HT siblings of each logical CPU */ |
| 76 | cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly; | 79 | cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly; |
| 77 | EXPORT_SYMBOL(cpu_sibling_map); | 80 | EXPORT_SYMBOL(cpu_sibling_map); |
| @@ -440,6 +443,18 @@ static void __devinit smp_callin(void) | |||
| 440 | 443 | ||
| 441 | static int cpucount; | 444 | static int cpucount; |
| 442 | 445 | ||
| 446 | /* maps the cpu to the sched domain representing multi-core */ | ||
| 447 | cpumask_t cpu_coregroup_map(int cpu) | ||
| 448 | { | ||
| 449 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
| 450 | /* | ||
| 451 | * For perf, we return last level cache shared map. | ||
| 452 | * TBD: when power saving sched policy is added, we will return | ||
| 453 | * cpu_core_map when power saving policy is enabled | ||
| 454 | */ | ||
| 455 | return c->llc_shared_map; | ||
| 456 | } | ||
| 457 | |||
| 443 | /* representing cpus for which sibling maps can be computed */ | 458 | /* representing cpus for which sibling maps can be computed */ |
| 444 | static cpumask_t cpu_sibling_setup_map; | 459 | static cpumask_t cpu_sibling_setup_map; |
| 445 | 460 | ||
| @@ -459,12 +474,16 @@ set_cpu_sibling_map(int cpu) | |||
| 459 | cpu_set(cpu, cpu_sibling_map[i]); | 474 | cpu_set(cpu, cpu_sibling_map[i]); |
| 460 | cpu_set(i, cpu_core_map[cpu]); | 475 | cpu_set(i, cpu_core_map[cpu]); |
| 461 | cpu_set(cpu, cpu_core_map[i]); | 476 | cpu_set(cpu, cpu_core_map[i]); |
| 477 | cpu_set(i, c[cpu].llc_shared_map); | ||
| 478 | cpu_set(cpu, c[i].llc_shared_map); | ||
| 462 | } | 479 | } |
| 463 | } | 480 | } |
| 464 | } else { | 481 | } else { |
| 465 | cpu_set(cpu, cpu_sibling_map[cpu]); | 482 | cpu_set(cpu, cpu_sibling_map[cpu]); |
| 466 | } | 483 | } |
| 467 | 484 | ||
| 485 | cpu_set(cpu, c[cpu].llc_shared_map); | ||
| 486 | |||
| 468 | if (current_cpu_data.x86_max_cores == 1) { | 487 | if (current_cpu_data.x86_max_cores == 1) { |
| 469 | cpu_core_map[cpu] = cpu_sibling_map[cpu]; | 488 | cpu_core_map[cpu] = cpu_sibling_map[cpu]; |
| 470 | c[cpu].booted_cores = 1; | 489 | c[cpu].booted_cores = 1; |
| @@ -472,6 +491,11 @@ set_cpu_sibling_map(int cpu) | |||
| 472 | } | 491 | } |
| 473 | 492 | ||
| 474 | for_each_cpu_mask(i, cpu_sibling_setup_map) { | 493 | for_each_cpu_mask(i, cpu_sibling_setup_map) { |
| 494 | if (cpu_llc_id[cpu] != BAD_APICID && | ||
| 495 | cpu_llc_id[cpu] == cpu_llc_id[i]) { | ||
| 496 | cpu_set(i, c[cpu].llc_shared_map); | ||
| 497 | cpu_set(cpu, c[i].llc_shared_map); | ||
| 498 | } | ||
| 475 | if (phys_proc_id[cpu] == phys_proc_id[i]) { | 499 | if (phys_proc_id[cpu] == phys_proc_id[i]) { |
| 476 | cpu_set(i, cpu_core_map[cpu]); | 500 | cpu_set(i, cpu_core_map[cpu]); |
| 477 | cpu_set(cpu, cpu_core_map[i]); | 501 | cpu_set(cpu, cpu_core_map[i]); |
diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index ac687d00a1ce..326595f3fa4d 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S | |||
| @@ -310,3 +310,5 @@ ENTRY(sys_call_table) | |||
| 310 | .long sys_pselect6 | 310 | .long sys_pselect6 |
| 311 | .long sys_ppoll | 311 | .long sys_ppoll |
| 312 | .long sys_unshare /* 310 */ | 312 | .long sys_unshare /* 310 */ |
| 313 | .long sys_set_robust_list | ||
| 314 | .long sys_get_robust_list | ||
diff --git a/arch/i386/kernel/timers/timer_pm.c b/arch/i386/kernel/timers/timer_pm.c index 264edaaac315..144e94a04933 100644 --- a/arch/i386/kernel/timers/timer_pm.c +++ b/arch/i386/kernel/timers/timer_pm.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
| 17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
| 18 | #include <linux/pci.h> | ||
| 18 | #include <asm/types.h> | 19 | #include <asm/types.h> |
| 19 | #include <asm/timer.h> | 20 | #include <asm/timer.h> |
| 20 | #include <asm/smp.h> | 21 | #include <asm/smp.h> |
| @@ -45,24 +46,31 @@ static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED; | |||
| 45 | 46 | ||
| 46 | #define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */ | 47 | #define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */ |
| 47 | 48 | ||
| 49 | static int pmtmr_need_workaround __read_mostly = 1; | ||
| 50 | |||
| 48 | /*helper function to safely read acpi pm timesource*/ | 51 | /*helper function to safely read acpi pm timesource*/ |
| 49 | static inline u32 read_pmtmr(void) | 52 | static inline u32 read_pmtmr(void) |
| 50 | { | 53 | { |
| 51 | u32 v1=0,v2=0,v3=0; | 54 | if (pmtmr_need_workaround) { |
| 52 | /* It has been reported that because of various broken | 55 | u32 v1, v2, v3; |
| 53 | * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM time | 56 | |
| 54 | * source is not latched, so you must read it multiple | 57 | /* It has been reported that because of various broken |
| 55 | * times to insure a safe value is read. | 58 | * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM time |
| 56 | */ | 59 | * source is not latched, so you must read it multiple |
| 57 | do { | 60 | * times to insure a safe value is read. |
| 58 | v1 = inl(pmtmr_ioport); | 61 | */ |
| 59 | v2 = inl(pmtmr_ioport); | 62 | do { |
| 60 | v3 = inl(pmtmr_ioport); | 63 | v1 = inl(pmtmr_ioport); |
| 61 | } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) | 64 | v2 = inl(pmtmr_ioport); |
| 62 | || (v3 > v1 && v3 < v2)); | 65 | v3 = inl(pmtmr_ioport); |
| 63 | 66 | } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) | |
| 64 | /* mask the output to 24 bits */ | 67 | || (v3 > v1 && v3 < v2)); |
| 65 | return v2 & ACPI_PM_MASK; | 68 | |
| 69 | /* mask the output to 24 bits */ | ||
| 70 | return v2 & ACPI_PM_MASK; | ||
| 71 | } | ||
| 72 | |||
| 73 | return inl(pmtmr_ioport) & ACPI_PM_MASK; | ||
| 66 | } | 74 | } |
| 67 | 75 | ||
| 68 | 76 | ||
| @@ -263,6 +271,72 @@ struct init_timer_opts __initdata timer_pmtmr_init = { | |||
| 263 | .opts = &timer_pmtmr, | 271 | .opts = &timer_pmtmr, |
| 264 | }; | 272 | }; |
| 265 | 273 | ||
| 274 | #ifdef CONFIG_PCI | ||
| 275 | /* | ||
| 276 | * PIIX4 Errata: | ||
| 277 | * | ||
| 278 | * The power management timer may return improper results when read. | ||
| 279 | * Although the timer value settles properly after incrementing, | ||
| 280 | * while incrementing there is a 3 ns window every 69.8 ns where the | ||
| 281 | * timer value is indeterminate (a 4.2% chance that the data will be | ||
| 282 | * incorrect when read). As a result, the ACPI free running count up | ||
| 283 | * timer specification is violated due to erroneous reads. | ||
| 284 | */ | ||
| 285 | static int __init pmtmr_bug_check(void) | ||
| 286 | { | ||
| 287 | static struct pci_device_id gray_list[] __initdata = { | ||
| 288 | /* these chipsets may have bug. */ | ||
| 289 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, | ||
| 290 | PCI_DEVICE_ID_INTEL_82801DB_0) }, | ||
| 291 | { }, | ||
| 292 | }; | ||
| 293 | struct pci_dev *dev; | ||
| 294 | int pmtmr_has_bug = 0; | ||
| 295 | u8 rev; | ||
| 296 | |||
| 297 | if (cur_timer != &timer_pmtmr || !pmtmr_need_workaround) | ||
| 298 | return 0; | ||
| 299 | |||
| 300 | dev = pci_get_device(PCI_VENDOR_ID_INTEL, | ||
| 301 | PCI_DEVICE_ID_INTEL_82371AB_3, NULL); | ||
| 302 | if (dev) { | ||
| 303 | pci_read_config_byte(dev, PCI_REVISION_ID, &rev); | ||
| 304 | /* the bug has been fixed in PIIX4M */ | ||
| 305 | if (rev < 3) { | ||
| 306 | printk(KERN_WARNING "* Found PM-Timer Bug on this " | ||
| 307 | "chipset. Due to workarounds for a bug,\n" | ||
| 308 | "* this time source is slow. Consider trying " | ||
| 309 | "other time sources (clock=)\n"); | ||
| 310 | pmtmr_has_bug = 1; | ||
| 311 | } | ||
| 312 | pci_dev_put(dev); | ||
| 313 | } | ||
| 314 | |||
| 315 | if (pci_dev_present(gray_list)) { | ||
| 316 | printk(KERN_WARNING "* This chipset may have PM-Timer Bug. Due" | ||
| 317 | " to workarounds for a bug,\n" | ||
| 318 | "* this time source is slow. If you are sure your timer" | ||
| 319 | " does not have\n" | ||
| 320 | "* this bug, please use \"pmtmr_good\" to disable the " | ||
| 321 | "workaround\n"); | ||
| 322 | pmtmr_has_bug = 1; | ||
| 323 | } | ||
| 324 | |||
| 325 | if (!pmtmr_has_bug) | ||
| 326 | pmtmr_need_workaround = 0; | ||
| 327 | |||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | device_initcall(pmtmr_bug_check); | ||
| 331 | #endif | ||
| 332 | |||
| 333 | static int __init pmtr_good_setup(char *__str) | ||
| 334 | { | ||
| 335 | pmtmr_need_workaround = 0; | ||
| 336 | return 1; | ||
| 337 | } | ||
| 338 | __setup("pmtmr_good", pmtr_good_setup); | ||
| 339 | |||
| 266 | MODULE_LICENSE("GPL"); | 340 | MODULE_LICENSE("GPL"); |
| 267 | MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>"); | 341 | MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>"); |
| 268 | MODULE_DESCRIPTION("Power Management Timer (PMTMR) as primary timing source for x86"); | 342 | MODULE_DESCRIPTION("Power Management Timer (PMTMR) as primary timing source for x86"); |
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 4624f8ca2459..6b63a5aa1e46 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
| @@ -92,22 +92,21 @@ asmlinkage void spurious_interrupt_bug(void); | |||
| 92 | asmlinkage void machine_check(void); | 92 | asmlinkage void machine_check(void); |
| 93 | 93 | ||
| 94 | static int kstack_depth_to_print = 24; | 94 | static int kstack_depth_to_print = 24; |
| 95 | struct notifier_block *i386die_chain; | 95 | ATOMIC_NOTIFIER_HEAD(i386die_chain); |
| 96 | static DEFINE_SPINLOCK(die_notifier_lock); | ||
| 97 | 96 | ||
| 98 | int register_die_notifier(struct notifier_block *nb) | 97 | int register_die_notifier(struct notifier_block *nb) |
| 99 | { | 98 | { |
| 100 | int err = 0; | ||
| 101 | unsigned long flags; | ||
| 102 | |||
| 103 | vmalloc_sync_all(); | 99 | vmalloc_sync_all(); |
| 104 | spin_lock_irqsave(&die_notifier_lock, flags); | 100 | return atomic_notifier_chain_register(&i386die_chain, nb); |
| 105 | err = notifier_chain_register(&i386die_chain, nb); | ||
| 106 | spin_unlock_irqrestore(&die_notifier_lock, flags); | ||
| 107 | return err; | ||
| 108 | } | 101 | } |
| 109 | EXPORT_SYMBOL(register_die_notifier); | 102 | EXPORT_SYMBOL(register_die_notifier); |
| 110 | 103 | ||
| 104 | int unregister_die_notifier(struct notifier_block *nb) | ||
| 105 | { | ||
| 106 | return atomic_notifier_chain_unregister(&i386die_chain, nb); | ||
| 107 | } | ||
| 108 | EXPORT_SYMBOL(unregister_die_notifier); | ||
| 109 | |||
| 111 | static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) | 110 | static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) |
| 112 | { | 111 | { |
| 113 | return p > (void *)tinfo && | 112 | return p > (void *)tinfo && |
diff --git a/arch/i386/mm/discontig.c b/arch/i386/mm/discontig.c index c4af9638dbfa..fe6eb901326e 100644 --- a/arch/i386/mm/discontig.c +++ b/arch/i386/mm/discontig.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/nodemask.h> | 31 | #include <linux/nodemask.h> |
| 32 | #include <linux/module.h> | 32 | #include <linux/module.h> |
| 33 | #include <linux/kexec.h> | 33 | #include <linux/kexec.h> |
| 34 | #include <linux/pfn.h> | ||
| 34 | 35 | ||
| 35 | #include <asm/e820.h> | 36 | #include <asm/e820.h> |
| 36 | #include <asm/setup.h> | 37 | #include <asm/setup.h> |
| @@ -352,17 +353,6 @@ void __init zone_sizes_init(void) | |||
| 352 | { | 353 | { |
| 353 | int nid; | 354 | int nid; |
| 354 | 355 | ||
| 355 | /* | ||
| 356 | * Insert nodes into pgdat_list backward so they appear in order. | ||
| 357 | * Clobber node 0's links and NULL out pgdat_list before starting. | ||
| 358 | */ | ||
| 359 | pgdat_list = NULL; | ||
| 360 | for (nid = MAX_NUMNODES - 1; nid >= 0; nid--) { | ||
| 361 | if (!node_online(nid)) | ||
| 362 | continue; | ||
| 363 | NODE_DATA(nid)->pgdat_next = pgdat_list; | ||
| 364 | pgdat_list = NODE_DATA(nid); | ||
| 365 | } | ||
| 366 | 356 | ||
| 367 | for_each_online_node(nid) { | 357 | for_each_online_node(nid) { |
| 368 | unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; | 358 | unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; |
diff --git a/arch/i386/mm/pgtable.c b/arch/i386/mm/pgtable.c index 9db3242103be..2889567e21a1 100644 --- a/arch/i386/mm/pgtable.c +++ b/arch/i386/mm/pgtable.c | |||
| @@ -36,7 +36,7 @@ void show_mem(void) | |||
| 36 | printk(KERN_INFO "Mem-info:\n"); | 36 | printk(KERN_INFO "Mem-info:\n"); |
| 37 | show_free_areas(); | 37 | show_free_areas(); |
| 38 | printk(KERN_INFO "Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | 38 | printk(KERN_INFO "Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); |
| 39 | for_each_pgdat(pgdat) { | 39 | for_each_online_pgdat(pgdat) { |
| 40 | pgdat_resize_lock(pgdat, &flags); | 40 | pgdat_resize_lock(pgdat, &flags); |
| 41 | for (i = 0; i < pgdat->node_spanned_pages; ++i) { | 41 | for (i = 0; i < pgdat->node_spanned_pages; ++i) { |
| 42 | page = pgdat_page_nr(pgdat, i); | 42 | page = pgdat_page_nr(pgdat, i); |
diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index dabd6c32641e..7c1ddc8ac443 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c | |||
| @@ -30,19 +30,19 @@ extern spinlock_t timerlist_lock; | |||
| 30 | fpswa_interface_t *fpswa_interface; | 30 | fpswa_interface_t *fpswa_interface; |
| 31 | EXPORT_SYMBOL(fpswa_interface); | 31 | EXPORT_SYMBOL(fpswa_interface); |
| 32 | 32 | ||
| 33 | struct notifier_block *ia64die_chain; | 33 | ATOMIC_NOTIFIER_HEAD(ia64die_chain); |
| 34 | 34 | ||
| 35 | int | 35 | int |
| 36 | register_die_notifier(struct notifier_block *nb) | 36 | register_die_notifier(struct notifier_block *nb) |
| 37 | { | 37 | { |
| 38 | return notifier_chain_register(&ia64die_chain, nb); | 38 | return atomic_notifier_chain_register(&ia64die_chain, nb); |
| 39 | } | 39 | } |
| 40 | EXPORT_SYMBOL_GPL(register_die_notifier); | 40 | EXPORT_SYMBOL_GPL(register_die_notifier); |
| 41 | 41 | ||
| 42 | int | 42 | int |
| 43 | unregister_die_notifier(struct notifier_block *nb) | 43 | unregister_die_notifier(struct notifier_block *nb) |
| 44 | { | 44 | { |
| 45 | return notifier_chain_unregister(&ia64die_chain, nb); | 45 | return atomic_notifier_chain_unregister(&ia64die_chain, nb); |
| 46 | } | 46 | } |
| 47 | EXPORT_SYMBOL_GPL(unregister_die_notifier); | 47 | EXPORT_SYMBOL_GPL(unregister_die_notifier); |
| 48 | 48 | ||
diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 2f5e44862e91..ec9eeb89975d 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c | |||
| @@ -379,31 +379,6 @@ static void __init *memory_less_node_alloc(int nid, unsigned long pernodesize) | |||
| 379 | } | 379 | } |
| 380 | 380 | ||
| 381 | /** | 381 | /** |
| 382 | * pgdat_insert - insert the pgdat into global pgdat_list | ||
| 383 | * @pgdat: the pgdat for a node. | ||
| 384 | */ | ||
| 385 | static void __init pgdat_insert(pg_data_t *pgdat) | ||
| 386 | { | ||
| 387 | pg_data_t *prev = NULL, *next; | ||
| 388 | |||
| 389 | for_each_pgdat(next) | ||
| 390 | if (pgdat->node_id < next->node_id) | ||
| 391 | break; | ||
| 392 | else | ||
| 393 | prev = next; | ||
| 394 | |||
| 395 | if (prev) { | ||
| 396 | prev->pgdat_next = pgdat; | ||
| 397 | pgdat->pgdat_next = next; | ||
| 398 | } else { | ||
| 399 | pgdat->pgdat_next = pgdat_list; | ||
| 400 | pgdat_list = pgdat; | ||
| 401 | } | ||
| 402 | |||
| 403 | return; | ||
| 404 | } | ||
| 405 | |||
| 406 | /** | ||
| 407 | * memory_less_nodes - allocate and initialize CPU only nodes pernode | 382 | * memory_less_nodes - allocate and initialize CPU only nodes pernode |
| 408 | * information. | 383 | * information. |
| 409 | */ | 384 | */ |
| @@ -560,7 +535,7 @@ void show_mem(void) | |||
| 560 | printk("Mem-info:\n"); | 535 | printk("Mem-info:\n"); |
| 561 | show_free_areas(); | 536 | show_free_areas(); |
| 562 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | 537 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); |
| 563 | for_each_pgdat(pgdat) { | 538 | for_each_online_pgdat(pgdat) { |
| 564 | unsigned long present; | 539 | unsigned long present; |
| 565 | unsigned long flags; | 540 | unsigned long flags; |
| 566 | int shared = 0, cached = 0, reserved = 0; | 541 | int shared = 0, cached = 0, reserved = 0; |
| @@ -745,11 +720,5 @@ void __init paging_init(void) | |||
| 745 | pfn_offset, zholes_size); | 720 | pfn_offset, zholes_size); |
| 746 | } | 721 | } |
| 747 | 722 | ||
| 748 | /* | ||
| 749 | * Make memory less nodes become a member of the known nodes. | ||
| 750 | */ | ||
| 751 | for_each_node_mask(node, memory_less_mask) | ||
| 752 | pgdat_insert(mem_data[node].pgdat); | ||
| 753 | |||
| 754 | zero_page_memmap_ptr = virt_to_page(ia64_imva(empty_zero_page)); | 723 | zero_page_memmap_ptr = virt_to_page(ia64_imva(empty_zero_page)); |
| 755 | } | 724 | } |
diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index ff4f31fcd330..2ef1151cde90 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c | |||
| @@ -600,7 +600,7 @@ mem_init (void) | |||
| 600 | kclist_add(&kcore_vmem, (void *)VMALLOC_START, VMALLOC_END-VMALLOC_START); | 600 | kclist_add(&kcore_vmem, (void *)VMALLOC_START, VMALLOC_END-VMALLOC_START); |
| 601 | kclist_add(&kcore_kernel, _stext, _end - _stext); | 601 | kclist_add(&kcore_kernel, _stext, _end - _stext); |
| 602 | 602 | ||
| 603 | for_each_pgdat(pgdat) | 603 | for_each_online_pgdat(pgdat) |
| 604 | if (pgdat->bdata->node_bootmem_map) | 604 | if (pgdat->bdata->node_bootmem_map) |
| 605 | totalram_pages += free_all_bootmem_node(pgdat); | 605 | totalram_pages += free_all_bootmem_node(pgdat); |
| 606 | 606 | ||
diff --git a/arch/m32r/kernel/setup.c b/arch/m32r/kernel/setup.c index d742037a7ccb..0d78942b4c76 100644 --- a/arch/m32r/kernel/setup.c +++ b/arch/m32r/kernel/setup.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/tty.h> | 24 | #include <linux/tty.h> |
| 25 | #include <linux/cpu.h> | 25 | #include <linux/cpu.h> |
| 26 | #include <linux/nodemask.h> | 26 | #include <linux/nodemask.h> |
| 27 | #include <linux/pfn.h> | ||
| 27 | 28 | ||
| 28 | #include <asm/processor.h> | 29 | #include <asm/processor.h> |
| 29 | #include <asm/pgtable.h> | 30 | #include <asm/pgtable.h> |
diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index 08e727955555..cf610a7c5ff0 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/initrd.h> | 13 | #include <linux/initrd.h> |
| 14 | #include <linux/nodemask.h> | 14 | #include <linux/nodemask.h> |
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | #include <linux/pfn.h> | ||
| 16 | 17 | ||
| 17 | #include <asm/setup.h> | 18 | #include <asm/setup.h> |
| 18 | 19 | ||
| @@ -137,12 +138,6 @@ unsigned long __init zone_sizes_init(void) | |||
| 137 | int nid, i; | 138 | int nid, i; |
| 138 | mem_prof_t *mp; | 139 | mem_prof_t *mp; |
| 139 | 140 | ||
| 140 | pgdat_list = NULL; | ||
| 141 | for (nid = num_online_nodes() - 1 ; nid >= 0 ; nid--) { | ||
| 142 | NODE_DATA(nid)->pgdat_next = pgdat_list; | ||
| 143 | pgdat_list = NODE_DATA(nid); | ||
| 144 | } | ||
| 145 | |||
| 146 | for_each_online_node(nid) { | 141 | for_each_online_node(nid) { |
| 147 | mp = &mem_prof[nid]; | 142 | mp = &mem_prof[nid]; |
| 148 | for (i = 0 ; i < MAX_NR_ZONES ; i++) { | 143 | for (i = 0 ; i < MAX_NR_ZONES ; i++) { |
diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index c9e7dad860b7..b71348fec1f4 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/highmem.h> | 18 | #include <linux/highmem.h> |
| 19 | #include <linux/bitops.h> | 19 | #include <linux/bitops.h> |
| 20 | #include <linux/nodemask.h> | 20 | #include <linux/nodemask.h> |
| 21 | #include <linux/pfn.h> | ||
| 21 | #include <asm/types.h> | 22 | #include <asm/types.h> |
| 22 | #include <asm/processor.h> | 23 | #include <asm/processor.h> |
| 23 | #include <asm/page.h> | 24 | #include <asm/page.h> |
| @@ -47,7 +48,7 @@ void show_mem(void) | |||
| 47 | printk("Mem-info:\n"); | 48 | printk("Mem-info:\n"); |
| 48 | show_free_areas(); | 49 | show_free_areas(); |
| 49 | printk("Free swap: %6ldkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); | 50 | printk("Free swap: %6ldkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); |
| 50 | for_each_pgdat(pgdat) { | 51 | for_each_online_pgdat(pgdat) { |
| 51 | unsigned long flags; | 52 | unsigned long flags; |
| 52 | pgdat_resize_lock(pgdat, &flags); | 53 | pgdat_resize_lock(pgdat, &flags); |
| 53 | for (i = 0; i < pgdat->node_spanned_pages; ++i) { | 54 | for (i = 0; i < pgdat->node_spanned_pages; ++i) { |
diff --git a/arch/mips/ddb5xxx/common/rtc_ds1386.c b/arch/mips/ddb5xxx/common/rtc_ds1386.c index 995896ac0e39..5dc34daa7150 100644 --- a/arch/mips/ddb5xxx/common/rtc_ds1386.c +++ b/arch/mips/ddb5xxx/common/rtc_ds1386.c | |||
| @@ -165,6 +165,6 @@ rtc_ds1386_init(unsigned long base) | |||
| 165 | WRITE_RTC(0xB, byte); | 165 | WRITE_RTC(0xB, byte); |
| 166 | 166 | ||
| 167 | /* set the function pointers */ | 167 | /* set the function pointers */ |
| 168 | rtc_get_time = rtc_ds1386_get_time; | 168 | rtc_mips_get_time = rtc_ds1386_get_time; |
| 169 | rtc_set_time = rtc_ds1386_set_time; | 169 | rtc_mips_set_time = rtc_ds1386_set_time; |
| 170 | } | 170 | } |
diff --git a/arch/mips/dec/time.c b/arch/mips/dec/time.c index 174822344131..f17d3378e9a6 100644 --- a/arch/mips/dec/time.c +++ b/arch/mips/dec/time.c | |||
| @@ -193,8 +193,8 @@ static void dec_ioasic_hpt_init(unsigned int count) | |||
| 193 | 193 | ||
| 194 | void __init dec_time_init(void) | 194 | void __init dec_time_init(void) |
| 195 | { | 195 | { |
| 196 | rtc_get_time = dec_rtc_get_time; | 196 | rtc_mips_get_time = dec_rtc_get_time; |
| 197 | rtc_set_mmss = dec_rtc_set_mmss; | 197 | rtc_mips_set_mmss = dec_rtc_set_mmss; |
| 198 | 198 | ||
| 199 | mips_timer_state = dec_timer_state; | 199 | mips_timer_state = dec_timer_state; |
| 200 | mips_timer_ack = dec_timer_ack; | 200 | mips_timer_ack = dec_timer_ack; |
diff --git a/arch/mips/ite-boards/generic/time.c b/arch/mips/ite-boards/generic/time.c index f5d67ee21ac6..b79817bb6cce 100644 --- a/arch/mips/ite-boards/generic/time.c +++ b/arch/mips/ite-boards/generic/time.c | |||
| @@ -227,8 +227,8 @@ void __init it8172_time_init(void) | |||
| 227 | 227 | ||
| 228 | local_irq_restore(flags); | 228 | local_irq_restore(flags); |
| 229 | 229 | ||
| 230 | rtc_get_time = it8172_rtc_get_time; | 230 | rtc_mips_get_time = it8172_rtc_get_time; |
| 231 | rtc_set_time = it8172_rtc_set_time; | 231 | rtc_mips_set_time = it8172_rtc_set_time; |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) | 234 | #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) |
diff --git a/arch/mips/ite-boards/ivr/init.c b/arch/mips/ite-boards/ivr/init.c index ea4e1935fec5..b774db035b31 100644 --- a/arch/mips/ite-boards/ivr/init.c +++ b/arch/mips/ite-boards/ivr/init.c | |||
| @@ -45,9 +45,6 @@ extern void __init prom_init_cmdline(void); | |||
| 45 | extern unsigned long __init prom_get_memsize(void); | 45 | extern unsigned long __init prom_get_memsize(void); |
| 46 | extern void __init it8172_init_ram_resource(unsigned long memsize); | 46 | extern void __init it8172_init_ram_resource(unsigned long memsize); |
| 47 | 47 | ||
| 48 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 49 | #define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) | ||
| 50 | |||
| 51 | const char *get_system_type(void) | 48 | const char *get_system_type(void) |
| 52 | { | 49 | { |
| 53 | return "Globespan IVR"; | 50 | return "Globespan IVR"; |
diff --git a/arch/mips/ite-boards/qed-4n-s01b/init.c b/arch/mips/ite-boards/qed-4n-s01b/init.c index 56dca7e0c21d..e8ec8be66a80 100644 --- a/arch/mips/ite-boards/qed-4n-s01b/init.c +++ b/arch/mips/ite-boards/qed-4n-s01b/init.c | |||
| @@ -45,9 +45,6 @@ extern void __init prom_init_cmdline(void); | |||
| 45 | extern unsigned long __init prom_get_memsize(void); | 45 | extern unsigned long __init prom_get_memsize(void); |
| 46 | extern void __init it8172_init_ram_resource(unsigned long memsize); | 46 | extern void __init it8172_init_ram_resource(unsigned long memsize); |
| 47 | 47 | ||
| 48 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 49 | #define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) | ||
| 50 | |||
| 51 | const char *get_system_type(void) | 48 | const char *get_system_type(void) |
| 52 | { | 49 | { |
| 53 | return "ITE QED-4N-S01B"; | 50 | return "ITE QED-4N-S01B"; |
diff --git a/arch/mips/jmr3927/common/rtc_ds1742.c b/arch/mips/jmr3927/common/rtc_ds1742.c index 9a8bff153d80..a6bd3f4d3049 100644 --- a/arch/mips/jmr3927/common/rtc_ds1742.c +++ b/arch/mips/jmr3927/common/rtc_ds1742.c | |||
| @@ -159,8 +159,8 @@ rtc_ds1742_init(unsigned long base) | |||
| 159 | db_assert((rtc_base & 0xe0000000) == KSEG1); | 159 | db_assert((rtc_base & 0xe0000000) == KSEG1); |
| 160 | 160 | ||
| 161 | /* set the function pointers */ | 161 | /* set the function pointers */ |
| 162 | rtc_get_time = rtc_ds1742_get_time; | 162 | rtc_mips_get_time = rtc_ds1742_get_time; |
| 163 | rtc_set_time = rtc_ds1742_set_time; | 163 | rtc_mips_set_time = rtc_ds1742_set_time; |
| 164 | 164 | ||
| 165 | /* clear oscillator stop bit */ | 165 | /* clear oscillator stop bit */ |
| 166 | CMOS_WRITE(RTC_READ, RTC_CONTROL); | 166 | CMOS_WRITE(RTC_READ, RTC_CONTROL); |
diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 0cb3b6097e0e..dcbfd27071f0 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <linux/highmem.h> | 34 | #include <linux/highmem.h> |
| 35 | #include <linux/console.h> | 35 | #include <linux/console.h> |
| 36 | #include <linux/mmzone.h> | 36 | #include <linux/mmzone.h> |
| 37 | #include <linux/pfn.h> | ||
| 37 | 38 | ||
| 38 | #include <asm/addrspace.h> | 39 | #include <asm/addrspace.h> |
| 39 | #include <asm/bootinfo.h> | 40 | #include <asm/bootinfo.h> |
| @@ -257,10 +258,6 @@ static inline int parse_rd_cmdline(unsigned long* rd_start, unsigned long* rd_en | |||
| 257 | return 0; | 258 | return 0; |
| 258 | } | 259 | } |
| 259 | 260 | ||
| 260 | #define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT) | ||
| 261 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 262 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 263 | |||
| 264 | #define MAXMEM HIGHMEM_START | 261 | #define MAXMEM HIGHMEM_START |
| 265 | #define MAXMEM_PFN PFN_DOWN(MAXMEM) | 262 | #define MAXMEM_PFN PFN_DOWN(MAXMEM) |
| 266 | 263 | ||
| @@ -493,10 +490,6 @@ static inline void resource_init(void) | |||
| 493 | } | 490 | } |
| 494 | } | 491 | } |
| 495 | 492 | ||
| 496 | #undef PFN_UP | ||
| 497 | #undef PFN_DOWN | ||
| 498 | #undef PFN_PHYS | ||
| 499 | |||
| 500 | #undef MAXMEM | 493 | #undef MAXMEM |
| 501 | #undef MAXMEM_PFN | 494 | #undef MAXMEM_PFN |
| 502 | 495 | ||
diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index 51273b7297a7..5e51a2d8f3f0 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c | |||
| @@ -65,9 +65,9 @@ static int null_rtc_set_time(unsigned long sec) | |||
| 65 | return 0; | 65 | return 0; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | unsigned long (*rtc_get_time)(void) = null_rtc_get_time; | 68 | unsigned long (*rtc_mips_get_time)(void) = null_rtc_get_time; |
| 69 | int (*rtc_set_time)(unsigned long) = null_rtc_set_time; | 69 | int (*rtc_mips_set_time)(unsigned long) = null_rtc_set_time; |
| 70 | int (*rtc_set_mmss)(unsigned long); | 70 | int (*rtc_mips_set_mmss)(unsigned long); |
| 71 | 71 | ||
| 72 | 72 | ||
| 73 | /* usecs per counter cycle, shifted to left by 32 bits */ | 73 | /* usecs per counter cycle, shifted to left by 32 bits */ |
| @@ -440,14 +440,14 @@ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
| 440 | 440 | ||
| 441 | /* | 441 | /* |
| 442 | * If we have an externally synchronized Linux clock, then update | 442 | * If we have an externally synchronized Linux clock, then update |
| 443 | * CMOS clock accordingly every ~11 minutes. rtc_set_time() has to be | 443 | * CMOS clock accordingly every ~11 minutes. rtc_mips_set_time() has to be |
| 444 | * called as close as possible to 500 ms before the new second starts. | 444 | * called as close as possible to 500 ms before the new second starts. |
| 445 | */ | 445 | */ |
| 446 | if (ntp_synced() && | 446 | if (ntp_synced() && |
| 447 | xtime.tv_sec > last_rtc_update + 660 && | 447 | xtime.tv_sec > last_rtc_update + 660 && |
| 448 | (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && | 448 | (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && |
| 449 | (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { | 449 | (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { |
| 450 | if (rtc_set_mmss(xtime.tv_sec) == 0) { | 450 | if (rtc_mips_set_mmss(xtime.tv_sec) == 0) { |
| 451 | last_rtc_update = xtime.tv_sec; | 451 | last_rtc_update = xtime.tv_sec; |
| 452 | } else { | 452 | } else { |
| 453 | /* do it again in 60 s */ | 453 | /* do it again in 60 s */ |
| @@ -565,7 +565,7 @@ asmlinkage void ll_local_timer_interrupt(int irq, struct pt_regs *regs) | |||
| 565 | * b) (optional) calibrate and set the mips_hpt_frequency | 565 | * b) (optional) calibrate and set the mips_hpt_frequency |
| 566 | * (only needed if you intended to use fixed_rate_gettimeoffset | 566 | * (only needed if you intended to use fixed_rate_gettimeoffset |
| 567 | * or use cpu counter as timer interrupt source) | 567 | * or use cpu counter as timer interrupt source) |
| 568 | * 2) setup xtime based on rtc_get_time(). | 568 | * 2) setup xtime based on rtc_mips_get_time(). |
| 569 | * 3) choose a appropriate gettimeoffset routine. | 569 | * 3) choose a appropriate gettimeoffset routine. |
| 570 | * 4) calculate a couple of cached variables for later usage | 570 | * 4) calculate a couple of cached variables for later usage |
| 571 | * 5) board_timer_setup() - | 571 | * 5) board_timer_setup() - |
| @@ -633,10 +633,10 @@ void __init time_init(void) | |||
| 633 | if (board_time_init) | 633 | if (board_time_init) |
| 634 | board_time_init(); | 634 | board_time_init(); |
| 635 | 635 | ||
| 636 | if (!rtc_set_mmss) | 636 | if (!rtc_mips_set_mmss) |
| 637 | rtc_set_mmss = rtc_set_time; | 637 | rtc_mips_set_mmss = rtc_mips_set_time; |
| 638 | 638 | ||
| 639 | xtime.tv_sec = rtc_get_time(); | 639 | xtime.tv_sec = rtc_mips_get_time(); |
| 640 | xtime.tv_nsec = 0; | 640 | xtime.tv_nsec = 0; |
| 641 | 641 | ||
| 642 | set_normalized_timespec(&wall_to_monotonic, | 642 | set_normalized_timespec(&wall_to_monotonic, |
| @@ -772,8 +772,8 @@ void to_tm(unsigned long tim, struct rtc_time *tm) | |||
| 772 | 772 | ||
| 773 | EXPORT_SYMBOL(rtc_lock); | 773 | EXPORT_SYMBOL(rtc_lock); |
| 774 | EXPORT_SYMBOL(to_tm); | 774 | EXPORT_SYMBOL(to_tm); |
| 775 | EXPORT_SYMBOL(rtc_set_time); | 775 | EXPORT_SYMBOL(rtc_mips_set_time); |
| 776 | EXPORT_SYMBOL(rtc_get_time); | 776 | EXPORT_SYMBOL(rtc_mips_get_time); |
| 777 | 777 | ||
| 778 | unsigned long long sched_clock(void) | 778 | unsigned long long sched_clock(void) |
| 779 | { | 779 | { |
diff --git a/arch/mips/lasat/setup.c b/arch/mips/lasat/setup.c index 83eb08b7a072..bb70a8240e61 100644 --- a/arch/mips/lasat/setup.c +++ b/arch/mips/lasat/setup.c | |||
| @@ -165,7 +165,8 @@ void __init plat_setup(void) | |||
| 165 | 165 | ||
| 166 | /* Set up panic notifier */ | 166 | /* Set up panic notifier */ |
| 167 | for (i = 0; i < sizeof(lasat_panic_block) / sizeof(struct notifier_block); i++) | 167 | for (i = 0; i < sizeof(lasat_panic_block) / sizeof(struct notifier_block); i++) |
| 168 | notifier_chain_register(&panic_notifier_list, &lasat_panic_block[i]); | 168 | atomic_notifier_chain_register(&panic_notifier_list, |
| 169 | &lasat_panic_block[i]); | ||
| 169 | 170 | ||
| 170 | lasat_reboot_setup(); | 171 | lasat_reboot_setup(); |
| 171 | 172 | ||
| @@ -174,8 +175,8 @@ void __init plat_setup(void) | |||
| 174 | 175 | ||
| 175 | #ifdef CONFIG_DS1603 | 176 | #ifdef CONFIG_DS1603 |
| 176 | ds1603 = &ds_defs[mips_machtype]; | 177 | ds1603 = &ds_defs[mips_machtype]; |
| 177 | rtc_get_time = ds1603_read; | 178 | rtc_mips_get_time = ds1603_read; |
| 178 | rtc_set_time = ds1603_set; | 179 | rtc_mips_set_time = ds1603_set; |
| 179 | #endif | 180 | #endif |
| 180 | 181 | ||
| 181 | #ifdef DYNAMIC_SERIAL_INIT | 182 | #ifdef DYNAMIC_SERIAL_INIT |
diff --git a/arch/mips/mips-boards/atlas/atlas_setup.c b/arch/mips/mips-boards/atlas/atlas_setup.c index 873cf3141a31..c20d401ecf80 100644 --- a/arch/mips/mips-boards/atlas/atlas_setup.c +++ b/arch/mips/mips-boards/atlas/atlas_setup.c | |||
| @@ -65,7 +65,7 @@ void __init plat_setup(void) | |||
| 65 | 65 | ||
| 66 | board_time_init = mips_time_init; | 66 | board_time_init = mips_time_init; |
| 67 | board_timer_setup = mips_timer_setup; | 67 | board_timer_setup = mips_timer_setup; |
| 68 | rtc_get_time = mips_rtc_get_time; | 68 | rtc_mips_get_time = mips_rtc_get_time; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | static void __init serial_init(void) | 71 | static void __init serial_init(void) |
diff --git a/arch/mips/mips-boards/generic/memory.c b/arch/mips/mips-boards/generic/memory.c index ee5e70c95cf3..32c9210373ac 100644 --- a/arch/mips/mips-boards/generic/memory.c +++ b/arch/mips/mips-boards/generic/memory.c | |||
| @@ -49,9 +49,6 @@ static char *mtypes[3] = { | |||
| 49 | /* References to section boundaries */ | 49 | /* References to section boundaries */ |
| 50 | extern char _end; | 50 | extern char _end; |
| 51 | 51 | ||
| 52 | #define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) | ||
| 53 | |||
| 54 | |||
| 55 | struct prom_pmemblock * __init prom_getmdesc(void) | 52 | struct prom_pmemblock * __init prom_getmdesc(void) |
| 56 | { | 53 | { |
| 57 | char *memsize_str; | 54 | char *memsize_str; |
| @@ -109,10 +106,10 @@ struct prom_pmemblock * __init prom_getmdesc(void) | |||
| 109 | 106 | ||
| 110 | mdesc[3].type = yamon_dontuse; | 107 | mdesc[3].type = yamon_dontuse; |
| 111 | mdesc[3].base = 0x00100000; | 108 | mdesc[3].base = 0x00100000; |
| 112 | mdesc[3].size = CPHYSADDR(PFN_ALIGN(&_end)) - mdesc[3].base; | 109 | mdesc[3].size = CPHYSADDR(PAGE_ALIGN(&_end)) - mdesc[3].base; |
| 113 | 110 | ||
| 114 | mdesc[4].type = yamon_free; | 111 | mdesc[4].type = yamon_free; |
| 115 | mdesc[4].base = CPHYSADDR(PFN_ALIGN(&_end)); | 112 | mdesc[4].base = CPHYSADDR(PAGE_ALIGN(&_end)); |
| 116 | mdesc[4].size = memsize - mdesc[4].base; | 113 | mdesc[4].size = memsize - mdesc[4].base; |
| 117 | 114 | ||
| 118 | return &mdesc[0]; | 115 | return &mdesc[0]; |
diff --git a/arch/mips/mips-boards/malta/malta_setup.c b/arch/mips/mips-boards/malta/malta_setup.c index 2209e8a9de34..b8488aab6df1 100644 --- a/arch/mips/mips-boards/malta/malta_setup.c +++ b/arch/mips/mips-boards/malta/malta_setup.c | |||
| @@ -225,5 +225,5 @@ void __init plat_setup(void) | |||
| 225 | 225 | ||
| 226 | board_time_init = mips_time_init; | 226 | board_time_init = mips_time_init; |
| 227 | board_timer_setup = mips_timer_setup; | 227 | board_timer_setup = mips_timer_setup; |
| 228 | rtc_get_time = mips_rtc_get_time; | 228 | rtc_mips_get_time = mips_rtc_get_time; |
| 229 | } | 229 | } |
diff --git a/arch/mips/mips-boards/sim/sim_mem.c b/arch/mips/mips-boards/sim/sim_mem.c index 1ec4e75656bd..e57f737bab10 100644 --- a/arch/mips/mips-boards/sim/sim_mem.c +++ b/arch/mips/mips-boards/sim/sim_mem.c | |||
| @@ -42,9 +42,6 @@ static char *mtypes[3] = { | |||
| 42 | /* References to section boundaries */ | 42 | /* References to section boundaries */ |
| 43 | extern char _end; | 43 | extern char _end; |
| 44 | 44 | ||
| 45 | #define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) | ||
| 46 | |||
| 47 | |||
| 48 | struct prom_pmemblock * __init prom_getmdesc(void) | 45 | struct prom_pmemblock * __init prom_getmdesc(void) |
| 49 | { | 46 | { |
| 50 | unsigned int memsize; | 47 | unsigned int memsize; |
| @@ -64,10 +61,10 @@ struct prom_pmemblock * __init prom_getmdesc(void) | |||
| 64 | 61 | ||
| 65 | mdesc[2].type = simmem_reserved; | 62 | mdesc[2].type = simmem_reserved; |
| 66 | mdesc[2].base = 0x00100000; | 63 | mdesc[2].base = 0x00100000; |
| 67 | mdesc[2].size = CPHYSADDR(PFN_ALIGN(&_end)) - mdesc[2].base; | 64 | mdesc[2].size = CPHYSADDR(PAGE_ALIGN(&_end)) - mdesc[2].base; |
| 68 | 65 | ||
| 69 | mdesc[3].type = simmem_free; | 66 | mdesc[3].type = simmem_free; |
| 70 | mdesc[3].base = CPHYSADDR(PFN_ALIGN(&_end)); | 67 | mdesc[3].base = CPHYSADDR(PAGE_ALIGN(&_end)); |
| 71 | mdesc[3].size = memsize - mdesc[3].base; | 68 | mdesc[3].size = memsize - mdesc[3].base; |
| 72 | 69 | ||
| 73 | return &mdesc[0]; | 70 | return &mdesc[0]; |
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 52f7d59fe612..ad89c442f299 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/highmem.h> | 25 | #include <linux/highmem.h> |
| 26 | #include <linux/swap.h> | 26 | #include <linux/swap.h> |
| 27 | #include <linux/proc_fs.h> | 27 | #include <linux/proc_fs.h> |
| 28 | #include <linux/pfn.h> | ||
| 28 | 29 | ||
| 29 | #include <asm/bootinfo.h> | 30 | #include <asm/bootinfo.h> |
| 30 | #include <asm/cachectl.h> | 31 | #include <asm/cachectl.h> |
| @@ -177,9 +178,6 @@ void __init paging_init(void) | |||
| 177 | free_area_init(zones_size); | 178 | free_area_init(zones_size); |
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | #define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT) | ||
| 181 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 182 | |||
| 183 | static inline int page_is_ram(unsigned long pagenr) | 181 | static inline int page_is_ram(unsigned long pagenr) |
| 184 | { | 182 | { |
| 185 | int i; | 183 | int i; |
diff --git a/arch/mips/momentum/jaguar_atx/setup.c b/arch/mips/momentum/jaguar_atx/setup.c index 3784c898db1a..91d9637143d7 100644 --- a/arch/mips/momentum/jaguar_atx/setup.c +++ b/arch/mips/momentum/jaguar_atx/setup.c | |||
| @@ -229,8 +229,8 @@ void momenco_time_init(void) | |||
| 229 | mips_hpt_frequency = cpu_clock / 2; | 229 | mips_hpt_frequency = cpu_clock / 2; |
| 230 | board_timer_setup = momenco_timer_setup; | 230 | board_timer_setup = momenco_timer_setup; |
| 231 | 231 | ||
| 232 | rtc_get_time = m48t37y_get_time; | 232 | rtc_mips_get_time = m48t37y_get_time; |
| 233 | rtc_set_time = m48t37y_set_time; | 233 | rtc_mips_set_time = m48t37y_set_time; |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | static struct resource mv_pci_io_mem0_resource = { | 236 | static struct resource mv_pci_io_mem0_resource = { |
diff --git a/arch/mips/momentum/ocelot_3/setup.c b/arch/mips/momentum/ocelot_3/setup.c index f95677f4f06f..969612eab8a6 100644 --- a/arch/mips/momentum/ocelot_3/setup.c +++ b/arch/mips/momentum/ocelot_3/setup.c | |||
| @@ -215,8 +215,8 @@ void momenco_time_init(void) | |||
| 215 | mips_hpt_frequency = cpu_clock / 2; | 215 | mips_hpt_frequency = cpu_clock / 2; |
| 216 | board_timer_setup = momenco_timer_setup; | 216 | board_timer_setup = momenco_timer_setup; |
| 217 | 217 | ||
| 218 | rtc_get_time = m48t37y_get_time; | 218 | rtc_mips_get_time = m48t37y_get_time; |
| 219 | rtc_set_time = m48t37y_set_time; | 219 | rtc_mips_set_time = m48t37y_set_time; |
| 220 | } | 220 | } |
| 221 | 221 | ||
| 222 | /* | 222 | /* |
diff --git a/arch/mips/momentum/ocelot_c/setup.c b/arch/mips/momentum/ocelot_c/setup.c index bd02e60d037a..a3e6f5575592 100644 --- a/arch/mips/momentum/ocelot_c/setup.c +++ b/arch/mips/momentum/ocelot_c/setup.c | |||
| @@ -227,8 +227,8 @@ void momenco_time_init(void) | |||
| 227 | printk("momenco_time_init cpu_clock=%d\n", cpu_clock); | 227 | printk("momenco_time_init cpu_clock=%d\n", cpu_clock); |
| 228 | board_timer_setup = momenco_timer_setup; | 228 | board_timer_setup = momenco_timer_setup; |
| 229 | 229 | ||
| 230 | rtc_get_time = m48t37y_get_time; | 230 | rtc_mips_get_time = m48t37y_get_time; |
| 231 | rtc_set_time = m48t37y_set_time; | 231 | rtc_mips_set_time = m48t37y_set_time; |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | void __init plat_setup(void) | 234 | void __init plat_setup(void) |
diff --git a/arch/mips/pmc-sierra/yosemite/setup.c b/arch/mips/pmc-sierra/yosemite/setup.c index 8bce711575de..3f724d661bdb 100644 --- a/arch/mips/pmc-sierra/yosemite/setup.c +++ b/arch/mips/pmc-sierra/yosemite/setup.c | |||
| @@ -198,8 +198,8 @@ static void __init py_rtc_setup(void) | |||
| 198 | if (!m48t37_base) | 198 | if (!m48t37_base) |
| 199 | printk(KERN_ERR "Mapping the RTC failed\n"); | 199 | printk(KERN_ERR "Mapping the RTC failed\n"); |
| 200 | 200 | ||
| 201 | rtc_get_time = m48t37y_get_time; | 201 | rtc_mips_get_time = m48t37y_get_time; |
| 202 | rtc_set_time = m48t37y_set_time; | 202 | rtc_mips_set_time = m48t37y_set_time; |
| 203 | 203 | ||
| 204 | write_seqlock(&xtime_lock); | 204 | write_seqlock(&xtime_lock); |
| 205 | xtime.tv_sec = m48t37y_get_time(); | 205 | xtime.tv_sec = m48t37y_get_time(); |
diff --git a/arch/mips/sgi-ip22/ip22-reset.c b/arch/mips/sgi-ip22/ip22-reset.c index 92a3b3c15ed3..a9c58e067b53 100644 --- a/arch/mips/sgi-ip22/ip22-reset.c +++ b/arch/mips/sgi-ip22/ip22-reset.c | |||
| @@ -238,7 +238,7 @@ static int __init reboot_setup(void) | |||
| 238 | request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL); | 238 | request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL); |
| 239 | init_timer(&blink_timer); | 239 | init_timer(&blink_timer); |
| 240 | blink_timer.function = blink_timeout; | 240 | blink_timer.function = blink_timeout; |
| 241 | notifier_chain_register(&panic_notifier_list, &panic_block); | 241 | atomic_notifier_chain_register(&panic_notifier_list, &panic_block); |
| 242 | 242 | ||
| 243 | return 0; | 243 | return 0; |
| 244 | } | 244 | } |
diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c index b7300cc5c5ad..cca688ad64ad 100644 --- a/arch/mips/sgi-ip22/ip22-time.c +++ b/arch/mips/sgi-ip22/ip22-time.c | |||
| @@ -212,8 +212,8 @@ static void indy_timer_setup(struct irqaction *irq) | |||
| 212 | void __init ip22_time_init(void) | 212 | void __init ip22_time_init(void) |
| 213 | { | 213 | { |
| 214 | /* setup hookup functions */ | 214 | /* setup hookup functions */ |
| 215 | rtc_get_time = indy_rtc_get_time; | 215 | rtc_mips_get_time = indy_rtc_get_time; |
| 216 | rtc_set_time = indy_rtc_set_time; | 216 | rtc_mips_set_time = indy_rtc_set_time; |
| 217 | 217 | ||
| 218 | board_time_init = indy_time_init; | 218 | board_time_init = indy_time_init; |
| 219 | board_timer_setup = indy_timer_setup; | 219 | board_timer_setup = indy_timer_setup; |
diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index e0d095daa5ed..6c00dce9f73f 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/nodemask.h> | 19 | #include <linux/nodemask.h> |
| 20 | #include <linux/swap.h> | 20 | #include <linux/swap.h> |
| 21 | #include <linux/bootmem.h> | 21 | #include <linux/bootmem.h> |
| 22 | #include <linux/pfn.h> | ||
| 22 | #include <asm/page.h> | 23 | #include <asm/page.h> |
| 23 | #include <asm/sections.h> | 24 | #include <asm/sections.h> |
| 24 | 25 | ||
| @@ -28,8 +29,6 @@ | |||
| 28 | #include <asm/sn/sn_private.h> | 29 | #include <asm/sn/sn_private.h> |
| 29 | 30 | ||
| 30 | 31 | ||
| 31 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 32 | |||
| 33 | #define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT) | 32 | #define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT) |
| 34 | #define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT) | 33 | #define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT) |
| 35 | 34 | ||
diff --git a/arch/mips/sgi-ip32/ip32-reset.c b/arch/mips/sgi-ip32/ip32-reset.c index 0c948008b023..ab9d9cef089e 100644 --- a/arch/mips/sgi-ip32/ip32-reset.c +++ b/arch/mips/sgi-ip32/ip32-reset.c | |||
| @@ -193,7 +193,7 @@ static __init int ip32_reboot_setup(void) | |||
| 193 | 193 | ||
| 194 | init_timer(&blink_timer); | 194 | init_timer(&blink_timer); |
| 195 | blink_timer.function = blink_timeout; | 195 | blink_timer.function = blink_timeout; |
| 196 | notifier_chain_register(&panic_notifier_list, &panic_block); | 196 | atomic_notifier_chain_register(&panic_notifier_list, &panic_block); |
| 197 | 197 | ||
| 198 | request_irq(MACEISA_RTC_IRQ, ip32_rtc_int, 0, "rtc", NULL); | 198 | request_irq(MACEISA_RTC_IRQ, ip32_rtc_int, 0, "rtc", NULL); |
| 199 | 199 | ||
diff --git a/arch/mips/sgi-ip32/ip32-setup.c b/arch/mips/sgi-ip32/ip32-setup.c index 2f50c79b7887..a2dd8ae1ea8f 100644 --- a/arch/mips/sgi-ip32/ip32-setup.c +++ b/arch/mips/sgi-ip32/ip32-setup.c | |||
| @@ -91,8 +91,8 @@ void __init plat_setup(void) | |||
| 91 | { | 91 | { |
| 92 | board_be_init = ip32_be_init; | 92 | board_be_init = ip32_be_init; |
| 93 | 93 | ||
| 94 | rtc_get_time = mc146818_get_cmos_time; | 94 | rtc_mips_get_time = mc146818_get_cmos_time; |
| 95 | rtc_set_mmss = mc146818_set_rtc_mmss; | 95 | rtc_mips_set_mmss = mc146818_set_rtc_mmss; |
| 96 | 96 | ||
| 97 | board_time_init = ip32_time_init; | 97 | board_time_init = ip32_time_init; |
| 98 | board_timer_setup = ip32_timer_setup; | 98 | board_timer_setup = ip32_timer_setup; |
diff --git a/arch/mips/sibyte/swarm/setup.c b/arch/mips/sibyte/swarm/setup.c index b661d2425a36..4b5f74ff3edd 100644 --- a/arch/mips/sibyte/swarm/setup.c +++ b/arch/mips/sibyte/swarm/setup.c | |||
| @@ -121,14 +121,14 @@ void __init plat_setup(void) | |||
| 121 | 121 | ||
| 122 | if (xicor_probe()) { | 122 | if (xicor_probe()) { |
| 123 | printk("swarm setup: Xicor 1241 RTC detected.\n"); | 123 | printk("swarm setup: Xicor 1241 RTC detected.\n"); |
| 124 | rtc_get_time = xicor_get_time; | 124 | rtc_mips_get_time = xicor_get_time; |
| 125 | rtc_set_time = xicor_set_time; | 125 | rtc_mips_set_time = xicor_set_time; |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | if (m41t81_probe()) { | 128 | if (m41t81_probe()) { |
| 129 | printk("swarm setup: M41T81 RTC detected.\n"); | 129 | printk("swarm setup: M41T81 RTC detected.\n"); |
| 130 | rtc_get_time = m41t81_get_time; | 130 | rtc_mips_get_time = m41t81_get_time; |
| 131 | rtc_set_time = m41t81_set_time; | 131 | rtc_mips_set_time = m41t81_set_time; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | printk("This kernel optimized for " | 134 | printk("This kernel optimized for " |
diff --git a/arch/mips/sni/setup.c b/arch/mips/sni/setup.c index 1141fcd13a59..01ba6c581e3d 100644 --- a/arch/mips/sni/setup.c +++ b/arch/mips/sni/setup.c | |||
| @@ -164,8 +164,8 @@ static struct pci_controller sni_controller = { | |||
| 164 | 164 | ||
| 165 | static inline void sni_pcimt_time_init(void) | 165 | static inline void sni_pcimt_time_init(void) |
| 166 | { | 166 | { |
| 167 | rtc_get_time = mc146818_get_cmos_time; | 167 | rtc_mips_get_time = mc146818_get_cmos_time; |
| 168 | rtc_set_time = mc146818_set_rtc_mmss; | 168 | rtc_mips_set_time = mc146818_set_rtc_mmss; |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | void __init plat_setup(void) | 171 | void __init plat_setup(void) |
diff --git a/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c b/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c index 2ad6401d2af4..6dcf077f61a0 100644 --- a/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c +++ b/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c | |||
| @@ -1036,8 +1036,8 @@ toshiba_rbtx4927_time_init(void) | |||
| 1036 | 1036 | ||
| 1037 | #ifdef CONFIG_RTC_DS1742 | 1037 | #ifdef CONFIG_RTC_DS1742 |
| 1038 | 1038 | ||
| 1039 | rtc_get_time = rtc_ds1742_get_time; | 1039 | rtc_mips_get_time = rtc_ds1742_get_time; |
| 1040 | rtc_set_time = rtc_ds1742_set_time; | 1040 | rtc_mips_set_time = rtc_ds1742_set_time; |
| 1041 | 1041 | ||
| 1042 | TOSHIBA_RBTX4927_SETUP_DPRINTK(TOSHIBA_RBTX4927_SETUP_TIME_INIT, | 1042 | TOSHIBA_RBTX4927_SETUP_DPRINTK(TOSHIBA_RBTX4927_SETUP_TIME_INIT, |
| 1043 | ":rtc_ds1742_init()-\n"); | 1043 | ":rtc_ds1742_init()-\n"); |
diff --git a/arch/mips/tx4938/common/rtc_rx5c348.c b/arch/mips/tx4938/common/rtc_rx5c348.c index d249edbb6af4..f74295f28527 100644 --- a/arch/mips/tx4938/common/rtc_rx5c348.c +++ b/arch/mips/tx4938/common/rtc_rx5c348.c | |||
| @@ -197,6 +197,6 @@ rtc_rx5c348_init(int chipid) | |||
| 197 | srtc_24h = 1; | 197 | srtc_24h = 1; |
| 198 | 198 | ||
| 199 | /* set the function pointers */ | 199 | /* set the function pointers */ |
| 200 | rtc_get_time = rtc_rx5c348_get_time; | 200 | rtc_mips_get_time = rtc_rx5c348_get_time; |
| 201 | rtc_set_time = rtc_rx5c348_set_time; | 201 | rtc_mips_set_time = rtc_rx5c348_set_time; |
| 202 | } | 202 | } |
diff --git a/arch/parisc/kernel/pdc_chassis.c b/arch/parisc/kernel/pdc_chassis.c index 2a01fe1bdc98..0cea6958f427 100644 --- a/arch/parisc/kernel/pdc_chassis.c +++ b/arch/parisc/kernel/pdc_chassis.c | |||
| @@ -150,7 +150,8 @@ void __init parisc_pdc_chassis_init(void) | |||
| 150 | 150 | ||
| 151 | if (handle) { | 151 | if (handle) { |
| 152 | /* initialize panic notifier chain */ | 152 | /* initialize panic notifier chain */ |
| 153 | notifier_chain_register(&panic_notifier_list, &pdc_chassis_panic_block); | 153 | atomic_notifier_chain_register(&panic_notifier_list, |
| 154 | &pdc_chassis_panic_block); | ||
| 154 | 155 | ||
| 155 | /* initialize reboot notifier chain */ | 156 | /* initialize reboot notifier chain */ |
| 156 | register_reboot_notifier(&pdc_chassis_reboot_block); | 157 | register_reboot_notifier(&pdc_chassis_reboot_block); |
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 2f3fdad35594..e20c1fae3423 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c | |||
| @@ -579,7 +579,8 @@ void __init setup_arch(char **cmdline_p) | |||
| 579 | panic_timeout = 180; | 579 | panic_timeout = 180; |
| 580 | 580 | ||
| 581 | if (ppc_md.panic) | 581 | if (ppc_md.panic) |
| 582 | notifier_chain_register(&panic_notifier_list, &ppc64_panic_block); | 582 | atomic_notifier_chain_register(&panic_notifier_list, |
| 583 | &ppc64_panic_block); | ||
| 583 | 584 | ||
| 584 | init_mm.start_code = PAGE_OFFSET; | 585 | init_mm.start_code = PAGE_OFFSET; |
| 585 | init_mm.end_code = (unsigned long) _etext; | 586 | init_mm.end_code = (unsigned long) _etext; |
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 98660aedeeb7..9763faab6739 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
| @@ -74,19 +74,19 @@ EXPORT_SYMBOL(__debugger_dabr_match); | |||
| 74 | EXPORT_SYMBOL(__debugger_fault_handler); | 74 | EXPORT_SYMBOL(__debugger_fault_handler); |
| 75 | #endif | 75 | #endif |
| 76 | 76 | ||
| 77 | struct notifier_block *powerpc_die_chain; | 77 | ATOMIC_NOTIFIER_HEAD(powerpc_die_chain); |
| 78 | static DEFINE_SPINLOCK(die_notifier_lock); | ||
| 79 | 78 | ||
| 80 | int register_die_notifier(struct notifier_block *nb) | 79 | int register_die_notifier(struct notifier_block *nb) |
| 81 | { | 80 | { |
| 82 | int err = 0; | 81 | return atomic_notifier_chain_register(&powerpc_die_chain, nb); |
| 83 | unsigned long flags; | 82 | } |
| 83 | EXPORT_SYMBOL(register_die_notifier); | ||
| 84 | 84 | ||
| 85 | spin_lock_irqsave(&die_notifier_lock, flags); | 85 | int unregister_die_notifier(struct notifier_block *nb) |
| 86 | err = notifier_chain_register(&powerpc_die_chain, nb); | 86 | { |
| 87 | spin_unlock_irqrestore(&die_notifier_lock, flags); | 87 | return atomic_notifier_chain_unregister(&powerpc_die_chain, nb); |
| 88 | return err; | ||
| 89 | } | 88 | } |
| 89 | EXPORT_SYMBOL(unregister_die_notifier); | ||
| 90 | 90 | ||
| 91 | /* | 91 | /* |
| 92 | * Trap & Exception support | 92 | * Trap & Exception support |
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index badac10d700c..5e435a9c3431 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c | |||
| @@ -195,7 +195,7 @@ void show_mem(void) | |||
| 195 | printk("Mem-info:\n"); | 195 | printk("Mem-info:\n"); |
| 196 | show_free_areas(); | 196 | show_free_areas(); |
| 197 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | 197 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); |
| 198 | for_each_pgdat(pgdat) { | 198 | for_each_online_pgdat(pgdat) { |
| 199 | unsigned long flags; | 199 | unsigned long flags; |
| 200 | pgdat_resize_lock(pgdat, &flags); | 200 | pgdat_resize_lock(pgdat, &flags); |
| 201 | for (i = 0; i < pgdat->node_spanned_pages; i++) { | 201 | for (i = 0; i < pgdat->node_spanned_pages; i++) { |
| @@ -351,7 +351,7 @@ void __init mem_init(void) | |||
| 351 | max_mapnr = max_pfn; | 351 | max_mapnr = max_pfn; |
| 352 | totalram_pages += free_all_bootmem(); | 352 | totalram_pages += free_all_bootmem(); |
| 353 | #endif | 353 | #endif |
| 354 | for_each_pgdat(pgdat) { | 354 | for_each_online_pgdat(pgdat) { |
| 355 | for (i = 0; i < pgdat->node_spanned_pages; i++) { | 355 | for (i = 0; i < pgdat->node_spanned_pages; i++) { |
| 356 | if (!pfn_valid(pgdat->node_start_pfn + i)) | 356 | if (!pfn_valid(pgdat->node_start_pfn + i)) |
| 357 | continue; | 357 | continue; |
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 86cfa6ecdcf3..5ad90676567a 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c | |||
| @@ -94,16 +94,16 @@ static struct device_node *derive_parent(const char *path) | |||
| 94 | return parent; | 94 | return parent; |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | static struct notifier_block *pSeries_reconfig_chain; | 97 | static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain); |
| 98 | 98 | ||
| 99 | int pSeries_reconfig_notifier_register(struct notifier_block *nb) | 99 | int pSeries_reconfig_notifier_register(struct notifier_block *nb) |
| 100 | { | 100 | { |
| 101 | return notifier_chain_register(&pSeries_reconfig_chain, nb); | 101 | return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) | 104 | void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) |
| 105 | { | 105 | { |
| 106 | notifier_chain_unregister(&pSeries_reconfig_chain, nb); | 106 | blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | static int pSeries_reconfig_add_node(const char *path, struct property *proplist) | 109 | static int pSeries_reconfig_add_node(const char *path, struct property *proplist) |
| @@ -131,7 +131,7 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist | |||
| 131 | goto out_err; | 131 | goto out_err; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | err = notifier_call_chain(&pSeries_reconfig_chain, | 134 | err = blocking_notifier_call_chain(&pSeries_reconfig_chain, |
| 135 | PSERIES_RECONFIG_ADD, np); | 135 | PSERIES_RECONFIG_ADD, np); |
| 136 | if (err == NOTIFY_BAD) { | 136 | if (err == NOTIFY_BAD) { |
| 137 | printk(KERN_ERR "Failed to add device node %s\n", path); | 137 | printk(KERN_ERR "Failed to add device node %s\n", path); |
| @@ -171,7 +171,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np) | |||
| 171 | 171 | ||
| 172 | remove_node_proc_entries(np); | 172 | remove_node_proc_entries(np); |
| 173 | 173 | ||
| 174 | notifier_call_chain(&pSeries_reconfig_chain, | 174 | blocking_notifier_call_chain(&pSeries_reconfig_chain, |
| 175 | PSERIES_RECONFIG_REMOVE, np); | 175 | PSERIES_RECONFIG_REMOVE, np); |
| 176 | of_detach_node(np); | 176 | of_detach_node(np); |
| 177 | 177 | ||
diff --git a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c index a0fc628ffb1e..d95c05d9824d 100644 --- a/arch/ppc/platforms/prep_setup.c +++ b/arch/ppc/platforms/prep_setup.c | |||
| @@ -736,7 +736,7 @@ ibm_statusled_progress(char *s, unsigned short hex) | |||
| 736 | hex = 0xfff; | 736 | hex = 0xfff; |
| 737 | if (!notifier_installed) { | 737 | if (!notifier_installed) { |
| 738 | ++notifier_installed; | 738 | ++notifier_installed; |
| 739 | notifier_chain_register(&panic_notifier_list, | 739 | atomic_notifier_chain_register(&panic_notifier_list, |
| 740 | &ibm_statusled_block); | 740 | &ibm_statusled_block); |
| 741 | } | 741 | } |
| 742 | } | 742 | } |
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 99182a415fe7..4a0f5a1551ea 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c | |||
| @@ -76,17 +76,17 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
| 76 | /* | 76 | /* |
| 77 | * Need to know about CPUs going idle? | 77 | * Need to know about CPUs going idle? |
| 78 | */ | 78 | */ |
| 79 | static struct notifier_block *idle_chain; | 79 | static ATOMIC_NOTIFIER_HEAD(idle_chain); |
| 80 | 80 | ||
| 81 | int register_idle_notifier(struct notifier_block *nb) | 81 | int register_idle_notifier(struct notifier_block *nb) |
| 82 | { | 82 | { |
| 83 | return notifier_chain_register(&idle_chain, nb); | 83 | return atomic_notifier_chain_register(&idle_chain, nb); |
| 84 | } | 84 | } |
| 85 | EXPORT_SYMBOL(register_idle_notifier); | 85 | EXPORT_SYMBOL(register_idle_notifier); |
| 86 | 86 | ||
| 87 | int unregister_idle_notifier(struct notifier_block *nb) | 87 | int unregister_idle_notifier(struct notifier_block *nb) |
| 88 | { | 88 | { |
| 89 | return notifier_chain_unregister(&idle_chain, nb); | 89 | return atomic_notifier_chain_unregister(&idle_chain, nb); |
| 90 | } | 90 | } |
| 91 | EXPORT_SYMBOL(unregister_idle_notifier); | 91 | EXPORT_SYMBOL(unregister_idle_notifier); |
| 92 | 92 | ||
| @@ -95,7 +95,7 @@ void do_monitor_call(struct pt_regs *regs, long interruption_code) | |||
| 95 | /* disable monitor call class 0 */ | 95 | /* disable monitor call class 0 */ |
| 96 | __ctl_clear_bit(8, 15); | 96 | __ctl_clear_bit(8, 15); |
| 97 | 97 | ||
| 98 | notifier_call_chain(&idle_chain, CPU_NOT_IDLE, | 98 | atomic_notifier_call_chain(&idle_chain, CPU_NOT_IDLE, |
| 99 | (void *)(long) smp_processor_id()); | 99 | (void *)(long) smp_processor_id()); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| @@ -116,7 +116,8 @@ static void default_idle(void) | |||
| 116 | return; | 116 | return; |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | rc = notifier_call_chain(&idle_chain, CPU_IDLE, (void *)(long) cpu); | 119 | rc = atomic_notifier_call_chain(&idle_chain, |
| 120 | CPU_IDLE, (void *)(long) cpu); | ||
| 120 | if (rc != NOTIFY_OK && rc != NOTIFY_DONE) | 121 | if (rc != NOTIFY_OK && rc != NOTIFY_DONE) |
| 121 | BUG(); | 122 | BUG(); |
| 122 | if (rc != NOTIFY_OK) { | 123 | if (rc != NOTIFY_OK) { |
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index c0e79843f580..7ee4ca203616 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/root_dev.h> | 20 | #include <linux/root_dev.h> |
| 21 | #include <linux/utsname.h> | 21 | #include <linux/utsname.h> |
| 22 | #include <linux/cpu.h> | 22 | #include <linux/cpu.h> |
| 23 | #include <linux/pfn.h> | ||
| 23 | #include <asm/uaccess.h> | 24 | #include <asm/uaccess.h> |
| 24 | #include <asm/io.h> | 25 | #include <asm/io.h> |
| 25 | #include <asm/sections.h> | 26 | #include <asm/sections.h> |
| @@ -275,10 +276,6 @@ void __init setup_arch(char **cmdline_p) | |||
| 275 | 276 | ||
| 276 | sh_mv_setup(cmdline_p); | 277 | sh_mv_setup(cmdline_p); |
| 277 | 278 | ||
| 278 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 279 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 280 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 281 | |||
| 282 | /* | 279 | /* |
| 283 | * Find the highest page frame number we have available | 280 | * Find the highest page frame number we have available |
| 284 | */ | 281 | */ |
diff --git a/arch/sh64/kernel/setup.c b/arch/sh64/kernel/setup.c index c7a7b816a30f..d2711c9c9d13 100644 --- a/arch/sh64/kernel/setup.c +++ b/arch/sh64/kernel/setup.c | |||
| @@ -48,6 +48,7 @@ | |||
| 48 | #include <linux/root_dev.h> | 48 | #include <linux/root_dev.h> |
| 49 | #include <linux/cpu.h> | 49 | #include <linux/cpu.h> |
| 50 | #include <linux/initrd.h> | 50 | #include <linux/initrd.h> |
| 51 | #include <linux/pfn.h> | ||
| 51 | #include <asm/processor.h> | 52 | #include <asm/processor.h> |
| 52 | #include <asm/page.h> | 53 | #include <asm/page.h> |
| 53 | #include <asm/pgtable.h> | 54 | #include <asm/pgtable.h> |
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index df612e4f75f9..ff090bb9734b 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c | |||
| @@ -43,18 +43,19 @@ | |||
| 43 | #include <linux/kmod.h> | 43 | #include <linux/kmod.h> |
| 44 | #endif | 44 | #endif |
| 45 | 45 | ||
| 46 | struct notifier_block *sparc64die_chain; | 46 | ATOMIC_NOTIFIER_HEAD(sparc64die_chain); |
| 47 | static DEFINE_SPINLOCK(die_notifier_lock); | ||
| 48 | 47 | ||
| 49 | int register_die_notifier(struct notifier_block *nb) | 48 | int register_die_notifier(struct notifier_block *nb) |
| 50 | { | 49 | { |
| 51 | int err = 0; | 50 | return atomic_notifier_chain_register(&sparc64die_chain, nb); |
| 52 | unsigned long flags; | ||
| 53 | spin_lock_irqsave(&die_notifier_lock, flags); | ||
| 54 | err = notifier_chain_register(&sparc64die_chain, nb); | ||
| 55 | spin_unlock_irqrestore(&die_notifier_lock, flags); | ||
| 56 | return err; | ||
| 57 | } | 51 | } |
| 52 | EXPORT_SYMBOL(register_die_notifier); | ||
| 53 | |||
| 54 | int unregister_die_notifier(struct notifier_block *nb) | ||
| 55 | { | ||
| 56 | return atomic_notifier_chain_unregister(&sparc64die_chain, nb); | ||
| 57 | } | ||
| 58 | EXPORT_SYMBOL(unregister_die_notifier); | ||
| 58 | 59 | ||
| 59 | /* When an irrecoverable trap occurs at tl > 0, the trap entry | 60 | /* When an irrecoverable trap occurs at tl > 0, the trap entry |
| 60 | * code logs the trap state registers at every level in the trap | 61 | * code logs the trap state registers at every level in the trap |
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 54388d10bcf9..1488816588ea 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c | |||
| @@ -762,7 +762,8 @@ static struct notifier_block panic_exit_notifier = { | |||
| 762 | 762 | ||
| 763 | static int add_notifier(void) | 763 | static int add_notifier(void) |
| 764 | { | 764 | { |
| 765 | notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); | 765 | atomic_notifier_chain_register(&panic_notifier_list, |
| 766 | &panic_exit_notifier); | ||
| 766 | return(0); | 767 | return(0); |
| 767 | } | 768 | } |
| 768 | 769 | ||
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index fa617e0719ab..0336575d2448 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | 2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) |
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| @@ -71,7 +71,7 @@ struct io_thread_req { | |||
| 71 | int error; | 71 | int error; |
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| 74 | extern int open_ubd_file(char *file, struct openflags *openflags, | 74 | extern int open_ubd_file(char *file, struct openflags *openflags, int shared, |
| 75 | char **backing_file_out, int *bitmap_offset_out, | 75 | char **backing_file_out, int *bitmap_offset_out, |
| 76 | unsigned long *bitmap_len_out, int *data_offset_out, | 76 | unsigned long *bitmap_len_out, int *data_offset_out, |
| 77 | int *create_cow_out); | 77 | int *create_cow_out); |
| @@ -137,7 +137,7 @@ static int fake_major = MAJOR_NR; | |||
| 137 | 137 | ||
| 138 | static struct gendisk *ubd_gendisk[MAX_DEV]; | 138 | static struct gendisk *ubd_gendisk[MAX_DEV]; |
| 139 | static struct gendisk *fake_gendisk[MAX_DEV]; | 139 | static struct gendisk *fake_gendisk[MAX_DEV]; |
| 140 | 140 | ||
| 141 | #ifdef CONFIG_BLK_DEV_UBD_SYNC | 141 | #ifdef CONFIG_BLK_DEV_UBD_SYNC |
| 142 | #define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \ | 142 | #define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \ |
| 143 | .cl = 1 }) | 143 | .cl = 1 }) |
| @@ -168,6 +168,7 @@ struct ubd { | |||
| 168 | __u64 size; | 168 | __u64 size; |
| 169 | struct openflags boot_openflags; | 169 | struct openflags boot_openflags; |
| 170 | struct openflags openflags; | 170 | struct openflags openflags; |
| 171 | int shared; | ||
| 171 | int no_cow; | 172 | int no_cow; |
| 172 | struct cow cow; | 173 | struct cow cow; |
| 173 | struct platform_device pdev; | 174 | struct platform_device pdev; |
| @@ -189,6 +190,7 @@ struct ubd { | |||
| 189 | .boot_openflags = OPEN_FLAGS, \ | 190 | .boot_openflags = OPEN_FLAGS, \ |
| 190 | .openflags = OPEN_FLAGS, \ | 191 | .openflags = OPEN_FLAGS, \ |
| 191 | .no_cow = 0, \ | 192 | .no_cow = 0, \ |
| 193 | .shared = 0, \ | ||
| 192 | .cow = DEFAULT_COW, \ | 194 | .cow = DEFAULT_COW, \ |
| 193 | } | 195 | } |
| 194 | 196 | ||
| @@ -305,7 +307,7 @@ static int ubd_setup_common(char *str, int *index_out) | |||
| 305 | } | 307 | } |
| 306 | major = simple_strtoul(str, &end, 0); | 308 | major = simple_strtoul(str, &end, 0); |
| 307 | if((*end != '\0') || (end == str)){ | 309 | if((*end != '\0') || (end == str)){ |
| 308 | printk(KERN_ERR | 310 | printk(KERN_ERR |
| 309 | "ubd_setup : didn't parse major number\n"); | 311 | "ubd_setup : didn't parse major number\n"); |
| 310 | return(1); | 312 | return(1); |
| 311 | } | 313 | } |
| @@ -316,7 +318,7 @@ static int ubd_setup_common(char *str, int *index_out) | |||
| 316 | printk(KERN_ERR "Can't assign a fake major twice\n"); | 318 | printk(KERN_ERR "Can't assign a fake major twice\n"); |
| 317 | goto out1; | 319 | goto out1; |
| 318 | } | 320 | } |
| 319 | 321 | ||
| 320 | fake_major = major; | 322 | fake_major = major; |
| 321 | 323 | ||
| 322 | printk(KERN_INFO "Setting extra ubd major number to %d\n", | 324 | printk(KERN_INFO "Setting extra ubd major number to %d\n", |
| @@ -351,7 +353,7 @@ static int ubd_setup_common(char *str, int *index_out) | |||
| 351 | if (index_out) | 353 | if (index_out) |
| 352 | *index_out = n; | 354 | *index_out = n; |
| 353 | 355 | ||
| 354 | for (i = 0; i < 4; i++) { | 356 | for (i = 0; i < sizeof("rscd="); i++) { |
| 355 | switch (*str) { | 357 | switch (*str) { |
| 356 | case 'r': | 358 | case 'r': |
| 357 | flags.w = 0; | 359 | flags.w = 0; |
| @@ -362,11 +364,14 @@ static int ubd_setup_common(char *str, int *index_out) | |||
| 362 | case 'd': | 364 | case 'd': |
| 363 | dev->no_cow = 1; | 365 | dev->no_cow = 1; |
| 364 | break; | 366 | break; |
| 367 | case 'c': | ||
| 368 | dev->shared = 1; | ||
| 369 | break; | ||
| 365 | case '=': | 370 | case '=': |
| 366 | str++; | 371 | str++; |
| 367 | goto break_loop; | 372 | goto break_loop; |
| 368 | default: | 373 | default: |
| 369 | printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r,s or d)\n"); | 374 | printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r, s, c, or d)\n"); |
| 370 | goto out; | 375 | goto out; |
| 371 | } | 376 | } |
| 372 | str++; | 377 | str++; |
| @@ -515,7 +520,7 @@ static void ubd_handler(void) | |||
| 515 | spin_unlock(&ubd_io_lock); | 520 | spin_unlock(&ubd_io_lock); |
| 516 | return; | 521 | return; |
| 517 | } | 522 | } |
| 518 | 523 | ||
| 519 | ubd_finish(rq, req.error); | 524 | ubd_finish(rq, req.error); |
| 520 | reactivate_fd(thread_fd, UBD_IRQ); | 525 | reactivate_fd(thread_fd, UBD_IRQ); |
| 521 | do_ubd_request(ubd_queue); | 526 | do_ubd_request(ubd_queue); |
| @@ -532,7 +537,7 @@ static int io_pid = -1; | |||
| 532 | 537 | ||
| 533 | void kill_io_thread(void) | 538 | void kill_io_thread(void) |
| 534 | { | 539 | { |
| 535 | if(io_pid != -1) | 540 | if(io_pid != -1) |
| 536 | os_kill_process(io_pid, 1); | 541 | os_kill_process(io_pid, 1); |
| 537 | } | 542 | } |
| 538 | 543 | ||
| @@ -567,14 +572,15 @@ static int ubd_open_dev(struct ubd *dev) | |||
| 567 | create_cow = 0; | 572 | create_cow = 0; |
| 568 | create_ptr = (dev->cow.file != NULL) ? &create_cow : NULL; | 573 | create_ptr = (dev->cow.file != NULL) ? &create_cow : NULL; |
| 569 | back_ptr = dev->no_cow ? NULL : &dev->cow.file; | 574 | back_ptr = dev->no_cow ? NULL : &dev->cow.file; |
| 570 | dev->fd = open_ubd_file(dev->file, &dev->openflags, back_ptr, | 575 | dev->fd = open_ubd_file(dev->file, &dev->openflags, dev->shared, |
| 571 | &dev->cow.bitmap_offset, &dev->cow.bitmap_len, | 576 | back_ptr, &dev->cow.bitmap_offset, |
| 572 | &dev->cow.data_offset, create_ptr); | 577 | &dev->cow.bitmap_len, &dev->cow.data_offset, |
| 578 | create_ptr); | ||
| 573 | 579 | ||
| 574 | if((dev->fd == -ENOENT) && create_cow){ | 580 | if((dev->fd == -ENOENT) && create_cow){ |
| 575 | dev->fd = create_cow_file(dev->file, dev->cow.file, | 581 | dev->fd = create_cow_file(dev->file, dev->cow.file, |
| 576 | dev->openflags, 1 << 9, PAGE_SIZE, | 582 | dev->openflags, 1 << 9, PAGE_SIZE, |
| 577 | &dev->cow.bitmap_offset, | 583 | &dev->cow.bitmap_offset, |
| 578 | &dev->cow.bitmap_len, | 584 | &dev->cow.bitmap_len, |
| 579 | &dev->cow.data_offset); | 585 | &dev->cow.data_offset); |
| 580 | if(dev->fd >= 0){ | 586 | if(dev->fd >= 0){ |
| @@ -598,16 +604,16 @@ static int ubd_open_dev(struct ubd *dev) | |||
| 598 | } | 604 | } |
| 599 | flush_tlb_kernel_vm(); | 605 | flush_tlb_kernel_vm(); |
| 600 | 606 | ||
| 601 | err = read_cow_bitmap(dev->fd, dev->cow.bitmap, | 607 | err = read_cow_bitmap(dev->fd, dev->cow.bitmap, |
| 602 | dev->cow.bitmap_offset, | 608 | dev->cow.bitmap_offset, |
| 603 | dev->cow.bitmap_len); | 609 | dev->cow.bitmap_len); |
| 604 | if(err < 0) | 610 | if(err < 0) |
| 605 | goto error; | 611 | goto error; |
| 606 | 612 | ||
| 607 | flags = dev->openflags; | 613 | flags = dev->openflags; |
| 608 | flags.w = 0; | 614 | flags.w = 0; |
| 609 | err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL, | 615 | err = open_ubd_file(dev->cow.file, &flags, dev->shared, NULL, |
| 610 | NULL, NULL); | 616 | NULL, NULL, NULL, NULL); |
| 611 | if(err < 0) goto error; | 617 | if(err < 0) goto error; |
| 612 | dev->cow.fd = err; | 618 | dev->cow.fd = err; |
| 613 | } | 619 | } |
| @@ -685,11 +691,11 @@ static int ubd_add(int n) | |||
| 685 | dev->size = ROUND_BLOCK(dev->size); | 691 | dev->size = ROUND_BLOCK(dev->size); |
| 686 | 692 | ||
| 687 | err = ubd_new_disk(MAJOR_NR, dev->size, n, &ubd_gendisk[n]); | 693 | err = ubd_new_disk(MAJOR_NR, dev->size, n, &ubd_gendisk[n]); |
| 688 | if(err) | 694 | if(err) |
| 689 | goto out_close; | 695 | goto out_close; |
| 690 | 696 | ||
| 691 | if(fake_major != MAJOR_NR) | 697 | if(fake_major != MAJOR_NR) |
| 692 | ubd_new_disk(fake_major, dev->size, n, | 698 | ubd_new_disk(fake_major, dev->size, n, |
| 693 | &fake_gendisk[n]); | 699 | &fake_gendisk[n]); |
| 694 | 700 | ||
| 695 | /* perhaps this should also be under the "if (fake_major)" above */ | 701 | /* perhaps this should also be under the "if (fake_major)" above */ |
| @@ -854,7 +860,7 @@ int ubd_init(void) | |||
| 854 | return -1; | 860 | return -1; |
| 855 | } | 861 | } |
| 856 | platform_driver_register(&ubd_driver); | 862 | platform_driver_register(&ubd_driver); |
| 857 | for (i = 0; i < MAX_DEV; i++) | 863 | for (i = 0; i < MAX_DEV; i++) |
| 858 | ubd_add(i); | 864 | ubd_add(i); |
| 859 | return 0; | 865 | return 0; |
| 860 | } | 866 | } |
| @@ -872,16 +878,16 @@ int ubd_driver_init(void){ | |||
| 872 | * enough. So use anyway the io thread. */ | 878 | * enough. So use anyway the io thread. */ |
| 873 | } | 879 | } |
| 874 | stack = alloc_stack(0, 0); | 880 | stack = alloc_stack(0, 0); |
| 875 | io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), | 881 | io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), |
| 876 | &thread_fd); | 882 | &thread_fd); |
| 877 | if(io_pid < 0){ | 883 | if(io_pid < 0){ |
| 878 | printk(KERN_ERR | 884 | printk(KERN_ERR |
| 879 | "ubd : Failed to start I/O thread (errno = %d) - " | 885 | "ubd : Failed to start I/O thread (errno = %d) - " |
| 880 | "falling back to synchronous I/O\n", -io_pid); | 886 | "falling back to synchronous I/O\n", -io_pid); |
| 881 | io_pid = -1; | 887 | io_pid = -1; |
| 882 | return(0); | 888 | return(0); |
| 883 | } | 889 | } |
| 884 | err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr, | 890 | err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr, |
| 885 | SA_INTERRUPT, "ubd", ubd_dev); | 891 | SA_INTERRUPT, "ubd", ubd_dev); |
| 886 | if(err != 0) | 892 | if(err != 0) |
| 887 | printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err); | 893 | printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err); |
| @@ -978,7 +984,7 @@ static void cowify_req(struct io_thread_req *req, unsigned long *bitmap, | |||
| 978 | if(req->op == UBD_READ) { | 984 | if(req->op == UBD_READ) { |
| 979 | for(i = 0; i < req->length >> 9; i++){ | 985 | for(i = 0; i < req->length >> 9; i++){ |
| 980 | if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) | 986 | if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) |
| 981 | ubd_set_bit(i, (unsigned char *) | 987 | ubd_set_bit(i, (unsigned char *) |
| 982 | &req->sector_mask); | 988 | &req->sector_mask); |
| 983 | } | 989 | } |
| 984 | } | 990 | } |
| @@ -999,7 +1005,7 @@ static int prepare_request(struct request *req, struct io_thread_req *io_req) | |||
| 999 | 1005 | ||
| 1000 | /* This should be impossible now */ | 1006 | /* This should be impossible now */ |
| 1001 | if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ | 1007 | if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ |
| 1002 | printk("Write attempted on readonly ubd device %s\n", | 1008 | printk("Write attempted on readonly ubd device %s\n", |
| 1003 | disk->disk_name); | 1009 | disk->disk_name); |
| 1004 | end_request(req, 0); | 1010 | end_request(req, 0); |
| 1005 | return(1); | 1011 | return(1); |
| @@ -1182,7 +1188,7 @@ int read_cow_bitmap(int fd, void *buf, int offset, int len) | |||
| 1182 | return(0); | 1188 | return(0); |
| 1183 | } | 1189 | } |
| 1184 | 1190 | ||
| 1185 | int open_ubd_file(char *file, struct openflags *openflags, | 1191 | int open_ubd_file(char *file, struct openflags *openflags, int shared, |
| 1186 | char **backing_file_out, int *bitmap_offset_out, | 1192 | char **backing_file_out, int *bitmap_offset_out, |
| 1187 | unsigned long *bitmap_len_out, int *data_offset_out, | 1193 | unsigned long *bitmap_len_out, int *data_offset_out, |
| 1188 | int *create_cow_out) | 1194 | int *create_cow_out) |
| @@ -1206,10 +1212,14 @@ int open_ubd_file(char *file, struct openflags *openflags, | |||
| 1206 | return fd; | 1212 | return fd; |
| 1207 | } | 1213 | } |
| 1208 | 1214 | ||
| 1209 | err = os_lock_file(fd, openflags->w); | 1215 | if(shared) |
| 1210 | if(err < 0){ | 1216 | printk("Not locking \"%s\" on the host\n", file); |
| 1211 | printk("Failed to lock '%s', err = %d\n", file, -err); | 1217 | else { |
| 1212 | goto out_close; | 1218 | err = os_lock_file(fd, openflags->w); |
| 1219 | if(err < 0){ | ||
| 1220 | printk("Failed to lock '%s', err = %d\n", file, -err); | ||
| 1221 | goto out_close; | ||
| 1222 | } | ||
| 1213 | } | 1223 | } |
| 1214 | 1224 | ||
| 1215 | /* Succesful return case! */ | 1225 | /* Succesful return case! */ |
| @@ -1260,7 +1270,7 @@ int create_cow_file(char *cow_file, char *backing_file, struct openflags flags, | |||
| 1260 | int err, fd; | 1270 | int err, fd; |
| 1261 | 1271 | ||
| 1262 | flags.c = 1; | 1272 | flags.c = 1; |
| 1263 | fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL); | 1273 | fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL); |
| 1264 | if(fd < 0){ | 1274 | if(fd < 0){ |
| 1265 | err = fd; | 1275 | err = fd; |
| 1266 | printk("Open of COW file '%s' failed, errno = %d\n", cow_file, | 1276 | printk("Open of COW file '%s' failed, errno = %d\n", cow_file, |
diff --git a/arch/um/include/irq_user.h b/arch/um/include/irq_user.h index b61deb8b362a..69a93c804f0e 100644 --- a/arch/um/include/irq_user.h +++ b/arch/um/include/irq_user.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | 2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) |
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| @@ -6,6 +6,17 @@ | |||
| 6 | #ifndef __IRQ_USER_H__ | 6 | #ifndef __IRQ_USER_H__ |
| 7 | #define __IRQ_USER_H__ | 7 | #define __IRQ_USER_H__ |
| 8 | 8 | ||
| 9 | struct irq_fd { | ||
| 10 | struct irq_fd *next; | ||
| 11 | void *id; | ||
| 12 | int fd; | ||
| 13 | int type; | ||
| 14 | int irq; | ||
| 15 | int pid; | ||
| 16 | int events; | ||
| 17 | int current_events; | ||
| 18 | }; | ||
| 19 | |||
| 9 | enum { IRQ_READ, IRQ_WRITE }; | 20 | enum { IRQ_READ, IRQ_WRITE }; |
| 10 | 21 | ||
| 11 | extern void sigio_handler(int sig, union uml_pt_regs *regs); | 22 | extern void sigio_handler(int sig, union uml_pt_regs *regs); |
| @@ -16,8 +27,6 @@ extern void reactivate_fd(int fd, int irqnum); | |||
| 16 | extern void deactivate_fd(int fd, int irqnum); | 27 | extern void deactivate_fd(int fd, int irqnum); |
| 17 | extern int deactivate_all_fds(void); | 28 | extern int deactivate_all_fds(void); |
| 18 | extern void forward_interrupts(int pid); | 29 | extern void forward_interrupts(int pid); |
| 19 | extern void init_irq_signals(int on_sigstack); | ||
| 20 | extern void forward_ipi(int fd, int pid); | ||
| 21 | extern int activate_ipi(int fd, int pid); | 30 | extern int activate_ipi(int fd, int pid); |
| 22 | extern unsigned long irq_lock(void); | 31 | extern unsigned long irq_lock(void); |
| 23 | extern void irq_unlock(unsigned long flags); | 32 | extern void irq_unlock(unsigned long flags); |
diff --git a/arch/um/include/kern.h b/arch/um/include/kern.h index 7d223beccbc0..4ce3fc650e57 100644 --- a/arch/um/include/kern.h +++ b/arch/um/include/kern.h | |||
| @@ -29,7 +29,7 @@ extern int getuid(void); | |||
| 29 | extern int getgid(void); | 29 | extern int getgid(void); |
| 30 | extern int pause(void); | 30 | extern int pause(void); |
| 31 | extern int write(int, const void *, int); | 31 | extern int write(int, const void *, int); |
| 32 | extern int exit(int); | 32 | extern void exit(int); |
| 33 | extern int close(int); | 33 | extern int close(int); |
| 34 | extern int read(unsigned int, char *, int); | 34 | extern int read(unsigned int, char *, int); |
| 35 | extern int pipe(int *); | 35 | extern int pipe(int *); |
diff --git a/arch/um/include/misc_constants.h b/arch/um/include/misc_constants.h new file mode 100644 index 000000000000..989bc08de36e --- /dev/null +++ b/arch/um/include/misc_constants.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef __MISC_CONSTANT_H_ | ||
| 2 | #define __MISC_CONSTANT_H_ | ||
| 3 | |||
| 4 | #include <user_constants.h> | ||
| 5 | |||
| 6 | #endif | ||
diff --git a/arch/um/include/os.h b/arch/um/include/os.h index 2a1c64d8d0bf..d3d1bc6074ef 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "sysdep/ptrace.h" | 12 | #include "sysdep/ptrace.h" |
| 13 | #include "kern_util.h" | 13 | #include "kern_util.h" |
| 14 | #include "skas/mm_id.h" | 14 | #include "skas/mm_id.h" |
| 15 | #include "irq_user.h" | ||
| 15 | 16 | ||
| 16 | #define OS_TYPE_FILE 1 | 17 | #define OS_TYPE_FILE 1 |
| 17 | #define OS_TYPE_DIR 2 | 18 | #define OS_TYPE_DIR 2 |
| @@ -121,6 +122,7 @@ static inline struct openflags of_cloexec(struct openflags flags) | |||
| 121 | return(flags); | 122 | return(flags); |
| 122 | } | 123 | } |
| 123 | 124 | ||
| 125 | /* file.c */ | ||
| 124 | extern int os_stat_file(const char *file_name, struct uml_stat *buf); | 126 | extern int os_stat_file(const char *file_name, struct uml_stat *buf); |
| 125 | extern int os_stat_fd(const int fd, struct uml_stat *buf); | 127 | extern int os_stat_fd(const int fd, struct uml_stat *buf); |
| 126 | extern int os_access(const char *file, int mode); | 128 | extern int os_access(const char *file, int mode); |
| @@ -156,10 +158,20 @@ extern int os_connect_socket(char *name); | |||
| 156 | extern int os_file_type(char *file); | 158 | extern int os_file_type(char *file); |
| 157 | extern int os_file_mode(char *file, struct openflags *mode_out); | 159 | extern int os_file_mode(char *file, struct openflags *mode_out); |
| 158 | extern int os_lock_file(int fd, int excl); | 160 | extern int os_lock_file(int fd, int excl); |
| 161 | extern void os_flush_stdout(void); | ||
| 162 | extern int os_stat_filesystem(char *path, long *bsize_out, | ||
| 163 | long long *blocks_out, long long *bfree_out, | ||
| 164 | long long *bavail_out, long long *files_out, | ||
| 165 | long long *ffree_out, void *fsid_out, | ||
| 166 | int fsid_size, long *namelen_out, | ||
| 167 | long *spare_out); | ||
| 168 | extern int os_change_dir(char *dir); | ||
| 169 | extern int os_fchange_dir(int fd); | ||
| 159 | 170 | ||
| 160 | /* start_up.c */ | 171 | /* start_up.c */ |
| 161 | extern void os_early_checks(void); | 172 | extern void os_early_checks(void); |
| 162 | extern int can_do_skas(void); | 173 | extern int can_do_skas(void); |
| 174 | extern void os_check_bugs(void); | ||
| 163 | 175 | ||
| 164 | /* Make sure they are clear when running in TT mode. Required by | 176 | /* Make sure they are clear when running in TT mode. Required by |
| 165 | * SEGV_MAYBE_FIXABLE */ | 177 | * SEGV_MAYBE_FIXABLE */ |
| @@ -198,6 +210,8 @@ extern void os_flush_stdout(void); | |||
| 198 | /* tt.c | 210 | /* tt.c |
| 199 | * for tt mode only (will be deleted in future...) | 211 | * for tt mode only (will be deleted in future...) |
| 200 | */ | 212 | */ |
| 213 | extern void forward_ipi(int fd, int pid); | ||
| 214 | extern void kill_child_dead(int pid); | ||
| 201 | extern void stop(void); | 215 | extern void stop(void); |
| 202 | extern int wait_for_stop(int pid, int sig, int cont_type, void *relay); | 216 | extern int wait_for_stop(int pid, int sig, int cont_type, void *relay); |
| 203 | extern int protect_memory(unsigned long addr, unsigned long len, | 217 | extern int protect_memory(unsigned long addr, unsigned long len, |
| @@ -294,4 +308,26 @@ extern void initial_thread_cb_skas(void (*proc)(void *), | |||
| 294 | extern void halt_skas(void); | 308 | extern void halt_skas(void); |
| 295 | extern void reboot_skas(void); | 309 | extern void reboot_skas(void); |
| 296 | 310 | ||
| 311 | /* irq.c */ | ||
| 312 | extern int os_waiting_for_events(struct irq_fd *active_fds); | ||
| 313 | extern int os_isatty(int fd); | ||
| 314 | extern int os_create_pollfd(int fd, int events, void *tmp_pfd, int size_tmpfds); | ||
| 315 | extern void os_free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg, | ||
| 316 | struct irq_fd *active_fds, struct irq_fd ***last_irq_ptr2); | ||
| 317 | extern void os_free_irq_later(struct irq_fd *active_fds, | ||
| 318 | int irq, void *dev_id); | ||
| 319 | extern int os_get_pollfd(int i); | ||
| 320 | extern void os_set_pollfd(int i, int fd); | ||
| 321 | extern void os_set_ioignore(void); | ||
| 322 | extern void init_irq_signals(int on_sigstack); | ||
| 323 | |||
| 324 | /* sigio.c */ | ||
| 325 | extern void write_sigio_workaround(void); | ||
| 326 | extern int add_sigio_fd(int fd, int read); | ||
| 327 | extern int ignore_sigio_fd(int fd); | ||
| 328 | |||
| 329 | /* skas/trap */ | ||
| 330 | extern void sig_handler_common_skas(int sig, void *sc_ptr); | ||
| 331 | extern void user_signal(int sig, union uml_pt_regs *regs, int pid); | ||
| 332 | |||
| 297 | #endif | 333 | #endif |
diff --git a/arch/um/include/sigio.h b/arch/um/include/sigio.h index 37d76e29a147..fe99ea163c2e 100644 --- a/arch/um/include/sigio.h +++ b/arch/um/include/sigio.h | |||
| @@ -8,9 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | extern int write_sigio_irq(int fd); | 9 | extern int write_sigio_irq(int fd); |
| 10 | extern int register_sigio_fd(int fd); | 10 | extern int register_sigio_fd(int fd); |
| 11 | extern int read_sigio_fd(int fd); | ||
| 12 | extern int add_sigio_fd(int fd, int read); | ||
| 13 | extern int ignore_sigio_fd(int fd); | ||
| 14 | extern void sigio_lock(void); | 11 | extern void sigio_lock(void); |
| 15 | extern void sigio_unlock(void); | 12 | extern void sigio_unlock(void); |
| 16 | 13 | ||
diff --git a/arch/um/include/skas/mode-skas.h b/arch/um/include/skas/mode-skas.h index 260065cfeef1..8bc6916bbbb1 100644 --- a/arch/um/include/skas/mode-skas.h +++ b/arch/um/include/skas/mode-skas.h | |||
| @@ -13,7 +13,6 @@ extern unsigned long exec_fp_regs[]; | |||
| 13 | extern unsigned long exec_fpx_regs[]; | 13 | extern unsigned long exec_fpx_regs[]; |
| 14 | extern int have_fpx_regs; | 14 | extern int have_fpx_regs; |
| 15 | 15 | ||
| 16 | extern void sig_handler_common_skas(int sig, void *sc_ptr); | ||
| 17 | extern void kill_off_processes_skas(void); | 16 | extern void kill_off_processes_skas(void); |
| 18 | 17 | ||
| 19 | #endif | 18 | #endif |
diff --git a/arch/um/include/skas/skas.h b/arch/um/include/skas/skas.h index 86357282d681..853b26f148c5 100644 --- a/arch/um/include/skas/skas.h +++ b/arch/um/include/skas/skas.h | |||
| @@ -17,7 +17,6 @@ extern int user_thread(unsigned long stack, int flags); | |||
| 17 | extern void new_thread_proc(void *stack, void (*handler)(int sig)); | 17 | extern void new_thread_proc(void *stack, void (*handler)(int sig)); |
| 18 | extern void new_thread_handler(int sig); | 18 | extern void new_thread_handler(int sig); |
| 19 | extern void handle_syscall(union uml_pt_regs *regs); | 19 | extern void handle_syscall(union uml_pt_regs *regs); |
| 20 | extern void user_signal(int sig, union uml_pt_regs *regs, int pid); | ||
| 21 | extern int new_mm(unsigned long stack); | 20 | extern int new_mm(unsigned long stack); |
| 22 | extern void get_skas_faultinfo(int pid, struct faultinfo * fi); | 21 | extern void get_skas_faultinfo(int pid, struct faultinfo * fi); |
| 23 | extern long execute_syscall_skas(void *r); | 22 | extern long execute_syscall_skas(void *r); |
diff --git a/arch/um/include/user_util.h b/arch/um/include/user_util.h index a6f1f176cf84..992a7e1e0fca 100644 --- a/arch/um/include/user_util.h +++ b/arch/um/include/user_util.h | |||
| @@ -58,7 +58,6 @@ extern int attach(int pid); | |||
| 58 | extern void kill_child_dead(int pid); | 58 | extern void kill_child_dead(int pid); |
| 59 | extern int cont(int pid); | 59 | extern int cont(int pid); |
| 60 | extern void check_sigio(void); | 60 | extern void check_sigio(void); |
| 61 | extern void write_sigio_workaround(void); | ||
| 62 | extern void arch_check_bugs(void); | 61 | extern void arch_check_bugs(void); |
| 63 | extern int cpu_feature(char *what, char *buf, int len); | 62 | extern int cpu_feature(char *what, char *buf, int len); |
| 64 | extern int arch_handle_signal(int sig, union uml_pt_regs *regs); | 63 | extern int arch_handle_signal(int sig, union uml_pt_regs *regs); |
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index 693018ba80f1..fe08971b64cf 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile | |||
| @@ -7,23 +7,20 @@ extra-y := vmlinux.lds | |||
| 7 | clean-files := | 7 | clean-files := |
| 8 | 8 | ||
| 9 | obj-y = config.o exec_kern.o exitcode.o \ | 9 | obj-y = config.o exec_kern.o exitcode.o \ |
| 10 | init_task.o irq.o irq_user.o ksyms.o mem.o physmem.o \ | 10 | init_task.o irq.o ksyms.o mem.o physmem.o \ |
| 11 | process_kern.o ptrace.o reboot.o resource.o sigio_user.o sigio_kern.o \ | 11 | process_kern.o ptrace.o reboot.o resource.o sigio_kern.o \ |
| 12 | signal_kern.o smp.o syscall_kern.o sysrq.o \ | 12 | signal_kern.o smp.o syscall_kern.o sysrq.o \ |
| 13 | time_kern.o tlb.o trap_kern.o uaccess.o um_arch.o umid.o | 13 | time_kern.o tlb.o trap_kern.o uaccess.o um_arch.o umid.o |
| 14 | 14 | ||
| 15 | obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o | 15 | obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o |
| 16 | obj-$(CONFIG_GPROF) += gprof_syms.o | 16 | obj-$(CONFIG_GPROF) += gprof_syms.o |
| 17 | obj-$(CONFIG_GCOV) += gmon_syms.o | 17 | obj-$(CONFIG_GCOV) += gmon_syms.o |
| 18 | obj-$(CONFIG_TTY_LOG) += tty_log.o | ||
| 19 | obj-$(CONFIG_SYSCALL_DEBUG) += syscall.o | 18 | obj-$(CONFIG_SYSCALL_DEBUG) += syscall.o |
| 20 | 19 | ||
| 21 | obj-$(CONFIG_MODE_TT) += tt/ | 20 | obj-$(CONFIG_MODE_TT) += tt/ |
| 22 | obj-$(CONFIG_MODE_SKAS) += skas/ | 21 | obj-$(CONFIG_MODE_SKAS) += skas/ |
| 23 | 22 | ||
| 24 | user-objs-$(CONFIG_TTY_LOG) += tty_log.o | 23 | USER_OBJS := config.o |
| 25 | |||
| 26 | USER_OBJS := $(user-objs-y) config.o tty_log.o | ||
| 27 | 24 | ||
| 28 | include arch/um/scripts/Makefile.rules | 25 | include arch/um/scripts/Makefile.rules |
| 29 | 26 | ||
diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c index c264e1c05ab3..1ca84319317d 100644 --- a/arch/um/kernel/exec_kern.c +++ b/arch/um/kernel/exec_kern.c | |||
| @@ -30,8 +30,6 @@ void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp) | |||
| 30 | CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp); | 30 | CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | extern void log_exec(char **argv, void *tty); | ||
| 34 | |||
| 35 | static long execve1(char *file, char __user * __user *argv, | 33 | static long execve1(char *file, char __user * __user *argv, |
| 36 | char __user *__user *env) | 34 | char __user *__user *env) |
| 37 | { | 35 | { |
diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index bbf94bf2921e..c39ea3abeda4 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c | |||
| @@ -31,6 +31,8 @@ | |||
| 31 | #include "irq_user.h" | 31 | #include "irq_user.h" |
| 32 | #include "irq_kern.h" | 32 | #include "irq_kern.h" |
| 33 | #include "os.h" | 33 | #include "os.h" |
| 34 | #include "sigio.h" | ||
| 35 | #include "misc_constants.h" | ||
| 34 | 36 | ||
| 35 | /* | 37 | /* |
| 36 | * Generic, controller-independent functions: | 38 | * Generic, controller-independent functions: |
| @@ -77,6 +79,298 @@ skip: | |||
| 77 | return 0; | 79 | return 0; |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 82 | struct irq_fd *active_fds = NULL; | ||
| 83 | static struct irq_fd **last_irq_ptr = &active_fds; | ||
| 84 | |||
| 85 | extern void free_irqs(void); | ||
| 86 | |||
| 87 | void sigio_handler(int sig, union uml_pt_regs *regs) | ||
| 88 | { | ||
| 89 | struct irq_fd *irq_fd; | ||
| 90 | int n; | ||
| 91 | |||
| 92 | if(smp_sigio_handler()) return; | ||
| 93 | while(1){ | ||
| 94 | n = os_waiting_for_events(active_fds); | ||
| 95 | if (n <= 0) { | ||
| 96 | if(n == -EINTR) continue; | ||
| 97 | else break; | ||
| 98 | } | ||
| 99 | |||
| 100 | for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ | ||
| 101 | if(irq_fd->current_events != 0){ | ||
| 102 | irq_fd->current_events = 0; | ||
| 103 | do_IRQ(irq_fd->irq, regs); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | free_irqs(); | ||
| 109 | } | ||
| 110 | |||
| 111 | static void maybe_sigio_broken(int fd, int type) | ||
| 112 | { | ||
| 113 | if(os_isatty(fd)){ | ||
| 114 | if((type == IRQ_WRITE) && !pty_output_sigio){ | ||
| 115 | write_sigio_workaround(); | ||
| 116 | add_sigio_fd(fd, 0); | ||
| 117 | } | ||
| 118 | else if((type == IRQ_READ) && !pty_close_sigio){ | ||
| 119 | write_sigio_workaround(); | ||
| 120 | add_sigio_fd(fd, 1); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | |||
| 126 | int activate_fd(int irq, int fd, int type, void *dev_id) | ||
| 127 | { | ||
| 128 | struct pollfd *tmp_pfd; | ||
| 129 | struct irq_fd *new_fd, *irq_fd; | ||
| 130 | unsigned long flags; | ||
| 131 | int pid, events, err, n; | ||
| 132 | |||
| 133 | pid = os_getpid(); | ||
| 134 | err = os_set_fd_async(fd, pid); | ||
| 135 | if(err < 0) | ||
| 136 | goto out; | ||
| 137 | |||
| 138 | new_fd = um_kmalloc(sizeof(*new_fd)); | ||
| 139 | err = -ENOMEM; | ||
| 140 | if(new_fd == NULL) | ||
| 141 | goto out; | ||
| 142 | |||
| 143 | if(type == IRQ_READ) events = UM_POLLIN | UM_POLLPRI; | ||
| 144 | else events = UM_POLLOUT; | ||
| 145 | *new_fd = ((struct irq_fd) { .next = NULL, | ||
| 146 | .id = dev_id, | ||
| 147 | .fd = fd, | ||
| 148 | .type = type, | ||
| 149 | .irq = irq, | ||
| 150 | .pid = pid, | ||
| 151 | .events = events, | ||
| 152 | .current_events = 0 } ); | ||
| 153 | |||
| 154 | /* Critical section - locked by a spinlock because this stuff can | ||
| 155 | * be changed from interrupt handlers. The stuff above is done | ||
| 156 | * outside the lock because it allocates memory. | ||
| 157 | */ | ||
| 158 | |||
| 159 | /* Actually, it only looks like it can be called from interrupt | ||
| 160 | * context. The culprit is reactivate_fd, which calls | ||
| 161 | * maybe_sigio_broken, which calls write_sigio_workaround, | ||
| 162 | * which calls activate_fd. However, write_sigio_workaround should | ||
| 163 | * only be called once, at boot time. That would make it clear that | ||
| 164 | * this is called only from process context, and can be locked with | ||
| 165 | * a semaphore. | ||
| 166 | */ | ||
| 167 | flags = irq_lock(); | ||
| 168 | for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ | ||
| 169 | if((irq_fd->fd == fd) && (irq_fd->type == type)){ | ||
| 170 | printk("Registering fd %d twice\n", fd); | ||
| 171 | printk("Irqs : %d, %d\n", irq_fd->irq, irq); | ||
| 172 | printk("Ids : 0x%p, 0x%p\n", irq_fd->id, dev_id); | ||
| 173 | goto out_unlock; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | /*-------------*/ | ||
| 178 | if(type == IRQ_WRITE) | ||
| 179 | fd = -1; | ||
| 180 | |||
| 181 | tmp_pfd = NULL; | ||
| 182 | n = 0; | ||
| 183 | |||
| 184 | while(1){ | ||
| 185 | n = os_create_pollfd(fd, events, tmp_pfd, n); | ||
| 186 | if (n == 0) | ||
| 187 | break; | ||
| 188 | |||
| 189 | /* n > 0 | ||
| 190 | * It means we couldn't put new pollfd to current pollfds | ||
| 191 | * and tmp_fds is NULL or too small for new pollfds array. | ||
| 192 | * Needed size is equal to n as minimum. | ||
| 193 | * | ||
| 194 | * Here we have to drop the lock in order to call | ||
| 195 | * kmalloc, which might sleep. | ||
| 196 | * If something else came in and changed the pollfds array | ||
| 197 | * so we will not be able to put new pollfd struct to pollfds | ||
| 198 | * then we free the buffer tmp_fds and try again. | ||
| 199 | */ | ||
| 200 | irq_unlock(flags); | ||
| 201 | if (tmp_pfd != NULL) { | ||
| 202 | kfree(tmp_pfd); | ||
| 203 | tmp_pfd = NULL; | ||
| 204 | } | ||
| 205 | |||
| 206 | tmp_pfd = um_kmalloc(n); | ||
| 207 | if (tmp_pfd == NULL) | ||
| 208 | goto out_kfree; | ||
| 209 | |||
| 210 | flags = irq_lock(); | ||
| 211 | } | ||
| 212 | /*-------------*/ | ||
| 213 | |||
| 214 | *last_irq_ptr = new_fd; | ||
| 215 | last_irq_ptr = &new_fd->next; | ||
| 216 | |||
| 217 | irq_unlock(flags); | ||
| 218 | |||
| 219 | /* This calls activate_fd, so it has to be outside the critical | ||
| 220 | * section. | ||
| 221 | */ | ||
| 222 | maybe_sigio_broken(fd, type); | ||
| 223 | |||
| 224 | return(0); | ||
| 225 | |||
| 226 | out_unlock: | ||
| 227 | irq_unlock(flags); | ||
| 228 | out_kfree: | ||
| 229 | kfree(new_fd); | ||
| 230 | out: | ||
| 231 | return(err); | ||
| 232 | } | ||
| 233 | |||
| 234 | static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) | ||
| 235 | { | ||
| 236 | unsigned long flags; | ||
| 237 | |||
| 238 | flags = irq_lock(); | ||
| 239 | os_free_irq_by_cb(test, arg, active_fds, &last_irq_ptr); | ||
| 240 | irq_unlock(flags); | ||
| 241 | } | ||
| 242 | |||
| 243 | struct irq_and_dev { | ||
| 244 | int irq; | ||
| 245 | void *dev; | ||
| 246 | }; | ||
| 247 | |||
| 248 | static int same_irq_and_dev(struct irq_fd *irq, void *d) | ||
| 249 | { | ||
| 250 | struct irq_and_dev *data = d; | ||
| 251 | |||
| 252 | return((irq->irq == data->irq) && (irq->id == data->dev)); | ||
| 253 | } | ||
| 254 | |||
| 255 | void free_irq_by_irq_and_dev(unsigned int irq, void *dev) | ||
| 256 | { | ||
| 257 | struct irq_and_dev data = ((struct irq_and_dev) { .irq = irq, | ||
| 258 | .dev = dev }); | ||
| 259 | |||
| 260 | free_irq_by_cb(same_irq_and_dev, &data); | ||
| 261 | } | ||
| 262 | |||
| 263 | static int same_fd(struct irq_fd *irq, void *fd) | ||
| 264 | { | ||
| 265 | return(irq->fd == *((int *) fd)); | ||
| 266 | } | ||
| 267 | |||
| 268 | void free_irq_by_fd(int fd) | ||
| 269 | { | ||
| 270 | free_irq_by_cb(same_fd, &fd); | ||
| 271 | } | ||
| 272 | |||
| 273 | static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out) | ||
| 274 | { | ||
| 275 | struct irq_fd *irq; | ||
| 276 | int i = 0; | ||
| 277 | int fdi; | ||
| 278 | |||
| 279 | for(irq=active_fds; irq != NULL; irq = irq->next){ | ||
| 280 | if((irq->fd == fd) && (irq->irq == irqnum)) break; | ||
| 281 | i++; | ||
| 282 | } | ||
| 283 | if(irq == NULL){ | ||
| 284 | printk("find_irq_by_fd doesn't have descriptor %d\n", fd); | ||
| 285 | goto out; | ||
| 286 | } | ||
| 287 | fdi = os_get_pollfd(i); | ||
| 288 | if((fdi != -1) && (fdi != fd)){ | ||
| 289 | printk("find_irq_by_fd - mismatch between active_fds and " | ||
| 290 | "pollfds, fd %d vs %d, need %d\n", irq->fd, | ||
| 291 | fdi, fd); | ||
| 292 | irq = NULL; | ||
| 293 | goto out; | ||
| 294 | } | ||
| 295 | *index_out = i; | ||
| 296 | out: | ||
| 297 | return(irq); | ||
| 298 | } | ||
| 299 | |||
| 300 | void reactivate_fd(int fd, int irqnum) | ||
| 301 | { | ||
| 302 | struct irq_fd *irq; | ||
| 303 | unsigned long flags; | ||
| 304 | int i; | ||
| 305 | |||
| 306 | flags = irq_lock(); | ||
| 307 | irq = find_irq_by_fd(fd, irqnum, &i); | ||
| 308 | if(irq == NULL){ | ||
| 309 | irq_unlock(flags); | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | os_set_pollfd(i, irq->fd); | ||
| 313 | irq_unlock(flags); | ||
| 314 | |||
| 315 | /* This calls activate_fd, so it has to be outside the critical | ||
| 316 | * section. | ||
| 317 | */ | ||
| 318 | maybe_sigio_broken(fd, irq->type); | ||
| 319 | } | ||
| 320 | |||
| 321 | void deactivate_fd(int fd, int irqnum) | ||
| 322 | { | ||
| 323 | struct irq_fd *irq; | ||
| 324 | unsigned long flags; | ||
| 325 | int i; | ||
| 326 | |||
| 327 | flags = irq_lock(); | ||
| 328 | irq = find_irq_by_fd(fd, irqnum, &i); | ||
| 329 | if(irq == NULL) | ||
| 330 | goto out; | ||
| 331 | os_set_pollfd(i, -1); | ||
| 332 | out: | ||
| 333 | irq_unlock(flags); | ||
| 334 | } | ||
| 335 | |||
| 336 | int deactivate_all_fds(void) | ||
| 337 | { | ||
| 338 | struct irq_fd *irq; | ||
| 339 | int err; | ||
| 340 | |||
| 341 | for(irq=active_fds;irq != NULL;irq = irq->next){ | ||
| 342 | err = os_clear_fd_async(irq->fd); | ||
| 343 | if(err) | ||
| 344 | return(err); | ||
| 345 | } | ||
| 346 | /* If there is a signal already queued, after unblocking ignore it */ | ||
| 347 | os_set_ioignore(); | ||
| 348 | |||
| 349 | return(0); | ||
| 350 | } | ||
| 351 | |||
| 352 | void forward_interrupts(int pid) | ||
| 353 | { | ||
| 354 | struct irq_fd *irq; | ||
| 355 | unsigned long flags; | ||
| 356 | int err; | ||
| 357 | |||
| 358 | flags = irq_lock(); | ||
| 359 | for(irq=active_fds;irq != NULL;irq = irq->next){ | ||
| 360 | err = os_set_owner(irq->fd, pid); | ||
| 361 | if(err < 0){ | ||
| 362 | /* XXX Just remove the irq rather than | ||
| 363 | * print out an infinite stream of these | ||
| 364 | */ | ||
| 365 | printk("Failed to forward %d to pid %d, err = %d\n", | ||
| 366 | irq->fd, pid, -err); | ||
| 367 | } | ||
| 368 | |||
| 369 | irq->pid = pid; | ||
| 370 | } | ||
| 371 | irq_unlock(flags); | ||
| 372 | } | ||
| 373 | |||
| 80 | /* | 374 | /* |
| 81 | * do_IRQ handles all normal device IRQ's (the special | 375 | * do_IRQ handles all normal device IRQ's (the special |
| 82 | * SMP cross-CPU interrupts have their own specific | 376 | * SMP cross-CPU interrupts have their own specific |
diff --git a/arch/um/kernel/irq_user.c b/arch/um/kernel/irq_user.c deleted file mode 100644 index 0e32f5f4a887..000000000000 --- a/arch/um/kernel/irq_user.c +++ /dev/null | |||
| @@ -1,412 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
| 3 | * Licensed under the GPL | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <stdlib.h> | ||
| 7 | #include <unistd.h> | ||
| 8 | #include <errno.h> | ||
| 9 | #include <signal.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include <sys/poll.h> | ||
| 12 | #include <sys/types.h> | ||
| 13 | #include <sys/time.h> | ||
| 14 | #include "user_util.h" | ||
| 15 | #include "kern_util.h" | ||
| 16 | #include "user.h" | ||
| 17 | #include "process.h" | ||
| 18 | #include "sigio.h" | ||
| 19 | #include "irq_user.h" | ||
| 20 | #include "os.h" | ||
| 21 | |||
| 22 | struct irq_fd { | ||
| 23 | struct irq_fd *next; | ||
| 24 | void *id; | ||
| 25 | int fd; | ||
| 26 | int type; | ||
| 27 | int irq; | ||
| 28 | int pid; | ||
| 29 | int events; | ||
| 30 | int current_events; | ||
| 31 | }; | ||
| 32 | |||
| 33 | static struct irq_fd *active_fds = NULL; | ||
| 34 | static struct irq_fd **last_irq_ptr = &active_fds; | ||
| 35 | |||
| 36 | static struct pollfd *pollfds = NULL; | ||
| 37 | static int pollfds_num = 0; | ||
| 38 | static int pollfds_size = 0; | ||
| 39 | |||
| 40 | extern int io_count, intr_count; | ||
| 41 | |||
| 42 | extern void free_irqs(void); | ||
| 43 | |||
| 44 | void sigio_handler(int sig, union uml_pt_regs *regs) | ||
| 45 | { | ||
| 46 | struct irq_fd *irq_fd; | ||
| 47 | int i, n; | ||
| 48 | |||
| 49 | if(smp_sigio_handler()) return; | ||
| 50 | while(1){ | ||
| 51 | n = poll(pollfds, pollfds_num, 0); | ||
| 52 | if(n < 0){ | ||
| 53 | if(errno == EINTR) continue; | ||
| 54 | printk("sigio_handler : poll returned %d, " | ||
| 55 | "errno = %d\n", n, errno); | ||
| 56 | break; | ||
| 57 | } | ||
| 58 | if(n == 0) break; | ||
| 59 | |||
| 60 | irq_fd = active_fds; | ||
| 61 | for(i = 0; i < pollfds_num; i++){ | ||
| 62 | if(pollfds[i].revents != 0){ | ||
| 63 | irq_fd->current_events = pollfds[i].revents; | ||
| 64 | pollfds[i].fd = -1; | ||
| 65 | } | ||
| 66 | irq_fd = irq_fd->next; | ||
| 67 | } | ||
| 68 | |||
| 69 | for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ | ||
| 70 | if(irq_fd->current_events != 0){ | ||
| 71 | irq_fd->current_events = 0; | ||
| 72 | do_IRQ(irq_fd->irq, regs); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | free_irqs(); | ||
| 78 | } | ||
| 79 | |||
| 80 | int activate_ipi(int fd, int pid) | ||
| 81 | { | ||
| 82 | return(os_set_fd_async(fd, pid)); | ||
| 83 | } | ||
| 84 | |||
| 85 | static void maybe_sigio_broken(int fd, int type) | ||
| 86 | { | ||
| 87 | if(isatty(fd)){ | ||
| 88 | if((type == IRQ_WRITE) && !pty_output_sigio){ | ||
| 89 | write_sigio_workaround(); | ||
| 90 | add_sigio_fd(fd, 0); | ||
| 91 | } | ||
| 92 | else if((type == IRQ_READ) && !pty_close_sigio){ | ||
| 93 | write_sigio_workaround(); | ||
| 94 | add_sigio_fd(fd, 1); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | int activate_fd(int irq, int fd, int type, void *dev_id) | ||
| 100 | { | ||
| 101 | struct pollfd *tmp_pfd; | ||
| 102 | struct irq_fd *new_fd, *irq_fd; | ||
| 103 | unsigned long flags; | ||
| 104 | int pid, events, err, n, size; | ||
| 105 | |||
| 106 | pid = os_getpid(); | ||
| 107 | err = os_set_fd_async(fd, pid); | ||
| 108 | if(err < 0) | ||
| 109 | goto out; | ||
| 110 | |||
| 111 | new_fd = um_kmalloc(sizeof(*new_fd)); | ||
| 112 | err = -ENOMEM; | ||
| 113 | if(new_fd == NULL) | ||
| 114 | goto out; | ||
| 115 | |||
| 116 | if(type == IRQ_READ) events = POLLIN | POLLPRI; | ||
| 117 | else events = POLLOUT; | ||
| 118 | *new_fd = ((struct irq_fd) { .next = NULL, | ||
| 119 | .id = dev_id, | ||
| 120 | .fd = fd, | ||
| 121 | .type = type, | ||
| 122 | .irq = irq, | ||
| 123 | .pid = pid, | ||
| 124 | .events = events, | ||
| 125 | .current_events = 0 } ); | ||
| 126 | |||
| 127 | /* Critical section - locked by a spinlock because this stuff can | ||
| 128 | * be changed from interrupt handlers. The stuff above is done | ||
| 129 | * outside the lock because it allocates memory. | ||
| 130 | */ | ||
| 131 | |||
| 132 | /* Actually, it only looks like it can be called from interrupt | ||
| 133 | * context. The culprit is reactivate_fd, which calls | ||
| 134 | * maybe_sigio_broken, which calls write_sigio_workaround, | ||
| 135 | * which calls activate_fd. However, write_sigio_workaround should | ||
| 136 | * only be called once, at boot time. That would make it clear that | ||
| 137 | * this is called only from process context, and can be locked with | ||
| 138 | * a semaphore. | ||
| 139 | */ | ||
| 140 | flags = irq_lock(); | ||
| 141 | for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ | ||
| 142 | if((irq_fd->fd == fd) && (irq_fd->type == type)){ | ||
| 143 | printk("Registering fd %d twice\n", fd); | ||
| 144 | printk("Irqs : %d, %d\n", irq_fd->irq, irq); | ||
| 145 | printk("Ids : 0x%x, 0x%x\n", irq_fd->id, dev_id); | ||
| 146 | goto out_unlock; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | n = pollfds_num; | ||
| 151 | if(n == pollfds_size){ | ||
| 152 | while(1){ | ||
| 153 | /* Here we have to drop the lock in order to call | ||
| 154 | * kmalloc, which might sleep. If something else | ||
| 155 | * came in and changed the pollfds array, we free | ||
| 156 | * the buffer and try again. | ||
| 157 | */ | ||
| 158 | irq_unlock(flags); | ||
| 159 | size = (pollfds_num + 1) * sizeof(pollfds[0]); | ||
| 160 | tmp_pfd = um_kmalloc(size); | ||
| 161 | flags = irq_lock(); | ||
| 162 | if(tmp_pfd == NULL) | ||
| 163 | goto out_unlock; | ||
| 164 | if(n == pollfds_size) | ||
| 165 | break; | ||
| 166 | kfree(tmp_pfd); | ||
| 167 | } | ||
| 168 | if(pollfds != NULL){ | ||
| 169 | memcpy(tmp_pfd, pollfds, | ||
| 170 | sizeof(pollfds[0]) * pollfds_size); | ||
| 171 | kfree(pollfds); | ||
| 172 | } | ||
| 173 | pollfds = tmp_pfd; | ||
| 174 | pollfds_size++; | ||
| 175 | } | ||
| 176 | |||
| 177 | if(type == IRQ_WRITE) | ||
| 178 | fd = -1; | ||
| 179 | |||
| 180 | pollfds[pollfds_num] = ((struct pollfd) { .fd = fd, | ||
| 181 | .events = events, | ||
| 182 | .revents = 0 }); | ||
| 183 | pollfds_num++; | ||
| 184 | |||
| 185 | *last_irq_ptr = new_fd; | ||
| 186 | last_irq_ptr = &new_fd->next; | ||
| 187 | |||
| 188 | irq_unlock(flags); | ||
| 189 | |||
| 190 | /* This calls activate_fd, so it has to be outside the critical | ||
| 191 | * section. | ||
| 192 | */ | ||
| 193 | maybe_sigio_broken(fd, type); | ||
| 194 | |||
| 195 | return(0); | ||
| 196 | |||
| 197 | out_unlock: | ||
| 198 | irq_unlock(flags); | ||
| 199 | kfree(new_fd); | ||
| 200 | out: | ||
| 201 | return(err); | ||
| 202 | } | ||
| 203 | |||
| 204 | static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) | ||
| 205 | { | ||
| 206 | struct irq_fd **prev; | ||
| 207 | unsigned long flags; | ||
| 208 | int i = 0; | ||
| 209 | |||
| 210 | flags = irq_lock(); | ||
| 211 | prev = &active_fds; | ||
| 212 | while(*prev != NULL){ | ||
| 213 | if((*test)(*prev, arg)){ | ||
| 214 | struct irq_fd *old_fd = *prev; | ||
| 215 | if((pollfds[i].fd != -1) && | ||
| 216 | (pollfds[i].fd != (*prev)->fd)){ | ||
| 217 | printk("free_irq_by_cb - mismatch between " | ||
| 218 | "active_fds and pollfds, fd %d vs %d\n", | ||
| 219 | (*prev)->fd, pollfds[i].fd); | ||
| 220 | goto out; | ||
| 221 | } | ||
| 222 | |||
| 223 | pollfds_num--; | ||
| 224 | |||
| 225 | /* This moves the *whole* array after pollfds[i] (though | ||
| 226 | * it doesn't spot as such)! */ | ||
| 227 | |||
| 228 | memmove(&pollfds[i], &pollfds[i + 1], | ||
| 229 | (pollfds_num - i) * sizeof(pollfds[0])); | ||
| 230 | |||
| 231 | if(last_irq_ptr == &old_fd->next) | ||
| 232 | last_irq_ptr = prev; | ||
| 233 | *prev = (*prev)->next; | ||
| 234 | if(old_fd->type == IRQ_WRITE) | ||
| 235 | ignore_sigio_fd(old_fd->fd); | ||
| 236 | kfree(old_fd); | ||
| 237 | continue; | ||
| 238 | } | ||
| 239 | prev = &(*prev)->next; | ||
| 240 | i++; | ||
| 241 | } | ||
| 242 | out: | ||
| 243 | irq_unlock(flags); | ||
| 244 | } | ||
| 245 | |||
| 246 | struct irq_and_dev { | ||
| 247 | int irq; | ||
| 248 | void *dev; | ||
| 249 | }; | ||
| 250 | |||
| 251 | static int same_irq_and_dev(struct irq_fd *irq, void *d) | ||
| 252 | { | ||
| 253 | struct irq_and_dev *data = d; | ||
| 254 | |||
| 255 | return((irq->irq == data->irq) && (irq->id == data->dev)); | ||
| 256 | } | ||
| 257 | |||
| 258 | void free_irq_by_irq_and_dev(unsigned int irq, void *dev) | ||
| 259 | { | ||
| 260 | struct irq_and_dev data = ((struct irq_and_dev) { .irq = irq, | ||
| 261 | .dev = dev }); | ||
| 262 | |||
| 263 | free_irq_by_cb(same_irq_and_dev, &data); | ||
| 264 | } | ||
| 265 | |||
| 266 | static int same_fd(struct irq_fd *irq, void *fd) | ||
| 267 | { | ||
| 268 | return(irq->fd == *((int *) fd)); | ||
| 269 | } | ||
| 270 | |||
| 271 | void free_irq_by_fd(int fd) | ||
| 272 | { | ||
| 273 | free_irq_by_cb(same_fd, &fd); | ||
| 274 | } | ||
| 275 | |||
| 276 | static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out) | ||
| 277 | { | ||
| 278 | struct irq_fd *irq; | ||
| 279 | int i = 0; | ||
| 280 | |||
| 281 | for(irq=active_fds; irq != NULL; irq = irq->next){ | ||
| 282 | if((irq->fd == fd) && (irq->irq == irqnum)) break; | ||
| 283 | i++; | ||
| 284 | } | ||
| 285 | if(irq == NULL){ | ||
| 286 | printk("find_irq_by_fd doesn't have descriptor %d\n", fd); | ||
| 287 | goto out; | ||
| 288 | } | ||
| 289 | if((pollfds[i].fd != -1) && (pollfds[i].fd != fd)){ | ||
| 290 | printk("find_irq_by_fd - mismatch between active_fds and " | ||
| 291 | "pollfds, fd %d vs %d, need %d\n", irq->fd, | ||
| 292 | pollfds[i].fd, fd); | ||
| 293 | irq = NULL; | ||
| 294 | goto out; | ||
| 295 | } | ||
| 296 | *index_out = i; | ||
| 297 | out: | ||
| 298 | return(irq); | ||
| 299 | } | ||
| 300 | |||
| 301 | void reactivate_fd(int fd, int irqnum) | ||
| 302 | { | ||
| 303 | struct irq_fd *irq; | ||
| 304 | unsigned long flags; | ||
| 305 | int i; | ||
| 306 | |||
| 307 | flags = irq_lock(); | ||
| 308 | irq = find_irq_by_fd(fd, irqnum, &i); | ||
| 309 | if(irq == NULL){ | ||
| 310 | irq_unlock(flags); | ||
| 311 | return; | ||
| 312 | } | ||
| 313 | |||
| 314 | pollfds[i].fd = irq->fd; | ||
| 315 | |||
| 316 | irq_unlock(flags); | ||
| 317 | |||
| 318 | /* This calls activate_fd, so it has to be outside the critical | ||
| 319 | * section. | ||
| 320 | */ | ||
| 321 | maybe_sigio_broken(fd, irq->type); | ||
| 322 | } | ||
| 323 | |||
| 324 | void deactivate_fd(int fd, int irqnum) | ||
| 325 | { | ||
| 326 | struct irq_fd *irq; | ||
| 327 | unsigned long flags; | ||
| 328 | int i; | ||
| 329 | |||
| 330 | flags = irq_lock(); | ||
| 331 | irq = find_irq_by_fd(fd, irqnum, &i); | ||
| 332 | if(irq == NULL) | ||
| 333 | goto out; | ||
| 334 | pollfds[i].fd = -1; | ||
| 335 | out: | ||
| 336 | irq_unlock(flags); | ||
| 337 | } | ||
| 338 | |||
| 339 | int deactivate_all_fds(void) | ||
| 340 | { | ||
| 341 | struct irq_fd *irq; | ||
| 342 | int err; | ||
| 343 | |||
| 344 | for(irq=active_fds;irq != NULL;irq = irq->next){ | ||
| 345 | err = os_clear_fd_async(irq->fd); | ||
| 346 | if(err) | ||
| 347 | return(err); | ||
| 348 | } | ||
| 349 | /* If there is a signal already queued, after unblocking ignore it */ | ||
| 350 | set_handler(SIGIO, SIG_IGN, 0, -1); | ||
| 351 | |||
| 352 | return(0); | ||
| 353 | } | ||
| 354 | |||
| 355 | void forward_ipi(int fd, int pid) | ||
| 356 | { | ||
| 357 | int err; | ||
| 358 | |||
| 359 | err = os_set_owner(fd, pid); | ||
| 360 | if(err < 0) | ||
| 361 | printk("forward_ipi: set_owner failed, fd = %d, me = %d, " | ||
| 362 | "target = %d, err = %d\n", fd, os_getpid(), pid, -err); | ||
| 363 | } | ||
| 364 | |||
| 365 | void forward_interrupts(int pid) | ||
| 366 | { | ||
| 367 | struct irq_fd *irq; | ||
| 368 | unsigned long flags; | ||
| 369 | int err; | ||
| 370 | |||
| 371 | flags = irq_lock(); | ||
| 372 | for(irq=active_fds;irq != NULL;irq = irq->next){ | ||
| 373 | err = os_set_owner(irq->fd, pid); | ||
| 374 | if(err < 0){ | ||
| 375 | /* XXX Just remove the irq rather than | ||
| 376 | * print out an infinite stream of these | ||
| 377 | */ | ||
| 378 | printk("Failed to forward %d to pid %d, err = %d\n", | ||
| 379 | irq->fd, pid, -err); | ||
| 380 | } | ||
| 381 | |||
| 382 | irq->pid = pid; | ||
| 383 | } | ||
| 384 | irq_unlock(flags); | ||
| 385 | } | ||
| 386 | |||
| 387 | void init_irq_signals(int on_sigstack) | ||
| 388 | { | ||
| 389 | __sighandler_t h; | ||
| 390 | int flags; | ||
| 391 | |||
| 392 | flags = on_sigstack ? SA_ONSTACK : 0; | ||
| 393 | if(timer_irq_inited) h = (__sighandler_t) alarm_handler; | ||
| 394 | else h = boot_timer_handler; | ||
| 395 | |||
| 396 | set_handler(SIGVTALRM, h, flags | SA_RESTART, | ||
| 397 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1); | ||
| 398 | set_handler(SIGIO, (__sighandler_t) sig_handler, flags | SA_RESTART, | ||
| 399 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
| 400 | signal(SIGWINCH, SIG_IGN); | ||
| 401 | } | ||
| 402 | |||
| 403 | /* | ||
| 404 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
| 405 | * Emacs will notice this stuff at the end of the file and automatically | ||
| 406 | * adjust the settings for this buffer only. This must remain at the end | ||
| 407 | * of the file. | ||
| 408 | * --------------------------------------------------------------------------- | ||
| 409 | * Local variables: | ||
| 410 | * c-file-style: "linux" | ||
| 411 | * End: | ||
| 412 | */ | ||
diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c index 0e65340eee33..0500800df1c1 100644 --- a/arch/um/kernel/physmem.c +++ b/arch/um/kernel/physmem.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "linux/vmalloc.h" | 9 | #include "linux/vmalloc.h" |
| 10 | #include "linux/bootmem.h" | 10 | #include "linux/bootmem.h" |
| 11 | #include "linux/module.h" | 11 | #include "linux/module.h" |
| 12 | #include "linux/pfn.h" | ||
| 12 | #include "asm/types.h" | 13 | #include "asm/types.h" |
| 13 | #include "asm/pgtable.h" | 14 | #include "asm/pgtable.h" |
| 14 | #include "kern_util.h" | 15 | #include "kern_util.h" |
| @@ -316,8 +317,6 @@ void map_memory(unsigned long virt, unsigned long phys, unsigned long len, | |||
| 316 | } | 317 | } |
| 317 | } | 318 | } |
| 318 | 319 | ||
| 319 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 320 | |||
| 321 | extern int __syscall_stub_start, __binary_start; | 320 | extern int __syscall_stub_start, __binary_start; |
| 322 | 321 | ||
| 323 | void setup_physmem(unsigned long start, unsigned long reserve_end, | 322 | void setup_physmem(unsigned long start, unsigned long reserve_end, |
diff --git a/arch/um/kernel/sigio_kern.c b/arch/um/kernel/sigio_kern.c index 229988463c4c..1c1300fb1e95 100644 --- a/arch/um/kernel/sigio_kern.c +++ b/arch/um/kernel/sigio_kern.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) | 2 | * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) |
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| @@ -12,13 +12,16 @@ | |||
| 12 | #include "sigio.h" | 12 | #include "sigio.h" |
| 13 | #include "irq_user.h" | 13 | #include "irq_user.h" |
| 14 | #include "irq_kern.h" | 14 | #include "irq_kern.h" |
| 15 | #include "os.h" | ||
| 15 | 16 | ||
| 16 | /* Protected by sigio_lock() called from write_sigio_workaround */ | 17 | /* Protected by sigio_lock() called from write_sigio_workaround */ |
| 17 | static int sigio_irq_fd = -1; | 18 | static int sigio_irq_fd = -1; |
| 18 | 19 | ||
| 19 | static irqreturn_t sigio_interrupt(int irq, void *data, struct pt_regs *unused) | 20 | static irqreturn_t sigio_interrupt(int irq, void *data, struct pt_regs *unused) |
| 20 | { | 21 | { |
| 21 | read_sigio_fd(sigio_irq_fd); | 22 | char c; |
| 23 | |||
| 24 | os_read_file(sigio_irq_fd, &c, sizeof(c)); | ||
| 22 | reactivate_fd(sigio_irq_fd, SIGIO_WRITE_IRQ); | 25 | reactivate_fd(sigio_irq_fd, SIGIO_WRITE_IRQ); |
| 23 | return(IRQ_HANDLED); | 26 | return(IRQ_HANDLED); |
| 24 | } | 27 | } |
| @@ -51,6 +54,9 @@ void sigio_unlock(void) | |||
| 51 | spin_unlock(&sigio_spinlock); | 54 | spin_unlock(&sigio_spinlock); |
| 52 | } | 55 | } |
| 53 | 56 | ||
| 57 | extern void sigio_cleanup(void); | ||
| 58 | __uml_exitcall(sigio_cleanup); | ||
| 59 | |||
| 54 | /* | 60 | /* |
| 55 | * Overrides for Emacs so that we follow Linus's tabbing style. | 61 | * Overrides for Emacs so that we follow Linus's tabbing style. |
| 56 | * Emacs will notice this stuff at the end of the file and automatically | 62 | * Emacs will notice this stuff at the end of the file and automatically |
diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c index 72113b0a96e7..c8d8d0ac1a7f 100644 --- a/arch/um/kernel/smp.c +++ b/arch/um/kernel/smp.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | 2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) |
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| @@ -77,9 +77,9 @@ static int idle_proc(void *cpup) | |||
| 77 | if(err < 0) | 77 | if(err < 0) |
| 78 | panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err); | 78 | panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err); |
| 79 | 79 | ||
| 80 | activate_ipi(cpu_data[cpu].ipi_pipe[0], | 80 | os_set_fd_async(cpu_data[cpu].ipi_pipe[0], |
| 81 | current->thread.mode.tt.extern_pid); | 81 | current->thread.mode.tt.extern_pid); |
| 82 | 82 | ||
| 83 | wmb(); | 83 | wmb(); |
| 84 | if (cpu_test_and_set(cpu, cpu_callin_map)) { | 84 | if (cpu_test_and_set(cpu, cpu_callin_map)) { |
| 85 | printk("huh, CPU#%d already present??\n", cpu); | 85 | printk("huh, CPU#%d already present??\n", cpu); |
| @@ -106,7 +106,7 @@ static struct task_struct *idle_thread(int cpu) | |||
| 106 | panic("copy_process failed in idle_thread, error = %ld", | 106 | panic("copy_process failed in idle_thread, error = %ld", |
| 107 | PTR_ERR(new_task)); | 107 | PTR_ERR(new_task)); |
| 108 | 108 | ||
| 109 | cpu_tasks[cpu] = ((struct cpu_task) | 109 | cpu_tasks[cpu] = ((struct cpu_task) |
| 110 | { .pid = new_task->thread.mode.tt.extern_pid, | 110 | { .pid = new_task->thread.mode.tt.extern_pid, |
| 111 | .task = new_task } ); | 111 | .task = new_task } ); |
| 112 | idle_threads[cpu] = new_task; | 112 | idle_threads[cpu] = new_task; |
| @@ -134,12 +134,12 @@ void smp_prepare_cpus(unsigned int maxcpus) | |||
| 134 | if(err < 0) | 134 | if(err < 0) |
| 135 | panic("CPU#0 failed to create IPI pipe, errno = %d", -err); | 135 | panic("CPU#0 failed to create IPI pipe, errno = %d", -err); |
| 136 | 136 | ||
| 137 | activate_ipi(cpu_data[me].ipi_pipe[0], | 137 | os_set_fd_async(cpu_data[me].ipi_pipe[0], |
| 138 | current->thread.mode.tt.extern_pid); | 138 | current->thread.mode.tt.extern_pid); |
| 139 | 139 | ||
| 140 | for(cpu = 1; cpu < ncpus; cpu++){ | 140 | for(cpu = 1; cpu < ncpus; cpu++){ |
| 141 | printk("Booting processor %d...\n", cpu); | 141 | printk("Booting processor %d...\n", cpu); |
| 142 | 142 | ||
| 143 | idle = idle_thread(cpu); | 143 | idle = idle_thread(cpu); |
| 144 | 144 | ||
| 145 | init_idle(idle, cpu); | 145 | init_idle(idle, cpu); |
| @@ -223,7 +223,7 @@ void smp_call_function_slave(int cpu) | |||
| 223 | atomic_inc(&scf_finished); | 223 | atomic_inc(&scf_finished); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, | 226 | int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, |
| 227 | int wait) | 227 | int wait) |
| 228 | { | 228 | { |
| 229 | int cpus = num_online_cpus() - 1; | 229 | int cpus = num_online_cpus() - 1; |
diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 80c9c18aae94..7d51dd7201c3 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c | |||
| @@ -421,7 +421,7 @@ int linux_main(int argc, char **argv) | |||
| 421 | #ifndef CONFIG_HIGHMEM | 421 | #ifndef CONFIG_HIGHMEM |
| 422 | highmem = 0; | 422 | highmem = 0; |
| 423 | printf("CONFIG_HIGHMEM not enabled - physical memory shrunk " | 423 | printf("CONFIG_HIGHMEM not enabled - physical memory shrunk " |
| 424 | "to %lu bytes\n", physmem_size); | 424 | "to %Lu bytes\n", physmem_size); |
| 425 | #endif | 425 | #endif |
| 426 | } | 426 | } |
| 427 | 427 | ||
| @@ -433,8 +433,8 @@ int linux_main(int argc, char **argv) | |||
| 433 | 433 | ||
| 434 | setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); | 434 | setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); |
| 435 | if(init_maps(physmem_size, iomem_size, highmem)){ | 435 | if(init_maps(physmem_size, iomem_size, highmem)){ |
| 436 | printf("Failed to allocate mem_map for %lu bytes of physical " | 436 | printf("Failed to allocate mem_map for %Lu bytes of physical " |
| 437 | "memory and %lu bytes of highmem\n", physmem_size, | 437 | "memory and %Lu bytes of highmem\n", physmem_size, |
| 438 | highmem); | 438 | highmem); |
| 439 | exit(1); | 439 | exit(1); |
| 440 | } | 440 | } |
| @@ -477,7 +477,8 @@ static struct notifier_block panic_exit_notifier = { | |||
| 477 | 477 | ||
| 478 | void __init setup_arch(char **cmdline_p) | 478 | void __init setup_arch(char **cmdline_p) |
| 479 | { | 479 | { |
| 480 | notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); | 480 | atomic_notifier_chain_register(&panic_notifier_list, |
| 481 | &panic_exit_notifier); | ||
| 481 | paging_init(); | 482 | paging_init(); |
| 482 | strlcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); | 483 | strlcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); |
| 483 | *cmdline_p = command_line; | 484 | *cmdline_p = command_line; |
| @@ -487,8 +488,7 @@ void __init setup_arch(char **cmdline_p) | |||
| 487 | void __init check_bugs(void) | 488 | void __init check_bugs(void) |
| 488 | { | 489 | { |
| 489 | arch_check_bugs(); | 490 | arch_check_bugs(); |
| 490 | check_sigio(); | 491 | os_check_bugs(); |
| 491 | check_devanon(); | ||
| 492 | } | 492 | } |
| 493 | 493 | ||
| 494 | void apply_alternatives(struct alt_instr *start, struct alt_instr *end) | 494 | void apply_alternatives(struct alt_instr *start, struct alt_instr *end) |
diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile index 08a4e628b24c..1659386b42bb 100644 --- a/arch/um/os-Linux/Makefile +++ b/arch/um/os-Linux/Makefile | |||
| @@ -3,14 +3,17 @@ | |||
| 3 | # Licensed under the GPL | 3 | # Licensed under the GPL |
| 4 | # | 4 | # |
| 5 | 5 | ||
| 6 | obj-y = aio.o elf_aux.o file.o helper.o main.o mem.o process.o signal.o \ | 6 | obj-y = aio.o elf_aux.o file.o helper.o irq.o main.o mem.o process.o sigio.o \ |
| 7 | start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o user_syms.o \ | 7 | signal.o start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o \ |
| 8 | util.o drivers/ sys-$(SUBARCH)/ | 8 | user_syms.o util.o drivers/ sys-$(SUBARCH)/ |
| 9 | 9 | ||
| 10 | obj-$(CONFIG_MODE_SKAS) += skas/ | 10 | obj-$(CONFIG_MODE_SKAS) += skas/ |
| 11 | obj-$(CONFIG_TTY_LOG) += tty_log.o | ||
| 12 | user-objs-$(CONFIG_TTY_LOG) += tty_log.o | ||
| 11 | 13 | ||
| 12 | USER_OBJS := aio.o elf_aux.o file.o helper.o main.o mem.o process.o signal.o \ | 14 | USER_OBJS := $(user-objs-y) aio.o elf_aux.o file.o helper.o irq.o main.o mem.o \ |
| 13 | start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o util.o | 15 | process.o sigio.o signal.o start_up.o time.o trap.o tt.o tty.o \ |
| 16 | uaccess.o umid.o util.o | ||
| 14 | 17 | ||
| 15 | elf_aux.o: $(ARCH_DIR)/kernel-offsets.h | 18 | elf_aux.o: $(ARCH_DIR)/kernel-offsets.h |
| 16 | CFLAGS_elf_aux.o += -I$(objtree)/arch/um | 19 | CFLAGS_elf_aux.o += -I$(objtree)/arch/um |
diff --git a/arch/um/os-Linux/irq.c b/arch/um/os-Linux/irq.c new file mode 100644 index 000000000000..e599be423da1 --- /dev/null +++ b/arch/um/os-Linux/irq.c | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
| 3 | * Licensed under the GPL | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <stdlib.h> | ||
| 7 | #include <unistd.h> | ||
| 8 | #include <errno.h> | ||
| 9 | #include <signal.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include <sys/poll.h> | ||
| 12 | #include <sys/types.h> | ||
| 13 | #include <sys/time.h> | ||
| 14 | #include "user_util.h" | ||
| 15 | #include "kern_util.h" | ||
| 16 | #include "user.h" | ||
| 17 | #include "process.h" | ||
| 18 | #include "sigio.h" | ||
| 19 | #include "irq_user.h" | ||
| 20 | #include "os.h" | ||
| 21 | |||
| 22 | static struct pollfd *pollfds = NULL; | ||
| 23 | static int pollfds_num = 0; | ||
| 24 | static int pollfds_size = 0; | ||
| 25 | |||
| 26 | int os_waiting_for_events(struct irq_fd *active_fds) | ||
| 27 | { | ||
| 28 | struct irq_fd *irq_fd; | ||
| 29 | int i, n, err; | ||
| 30 | |||
| 31 | n = poll(pollfds, pollfds_num, 0); | ||
| 32 | if(n < 0){ | ||
| 33 | err = -errno; | ||
| 34 | if(errno != EINTR) | ||
| 35 | printk("sigio_handler: os_waiting_for_events:" | ||
| 36 | " poll returned %d, errno = %d\n", n, errno); | ||
| 37 | return err; | ||
| 38 | } | ||
| 39 | |||
| 40 | if(n == 0) | ||
| 41 | return 0; | ||
| 42 | |||
| 43 | irq_fd = active_fds; | ||
| 44 | |||
| 45 | for(i = 0; i < pollfds_num; i++){ | ||
| 46 | if(pollfds[i].revents != 0){ | ||
| 47 | irq_fd->current_events = pollfds[i].revents; | ||
| 48 | pollfds[i].fd = -1; | ||
| 49 | } | ||
| 50 | irq_fd = irq_fd->next; | ||
| 51 | } | ||
| 52 | return n; | ||
| 53 | } | ||
| 54 | |||
| 55 | int os_isatty(int fd) | ||
| 56 | { | ||
| 57 | return(isatty(fd)); | ||
| 58 | } | ||
| 59 | |||
| 60 | int os_create_pollfd(int fd, int events, void *tmp_pfd, int size_tmpfds) | ||
| 61 | { | ||
| 62 | if (pollfds_num == pollfds_size) { | ||
| 63 | if (size_tmpfds <= pollfds_size * sizeof(pollfds[0])) { | ||
| 64 | /* return min size needed for new pollfds area */ | ||
| 65 | return((pollfds_size + 1) * sizeof(pollfds[0])); | ||
| 66 | } | ||
| 67 | |||
| 68 | if(pollfds != NULL){ | ||
| 69 | memcpy(tmp_pfd, pollfds, | ||
| 70 | sizeof(pollfds[0]) * pollfds_size); | ||
| 71 | /* remove old pollfds */ | ||
| 72 | kfree(pollfds); | ||
| 73 | } | ||
| 74 | pollfds = tmp_pfd; | ||
| 75 | pollfds_size++; | ||
| 76 | } else { | ||
| 77 | /* remove not used tmp_pfd */ | ||
| 78 | if (tmp_pfd != NULL) | ||
| 79 | kfree(tmp_pfd); | ||
| 80 | } | ||
| 81 | |||
| 82 | pollfds[pollfds_num] = ((struct pollfd) { .fd = fd, | ||
| 83 | .events = events, | ||
| 84 | .revents = 0 }); | ||
| 85 | pollfds_num++; | ||
| 86 | |||
| 87 | return(0); | ||
| 88 | } | ||
| 89 | |||
| 90 | void os_free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg, | ||
| 91 | struct irq_fd *active_fds, struct irq_fd ***last_irq_ptr2) | ||
| 92 | { | ||
| 93 | struct irq_fd **prev; | ||
| 94 | int i = 0; | ||
| 95 | |||
| 96 | prev = &active_fds; | ||
| 97 | while(*prev != NULL){ | ||
| 98 | if((*test)(*prev, arg)){ | ||
| 99 | struct irq_fd *old_fd = *prev; | ||
| 100 | if((pollfds[i].fd != -1) && | ||
| 101 | (pollfds[i].fd != (*prev)->fd)){ | ||
| 102 | printk("os_free_irq_by_cb - mismatch between " | ||
| 103 | "active_fds and pollfds, fd %d vs %d\n", | ||
| 104 | (*prev)->fd, pollfds[i].fd); | ||
| 105 | goto out; | ||
| 106 | } | ||
| 107 | |||
| 108 | pollfds_num--; | ||
| 109 | |||
| 110 | /* This moves the *whole* array after pollfds[i] | ||
| 111 | * (though it doesn't spot as such)! | ||
| 112 | */ | ||
| 113 | |||
| 114 | memmove(&pollfds[i], &pollfds[i + 1], | ||
| 115 | (pollfds_num - i) * sizeof(pollfds[0])); | ||
| 116 | if(*last_irq_ptr2 == &old_fd->next) | ||
| 117 | *last_irq_ptr2 = prev; | ||
| 118 | |||
| 119 | *prev = (*prev)->next; | ||
| 120 | if(old_fd->type == IRQ_WRITE) | ||
| 121 | ignore_sigio_fd(old_fd->fd); | ||
| 122 | kfree(old_fd); | ||
| 123 | continue; | ||
| 124 | } | ||
| 125 | prev = &(*prev)->next; | ||
| 126 | i++; | ||
| 127 | } | ||
| 128 | out: | ||
| 129 | return; | ||
| 130 | } | ||
| 131 | |||
| 132 | |||
| 133 | int os_get_pollfd(int i) | ||
| 134 | { | ||
| 135 | return(pollfds[i].fd); | ||
| 136 | } | ||
| 137 | |||
| 138 | void os_set_pollfd(int i, int fd) | ||
| 139 | { | ||
| 140 | pollfds[i].fd = fd; | ||
| 141 | } | ||
| 142 | |||
| 143 | void os_set_ioignore(void) | ||
| 144 | { | ||
| 145 | set_handler(SIGIO, SIG_IGN, 0, -1); | ||
| 146 | } | ||
| 147 | |||
| 148 | void init_irq_signals(int on_sigstack) | ||
| 149 | { | ||
| 150 | __sighandler_t h; | ||
| 151 | int flags; | ||
| 152 | |||
| 153 | flags = on_sigstack ? SA_ONSTACK : 0; | ||
| 154 | if(timer_irq_inited) h = (__sighandler_t) alarm_handler; | ||
| 155 | else h = boot_timer_handler; | ||
| 156 | |||
| 157 | set_handler(SIGVTALRM, h, flags | SA_RESTART, | ||
| 158 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1); | ||
| 159 | set_handler(SIGIO, (__sighandler_t) sig_handler, flags | SA_RESTART, | ||
| 160 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
| 161 | signal(SIGWINCH, SIG_IGN); | ||
| 162 | } | ||
diff --git a/arch/um/kernel/sigio_user.c b/arch/um/os-Linux/sigio.c index f7b18e157d35..9ba942947146 100644 --- a/arch/um/kernel/sigio_user.c +++ b/arch/um/os-Linux/sigio.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | 2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) |
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| @@ -20,128 +20,7 @@ | |||
| 20 | #include "sigio.h" | 20 | #include "sigio.h" |
| 21 | #include "os.h" | 21 | #include "os.h" |
| 22 | 22 | ||
| 23 | /* Changed during early boot */ | 23 | /* Protected by sigio_lock(), also used by sigio_cleanup, which is an |
| 24 | int pty_output_sigio = 0; | ||
| 25 | int pty_close_sigio = 0; | ||
| 26 | |||
| 27 | /* Used as a flag during SIGIO testing early in boot */ | ||
| 28 | static volatile int got_sigio = 0; | ||
| 29 | |||
| 30 | void __init handler(int sig) | ||
| 31 | { | ||
| 32 | got_sigio = 1; | ||
| 33 | } | ||
| 34 | |||
| 35 | struct openpty_arg { | ||
| 36 | int master; | ||
| 37 | int slave; | ||
| 38 | int err; | ||
| 39 | }; | ||
| 40 | |||
| 41 | static void openpty_cb(void *arg) | ||
| 42 | { | ||
| 43 | struct openpty_arg *info = arg; | ||
| 44 | |||
| 45 | info->err = 0; | ||
| 46 | if(openpty(&info->master, &info->slave, NULL, NULL, NULL)) | ||
| 47 | info->err = -errno; | ||
| 48 | } | ||
| 49 | |||
| 50 | void __init check_one_sigio(void (*proc)(int, int)) | ||
| 51 | { | ||
| 52 | struct sigaction old, new; | ||
| 53 | struct openpty_arg pty = { .master = -1, .slave = -1 }; | ||
| 54 | int master, slave, err; | ||
| 55 | |||
| 56 | initial_thread_cb(openpty_cb, &pty); | ||
| 57 | if(pty.err){ | ||
| 58 | printk("openpty failed, errno = %d\n", -pty.err); | ||
| 59 | return; | ||
| 60 | } | ||
| 61 | |||
| 62 | master = pty.master; | ||
| 63 | slave = pty.slave; | ||
| 64 | |||
| 65 | if((master == -1) || (slave == -1)){ | ||
| 66 | printk("openpty failed to allocate a pty\n"); | ||
| 67 | return; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Not now, but complain so we now where we failed. */ | ||
| 71 | err = raw(master); | ||
| 72 | if (err < 0) | ||
| 73 | panic("check_sigio : __raw failed, errno = %d\n", -err); | ||
| 74 | |||
| 75 | err = os_sigio_async(master, slave); | ||
| 76 | if(err < 0) | ||
| 77 | panic("tty_fds : sigio_async failed, err = %d\n", -err); | ||
| 78 | |||
| 79 | if(sigaction(SIGIO, NULL, &old) < 0) | ||
| 80 | panic("check_sigio : sigaction 1 failed, errno = %d\n", errno); | ||
| 81 | new = old; | ||
| 82 | new.sa_handler = handler; | ||
| 83 | if(sigaction(SIGIO, &new, NULL) < 0) | ||
| 84 | panic("check_sigio : sigaction 2 failed, errno = %d\n", errno); | ||
| 85 | |||
| 86 | got_sigio = 0; | ||
| 87 | (*proc)(master, slave); | ||
| 88 | |||
| 89 | os_close_file(master); | ||
| 90 | os_close_file(slave); | ||
| 91 | |||
| 92 | if(sigaction(SIGIO, &old, NULL) < 0) | ||
| 93 | panic("check_sigio : sigaction 3 failed, errno = %d\n", errno); | ||
| 94 | } | ||
| 95 | |||
| 96 | static void tty_output(int master, int slave) | ||
| 97 | { | ||
| 98 | int n; | ||
| 99 | char buf[512]; | ||
| 100 | |||
| 101 | printk("Checking that host ptys support output SIGIO..."); | ||
| 102 | |||
| 103 | memset(buf, 0, sizeof(buf)); | ||
| 104 | |||
| 105 | while(os_write_file(master, buf, sizeof(buf)) > 0) ; | ||
| 106 | if(errno != EAGAIN) | ||
| 107 | panic("check_sigio : write failed, errno = %d\n", errno); | ||
| 108 | while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ; | ||
| 109 | |||
| 110 | if (got_sigio) { | ||
| 111 | printk("Yes\n"); | ||
| 112 | pty_output_sigio = 1; | ||
| 113 | } else if (n == -EAGAIN) { | ||
| 114 | printk("No, enabling workaround\n"); | ||
| 115 | } else { | ||
| 116 | panic("check_sigio : read failed, err = %d\n", n); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | static void tty_close(int master, int slave) | ||
| 121 | { | ||
| 122 | printk("Checking that host ptys support SIGIO on close..."); | ||
| 123 | |||
| 124 | os_close_file(slave); | ||
| 125 | if(got_sigio){ | ||
| 126 | printk("Yes\n"); | ||
| 127 | pty_close_sigio = 1; | ||
| 128 | } | ||
| 129 | else printk("No, enabling workaround\n"); | ||
| 130 | } | ||
| 131 | |||
| 132 | void __init check_sigio(void) | ||
| 133 | { | ||
| 134 | if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) && | ||
| 135 | (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){ | ||
| 136 | printk("No pseudo-terminals available - skipping pty SIGIO " | ||
| 137 | "check\n"); | ||
| 138 | return; | ||
| 139 | } | ||
| 140 | check_one_sigio(tty_output); | ||
| 141 | check_one_sigio(tty_close); | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Protected by sigio_lock(), also used by sigio_cleanup, which is an | ||
| 145 | * exitcall. | 24 | * exitcall. |
| 146 | */ | 25 | */ |
| 147 | static int write_sigio_pid = -1; | 26 | static int write_sigio_pid = -1; |
| @@ -150,8 +29,10 @@ static int write_sigio_pid = -1; | |||
| 150 | * the descriptors closed after it is killed. So, it can't see them change. | 29 | * the descriptors closed after it is killed. So, it can't see them change. |
| 151 | * On the UML side, they are changed under the sigio_lock. | 30 | * On the UML side, they are changed under the sigio_lock. |
| 152 | */ | 31 | */ |
| 153 | static int write_sigio_fds[2] = { -1, -1 }; | 32 | #define SIGIO_FDS_INIT {-1, -1} |
| 154 | static int sigio_private[2] = { -1, -1 }; | 33 | |
| 34 | static int write_sigio_fds[2] = SIGIO_FDS_INIT; | ||
| 35 | static int sigio_private[2] = SIGIO_FDS_INIT; | ||
| 155 | 36 | ||
| 156 | struct pollfds { | 37 | struct pollfds { |
| 157 | struct pollfd *poll; | 38 | struct pollfd *poll; |
| @@ -264,13 +145,13 @@ static void update_thread(void) | |||
| 264 | return; | 145 | return; |
| 265 | fail: | 146 | fail: |
| 266 | /* Critical section start */ | 147 | /* Critical section start */ |
| 267 | if(write_sigio_pid != -1) | 148 | if(write_sigio_pid != -1) |
| 268 | os_kill_process(write_sigio_pid, 1); | 149 | os_kill_process(write_sigio_pid, 1); |
| 269 | write_sigio_pid = -1; | 150 | write_sigio_pid = -1; |
| 270 | os_close_file(sigio_private[0]); | 151 | close(sigio_private[0]); |
| 271 | os_close_file(sigio_private[1]); | 152 | close(sigio_private[1]); |
| 272 | os_close_file(write_sigio_fds[0]); | 153 | close(write_sigio_fds[0]); |
| 273 | os_close_file(write_sigio_fds[1]); | 154 | close(write_sigio_fds[1]); |
| 274 | /* Critical section end */ | 155 | /* Critical section end */ |
| 275 | set_signals(flags); | 156 | set_signals(flags); |
| 276 | } | 157 | } |
| @@ -281,13 +162,13 @@ int add_sigio_fd(int fd, int read) | |||
| 281 | 162 | ||
| 282 | sigio_lock(); | 163 | sigio_lock(); |
| 283 | for(i = 0; i < current_poll.used; i++){ | 164 | for(i = 0; i < current_poll.used; i++){ |
| 284 | if(current_poll.poll[i].fd == fd) | 165 | if(current_poll.poll[i].fd == fd) |
| 285 | goto out; | 166 | goto out; |
| 286 | } | 167 | } |
| 287 | 168 | ||
| 288 | n = current_poll.used + 1; | 169 | n = current_poll.used + 1; |
| 289 | err = need_poll(n); | 170 | err = need_poll(n); |
| 290 | if(err) | 171 | if(err) |
| 291 | goto out; | 172 | goto out; |
| 292 | 173 | ||
| 293 | for(i = 0; i < current_poll.used; i++) | 174 | for(i = 0; i < current_poll.used; i++) |
| @@ -316,7 +197,7 @@ int ignore_sigio_fd(int fd) | |||
| 316 | } | 197 | } |
| 317 | if(i == current_poll.used) | 198 | if(i == current_poll.used) |
| 318 | goto out; | 199 | goto out; |
| 319 | 200 | ||
| 320 | err = need_poll(current_poll.used - 1); | 201 | err = need_poll(current_poll.used - 1); |
| 321 | if(err) | 202 | if(err) |
| 322 | goto out; | 203 | goto out; |
| @@ -337,7 +218,7 @@ int ignore_sigio_fd(int fd) | |||
| 337 | return(err); | 218 | return(err); |
| 338 | } | 219 | } |
| 339 | 220 | ||
| 340 | static struct pollfd* setup_initial_poll(int fd) | 221 | static struct pollfd *setup_initial_poll(int fd) |
| 341 | { | 222 | { |
| 342 | struct pollfd *p; | 223 | struct pollfd *p; |
| 343 | 224 | ||
| @@ -377,7 +258,7 @@ void write_sigio_workaround(void) | |||
| 377 | } | 258 | } |
| 378 | err = os_pipe(l_sigio_private, 1, 1); | 259 | err = os_pipe(l_sigio_private, 1, 1); |
| 379 | if(err < 0){ | 260 | if(err < 0){ |
| 380 | printk("write_sigio_workaround - os_pipe 1 failed, " | 261 | printk("write_sigio_workaround - os_pipe 2 failed, " |
| 381 | "err = %d\n", -err); | 262 | "err = %d\n", -err); |
| 382 | goto out_close1; | 263 | goto out_close1; |
| 383 | } | 264 | } |
| @@ -391,76 +272,52 @@ void write_sigio_workaround(void) | |||
| 391 | /* Did we race? Don't try to optimize this, please, it's not so likely | 272 | /* Did we race? Don't try to optimize this, please, it's not so likely |
| 392 | * to happen, and no more than once at the boot. */ | 273 | * to happen, and no more than once at the boot. */ |
| 393 | if(write_sigio_pid != -1) | 274 | if(write_sigio_pid != -1) |
| 394 | goto out_unlock; | 275 | goto out_free; |
| 395 | 276 | ||
| 396 | write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, | 277 | current_poll = ((struct pollfds) { .poll = p, |
| 397 | CLONE_FILES | CLONE_VM, &stack, 0); | 278 | .used = 1, |
| 398 | 279 | .size = 1 }); | |
| 399 | if (write_sigio_pid < 0) | ||
| 400 | goto out_clear; | ||
| 401 | 280 | ||
| 402 | if (write_sigio_irq(l_write_sigio_fds[0])) | 281 | if (write_sigio_irq(l_write_sigio_fds[0])) |
| 403 | goto out_kill; | 282 | goto out_clear_poll; |
| 404 | 283 | ||
| 405 | /* Success, finally. */ | ||
| 406 | memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds)); | 284 | memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds)); |
| 407 | memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private)); | 285 | memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private)); |
| 408 | 286 | ||
| 409 | current_poll = ((struct pollfds) { .poll = p, | 287 | write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, |
| 410 | .used = 1, | 288 | CLONE_FILES | CLONE_VM, &stack, 0); |
| 411 | .size = 1 }); | ||
| 412 | 289 | ||
| 413 | sigio_unlock(); | 290 | if (write_sigio_pid < 0) |
| 414 | return; | 291 | goto out_clear; |
| 415 | 292 | ||
| 416 | out_kill: | ||
| 417 | l_write_sigio_pid = write_sigio_pid; | ||
| 418 | write_sigio_pid = -1; | ||
| 419 | sigio_unlock(); | 293 | sigio_unlock(); |
| 420 | /* Going to call waitpid, avoid holding the lock. */ | 294 | return; |
| 421 | os_kill_process(l_write_sigio_pid, 1); | ||
| 422 | goto out_free; | ||
| 423 | 295 | ||
| 424 | out_clear: | 296 | out_clear: |
| 425 | write_sigio_pid = -1; | 297 | write_sigio_pid = -1; |
| 426 | out_unlock: | 298 | write_sigio_fds[0] = -1; |
| 427 | sigio_unlock(); | 299 | write_sigio_fds[1] = -1; |
| 428 | out_free: | 300 | sigio_private[0] = -1; |
| 301 | sigio_private[1] = -1; | ||
| 302 | out_clear_poll: | ||
| 303 | current_poll = ((struct pollfds) { .poll = NULL, | ||
| 304 | .size = 0, | ||
| 305 | .used = 0 }); | ||
| 306 | out_free: | ||
| 429 | kfree(p); | 307 | kfree(p); |
| 430 | out_close2: | 308 | sigio_unlock(); |
| 431 | os_close_file(l_sigio_private[0]); | 309 | out_close2: |
| 432 | os_close_file(l_sigio_private[1]); | 310 | close(l_sigio_private[0]); |
| 433 | out_close1: | 311 | close(l_sigio_private[1]); |
| 434 | os_close_file(l_write_sigio_fds[0]); | 312 | out_close1: |
| 435 | os_close_file(l_write_sigio_fds[1]); | 313 | close(l_write_sigio_fds[0]); |
| 436 | return; | 314 | close(l_write_sigio_fds[1]); |
| 437 | } | ||
| 438 | |||
| 439 | int read_sigio_fd(int fd) | ||
| 440 | { | ||
| 441 | int n; | ||
| 442 | char c; | ||
| 443 | |||
| 444 | n = os_read_file(fd, &c, sizeof(c)); | ||
| 445 | if(n != sizeof(c)){ | ||
| 446 | if(n < 0) { | ||
| 447 | printk("read_sigio_fd - read failed, err = %d\n", -n); | ||
| 448 | return(n); | ||
| 449 | } | ||
| 450 | else { | ||
| 451 | printk("read_sigio_fd - short read, bytes = %d\n", n); | ||
| 452 | return(-EIO); | ||
| 453 | } | ||
| 454 | } | ||
| 455 | return(n); | ||
| 456 | } | 315 | } |
| 457 | 316 | ||
| 458 | static void sigio_cleanup(void) | 317 | void sigio_cleanup(void) |
| 459 | { | 318 | { |
| 460 | if (write_sigio_pid != -1) { | 319 | if(write_sigio_pid != -1){ |
| 461 | os_kill_process(write_sigio_pid, 1); | 320 | os_kill_process(write_sigio_pid, 1); |
| 462 | write_sigio_pid = -1; | 321 | write_sigio_pid = -1; |
| 463 | } | 322 | } |
| 464 | } | 323 | } |
| 465 | |||
| 466 | __uml_exitcall(sigio_cleanup); | ||
diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c index 829d6b0d8b02..32753131f8d8 100644 --- a/arch/um/os-Linux/start_up.c +++ b/arch/um/os-Linux/start_up.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | #include <pty.h> | ||
| 6 | #include <stdio.h> | 7 | #include <stdio.h> |
| 7 | #include <stddef.h> | 8 | #include <stddef.h> |
| 8 | #include <stdarg.h> | 9 | #include <stdarg.h> |
| @@ -539,3 +540,130 @@ int __init parse_iomem(char *str, int *add) | |||
| 539 | return(1); | 540 | return(1); |
| 540 | } | 541 | } |
| 541 | 542 | ||
| 543 | |||
| 544 | /* Changed during early boot */ | ||
| 545 | int pty_output_sigio = 0; | ||
| 546 | int pty_close_sigio = 0; | ||
| 547 | |||
| 548 | /* Used as a flag during SIGIO testing early in boot */ | ||
| 549 | static volatile int got_sigio = 0; | ||
| 550 | |||
| 551 | static void __init handler(int sig) | ||
| 552 | { | ||
| 553 | got_sigio = 1; | ||
| 554 | } | ||
| 555 | |||
| 556 | struct openpty_arg { | ||
| 557 | int master; | ||
| 558 | int slave; | ||
| 559 | int err; | ||
| 560 | }; | ||
| 561 | |||
| 562 | static void openpty_cb(void *arg) | ||
| 563 | { | ||
| 564 | struct openpty_arg *info = arg; | ||
| 565 | |||
| 566 | info->err = 0; | ||
| 567 | if(openpty(&info->master, &info->slave, NULL, NULL, NULL)) | ||
| 568 | info->err = -errno; | ||
| 569 | } | ||
| 570 | |||
| 571 | static void __init check_one_sigio(void (*proc)(int, int)) | ||
| 572 | { | ||
| 573 | struct sigaction old, new; | ||
| 574 | struct openpty_arg pty = { .master = -1, .slave = -1 }; | ||
| 575 | int master, slave, err; | ||
| 576 | |||
| 577 | initial_thread_cb(openpty_cb, &pty); | ||
| 578 | if(pty.err){ | ||
| 579 | printk("openpty failed, errno = %d\n", -pty.err); | ||
| 580 | return; | ||
| 581 | } | ||
| 582 | |||
| 583 | master = pty.master; | ||
| 584 | slave = pty.slave; | ||
| 585 | |||
| 586 | if((master == -1) || (slave == -1)){ | ||
| 587 | printk("openpty failed to allocate a pty\n"); | ||
| 588 | return; | ||
| 589 | } | ||
| 590 | |||
| 591 | /* Not now, but complain so we now where we failed. */ | ||
| 592 | err = raw(master); | ||
| 593 | if (err < 0) | ||
| 594 | panic("check_sigio : __raw failed, errno = %d\n", -err); | ||
| 595 | |||
| 596 | err = os_sigio_async(master, slave); | ||
| 597 | if(err < 0) | ||
| 598 | panic("tty_fds : sigio_async failed, err = %d\n", -err); | ||
| 599 | |||
| 600 | if(sigaction(SIGIO, NULL, &old) < 0) | ||
| 601 | panic("check_sigio : sigaction 1 failed, errno = %d\n", errno); | ||
| 602 | new = old; | ||
| 603 | new.sa_handler = handler; | ||
| 604 | if(sigaction(SIGIO, &new, NULL) < 0) | ||
| 605 | panic("check_sigio : sigaction 2 failed, errno = %d\n", errno); | ||
| 606 | |||
| 607 | got_sigio = 0; | ||
| 608 | (*proc)(master, slave); | ||
| 609 | |||
| 610 | close(master); | ||
| 611 | close(slave); | ||
| 612 | |||
| 613 | if(sigaction(SIGIO, &old, NULL) < 0) | ||
| 614 | panic("check_sigio : sigaction 3 failed, errno = %d\n", errno); | ||
| 615 | } | ||
| 616 | |||
| 617 | static void tty_output(int master, int slave) | ||
| 618 | { | ||
| 619 | int n; | ||
| 620 | char buf[512]; | ||
| 621 | |||
| 622 | printk("Checking that host ptys support output SIGIO..."); | ||
| 623 | |||
| 624 | memset(buf, 0, sizeof(buf)); | ||
| 625 | |||
| 626 | while(os_write_file(master, buf, sizeof(buf)) > 0) ; | ||
| 627 | if(errno != EAGAIN) | ||
| 628 | panic("check_sigio : write failed, errno = %d\n", errno); | ||
| 629 | while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ; | ||
| 630 | |||
| 631 | if(got_sigio){ | ||
| 632 | printk("Yes\n"); | ||
| 633 | pty_output_sigio = 1; | ||
| 634 | } | ||
| 635 | else if(n == -EAGAIN) printk("No, enabling workaround\n"); | ||
| 636 | else panic("check_sigio : read failed, err = %d\n", n); | ||
| 637 | } | ||
| 638 | |||
| 639 | static void tty_close(int master, int slave) | ||
| 640 | { | ||
| 641 | printk("Checking that host ptys support SIGIO on close..."); | ||
| 642 | |||
| 643 | close(slave); | ||
| 644 | if(got_sigio){ | ||
| 645 | printk("Yes\n"); | ||
| 646 | pty_close_sigio = 1; | ||
| 647 | } | ||
| 648 | else printk("No, enabling workaround\n"); | ||
| 649 | } | ||
| 650 | |||
| 651 | void __init check_sigio(void) | ||
| 652 | { | ||
| 653 | if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) && | ||
| 654 | (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){ | ||
| 655 | printk("No pseudo-terminals available - skipping pty SIGIO " | ||
| 656 | "check\n"); | ||
| 657 | return; | ||
| 658 | } | ||
| 659 | check_one_sigio(tty_output); | ||
| 660 | check_one_sigio(tty_close); | ||
| 661 | } | ||
| 662 | |||
| 663 | void os_check_bugs(void) | ||
| 664 | { | ||
| 665 | check_ptrace(); | ||
| 666 | check_sigio(); | ||
| 667 | check_devanon(); | ||
| 668 | } | ||
| 669 | |||
diff --git a/arch/um/os-Linux/tt.c b/arch/um/os-Linux/tt.c index 919d19f11537..5461a065bbb9 100644 --- a/arch/um/os-Linux/tt.c +++ b/arch/um/os-Linux/tt.c | |||
| @@ -110,6 +110,16 @@ int wait_for_stop(int pid, int sig, int cont_type, void *relay) | |||
| 110 | } | 110 | } |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | void forward_ipi(int fd, int pid) | ||
| 114 | { | ||
| 115 | int err; | ||
| 116 | |||
| 117 | err = os_set_owner(fd, pid); | ||
| 118 | if(err < 0) | ||
| 119 | printk("forward_ipi: set_owner failed, fd = %d, me = %d, " | ||
| 120 | "target = %d, err = %d\n", fd, os_getpid(), pid, -err); | ||
| 121 | } | ||
| 122 | |||
| 113 | /* | 123 | /* |
| 114 | *------------------------- | 124 | *------------------------- |
| 115 | * only for tt mode (will be deleted in future...) | 125 | * only for tt mode (will be deleted in future...) |
diff --git a/arch/um/kernel/tty_log.c b/arch/um/os-Linux/tty_log.c index 9ada656f68ce..c6ba56c1560f 100644 --- a/arch/um/kernel/tty_log.c +++ b/arch/um/os-Linux/tty_log.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) and | 2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) and |
| 3 | * geoffrey hing <ghing@net.ohio-state.edu> | 3 | * geoffrey hing <ghing@net.ohio-state.edu> |
| 4 | * Licensed under the GPL | 4 | * Licensed under the GPL |
| 5 | */ | 5 | */ |
| @@ -58,7 +58,7 @@ int open_tty_log(void *tty, void *current_tty) | |||
| 58 | return(tty_log_fd); | 58 | return(tty_log_fd); |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec, | 61 | sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec, |
| 62 | (unsigned int) tv.tv_usec); | 62 | (unsigned int) tv.tv_usec); |
| 63 | 63 | ||
| 64 | fd = os_open_file(buf, of_append(of_create(of_rdwr(OPENFLAGS()))), | 64 | fd = os_open_file(buf, of_append(of_create(of_rdwr(OPENFLAGS()))), |
| @@ -216,15 +216,3 @@ __uml_setup("tty_log_fd=", set_tty_log_fd, | |||
| 216 | " tty data will be written. Preconfigure the descriptor with something\n" | 216 | " tty data will be written. Preconfigure the descriptor with something\n" |
| 217 | " like '10>tty_log tty_log_fd=10'.\n\n" | 217 | " like '10>tty_log tty_log_fd=10'.\n\n" |
| 218 | ); | 218 | ); |
| 219 | |||
| 220 | |||
| 221 | /* | ||
| 222 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
| 223 | * Emacs will notice this stuff at the end of the file and automatically | ||
| 224 | * adjust the settings for this buffer only. This must remain at the end | ||
| 225 | * of the file. | ||
| 226 | * --------------------------------------------------------------------------- | ||
| 227 | * Local variables: | ||
| 228 | * c-file-style: "linux" | ||
| 229 | * End: | ||
| 230 | */ | ||
diff --git a/arch/um/os-Linux/umid.c b/arch/um/os-Linux/umid.c index ecf107ae5ac8..198e59163288 100644 --- a/arch/um/os-Linux/umid.c +++ b/arch/um/os-Linux/umid.c | |||
| @@ -143,8 +143,10 @@ static int not_dead_yet(char *dir) | |||
| 143 | goto out_close; | 143 | goto out_close; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | if((kill(p, 0) == 0) || (errno != ESRCH)) | 146 | if((kill(p, 0) == 0) || (errno != ESRCH)){ |
| 147 | printk("umid \"%s\" is already in use by pid %d\n", umid, p); | ||
| 147 | return 1; | 148 | return 1; |
| 149 | } | ||
| 148 | 150 | ||
| 149 | err = actually_do_remove(dir); | 151 | err = actually_do_remove(dir); |
| 150 | if(err) | 152 | if(err) |
| @@ -234,33 +236,44 @@ int __init make_umid(void) | |||
| 234 | err = mkdir(tmp, 0777); | 236 | err = mkdir(tmp, 0777); |
| 235 | if(err < 0){ | 237 | if(err < 0){ |
| 236 | err = -errno; | 238 | err = -errno; |
| 237 | if(errno != EEXIST) | 239 | if(err != -EEXIST) |
| 238 | goto err; | 240 | goto err; |
| 239 | 241 | ||
| 240 | if(not_dead_yet(tmp) < 0) | 242 | /* 1 -> this umid is already in use |
| 243 | * < 0 -> we couldn't remove the umid directory | ||
| 244 | * In either case, we can't use this umid, so return -EEXIST. | ||
| 245 | */ | ||
| 246 | if(not_dead_yet(tmp) != 0) | ||
| 241 | goto err; | 247 | goto err; |
| 242 | 248 | ||
| 243 | err = mkdir(tmp, 0777); | 249 | err = mkdir(tmp, 0777); |
| 244 | } | 250 | } |
| 245 | if(err < 0){ | 251 | if(err){ |
| 246 | printk("Failed to create '%s' - err = %d\n", umid, err); | 252 | err = -errno; |
| 247 | goto err_rmdir; | 253 | printk("Failed to create '%s' - err = %d\n", umid, -errno); |
| 254 | goto err; | ||
| 248 | } | 255 | } |
| 249 | 256 | ||
| 250 | umid_setup = 1; | 257 | umid_setup = 1; |
| 251 | 258 | ||
| 252 | create_pid_file(); | 259 | create_pid_file(); |
| 253 | 260 | ||
| 254 | return 0; | 261 | err = 0; |
| 255 | |||
| 256 | err_rmdir: | ||
| 257 | rmdir(tmp); | ||
| 258 | err: | 262 | err: |
| 259 | return err; | 263 | return err; |
| 260 | } | 264 | } |
| 261 | 265 | ||
| 262 | static int __init make_umid_init(void) | 266 | static int __init make_umid_init(void) |
| 263 | { | 267 | { |
| 268 | if(!make_umid()) | ||
| 269 | return 0; | ||
| 270 | |||
| 271 | /* If initializing with the given umid failed, then try again with | ||
| 272 | * a random one. | ||
| 273 | */ | ||
| 274 | printk("Failed to initialize umid \"%s\", trying with a random umid\n", | ||
| 275 | umid); | ||
| 276 | *umid = '\0'; | ||
| 264 | make_umid(); | 277 | make_umid(); |
| 265 | 278 | ||
| 266 | return 0; | 279 | return 0; |
diff --git a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c index e839ce65ad28..8032a105949a 100644 --- a/arch/um/sys-i386/ptrace.c +++ b/arch/um/sys-i386/ptrace.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <linux/config.h> | 6 | #include <linux/config.h> |
| 7 | #include <linux/compiler.h> | 7 | #include <linux/compiler.h> |
| 8 | #include "linux/sched.h" | 8 | #include "linux/sched.h" |
| 9 | #include "linux/mm.h" | ||
| 9 | #include "asm/elf.h" | 10 | #include "asm/elf.h" |
| 10 | #include "asm/ptrace.h" | 11 | #include "asm/ptrace.h" |
| 11 | #include "asm/uaccess.h" | 12 | #include "asm/uaccess.h" |
| @@ -26,9 +27,17 @@ int is_syscall(unsigned long addr) | |||
| 26 | 27 | ||
| 27 | n = copy_from_user(&instr, (void __user *) addr, sizeof(instr)); | 28 | n = copy_from_user(&instr, (void __user *) addr, sizeof(instr)); |
| 28 | if(n){ | 29 | if(n){ |
| 29 | printk("is_syscall : failed to read instruction from 0x%lx\n", | 30 | /* access_process_vm() grants access to vsyscall and stub, |
| 30 | addr); | 31 | * while copy_from_user doesn't. Maybe access_process_vm is |
| 31 | return(0); | 32 | * slow, but that doesn't matter, since it will be called only |
| 33 | * in case of singlestepping, if copy_from_user failed. | ||
| 34 | */ | ||
| 35 | n = access_process_vm(current, addr, &instr, sizeof(instr), 0); | ||
| 36 | if(n != sizeof(instr)) { | ||
| 37 | printk("is_syscall : failed to read instruction from " | ||
| 38 | "0x%lx\n", addr); | ||
| 39 | return(1); | ||
| 40 | } | ||
| 32 | } | 41 | } |
| 33 | /* int 0x80 or sysenter */ | 42 | /* int 0x80 or sysenter */ |
| 34 | return((instr == 0x80cd) || (instr == 0x340f)); | 43 | return((instr == 0x80cd) || (instr == 0x340f)); |
diff --git a/arch/um/sys-i386/signal.c b/arch/um/sys-i386/signal.c index 7cd1a82dc8c2..33a40f5ef0d2 100644 --- a/arch/um/sys-i386/signal.c +++ b/arch/um/sys-i386/signal.c | |||
| @@ -58,7 +58,7 @@ static int copy_sc_from_user_skas(struct pt_regs *regs, | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, | 60 | int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, |
| 61 | struct pt_regs *regs) | 61 | struct pt_regs *regs, unsigned long sp) |
| 62 | { | 62 | { |
| 63 | struct sigcontext sc; | 63 | struct sigcontext sc; |
| 64 | unsigned long fpregs[HOST_FP_SIZE]; | 64 | unsigned long fpregs[HOST_FP_SIZE]; |
| @@ -72,7 +72,7 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, | |||
| 72 | sc.edi = REGS_EDI(regs->regs.skas.regs); | 72 | sc.edi = REGS_EDI(regs->regs.skas.regs); |
| 73 | sc.esi = REGS_ESI(regs->regs.skas.regs); | 73 | sc.esi = REGS_ESI(regs->regs.skas.regs); |
| 74 | sc.ebp = REGS_EBP(regs->regs.skas.regs); | 74 | sc.ebp = REGS_EBP(regs->regs.skas.regs); |
| 75 | sc.esp = REGS_SP(regs->regs.skas.regs); | 75 | sc.esp = sp; |
| 76 | sc.ebx = REGS_EBX(regs->regs.skas.regs); | 76 | sc.ebx = REGS_EBX(regs->regs.skas.regs); |
| 77 | sc.edx = REGS_EDX(regs->regs.skas.regs); | 77 | sc.edx = REGS_EDX(regs->regs.skas.regs); |
| 78 | sc.ecx = REGS_ECX(regs->regs.skas.regs); | 78 | sc.ecx = REGS_ECX(regs->regs.skas.regs); |
| @@ -132,7 +132,7 @@ int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, | |||
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, | 134 | int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, |
| 135 | struct sigcontext *from, int fpsize) | 135 | struct sigcontext *from, int fpsize, unsigned long sp) |
| 136 | { | 136 | { |
| 137 | struct _fpstate *to_fp, *from_fp; | 137 | struct _fpstate *to_fp, *from_fp; |
| 138 | int err; | 138 | int err; |
| @@ -140,11 +140,18 @@ int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, | |||
| 140 | to_fp = (fp ? fp : (struct _fpstate *) (to + 1)); | 140 | to_fp = (fp ? fp : (struct _fpstate *) (to + 1)); |
| 141 | from_fp = from->fpstate; | 141 | from_fp = from->fpstate; |
| 142 | err = copy_to_user(to, from, sizeof(*to)); | 142 | err = copy_to_user(to, from, sizeof(*to)); |
| 143 | |||
| 144 | /* The SP in the sigcontext is the updated one for the signal | ||
| 145 | * delivery. The sp passed in is the original, and this needs | ||
| 146 | * to be restored, so we stick it in separately. | ||
| 147 | */ | ||
| 148 | err |= copy_to_user(&SC_SP(to), sp, sizeof(sp)); | ||
| 149 | |||
| 143 | if(from_fp != NULL){ | 150 | if(from_fp != NULL){ |
| 144 | err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate)); | 151 | err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate)); |
| 145 | err |= copy_to_user(to_fp, from_fp, fpsize); | 152 | err |= copy_to_user(to_fp, from_fp, fpsize); |
| 146 | } | 153 | } |
| 147 | return(err); | 154 | return err; |
| 148 | } | 155 | } |
| 149 | #endif | 156 | #endif |
| 150 | 157 | ||
| @@ -159,11 +166,11 @@ static int copy_sc_from_user(struct pt_regs *to, void __user *from) | |||
| 159 | } | 166 | } |
| 160 | 167 | ||
| 161 | static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, | 168 | static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, |
| 162 | struct pt_regs *from) | 169 | struct pt_regs *from, unsigned long sp) |
| 163 | { | 170 | { |
| 164 | return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), | 171 | return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), |
| 165 | sizeof(*fp)), | 172 | sizeof(*fp), sp), |
| 166 | copy_sc_to_user_skas(to, fp, from))); | 173 | copy_sc_to_user_skas(to, fp, from, sp))); |
| 167 | } | 174 | } |
| 168 | 175 | ||
| 169 | static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, | 176 | static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, |
| @@ -174,7 +181,7 @@ static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, | |||
| 174 | err |= put_user(current->sas_ss_sp, &uc->uc_stack.ss_sp); | 181 | err |= put_user(current->sas_ss_sp, &uc->uc_stack.ss_sp); |
| 175 | err |= put_user(sas_ss_flags(sp), &uc->uc_stack.ss_flags); | 182 | err |= put_user(sas_ss_flags(sp), &uc->uc_stack.ss_flags); |
| 176 | err |= put_user(current->sas_ss_size, &uc->uc_stack.ss_size); | 183 | err |= put_user(current->sas_ss_size, &uc->uc_stack.ss_size); |
| 177 | err |= copy_sc_to_user(&uc->uc_mcontext, fp, ¤t->thread.regs); | 184 | err |= copy_sc_to_user(&uc->uc_mcontext, fp, ¤t->thread.regs, sp); |
| 178 | err |= copy_to_user(&uc->uc_sigmask, set, sizeof(*set)); | 185 | err |= copy_to_user(&uc->uc_sigmask, set, sizeof(*set)); |
| 179 | return(err); | 186 | return(err); |
| 180 | } | 187 | } |
| @@ -207,6 +214,7 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, | |||
| 207 | { | 214 | { |
| 208 | struct sigframe __user *frame; | 215 | struct sigframe __user *frame; |
| 209 | void *restorer; | 216 | void *restorer; |
| 217 | unsigned long save_sp = PT_REGS_SP(regs); | ||
| 210 | int err = 0; | 218 | int err = 0; |
| 211 | 219 | ||
| 212 | stack_top &= -8UL; | 220 | stack_top &= -8UL; |
| @@ -218,9 +226,19 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, | |||
| 218 | if(ka->sa.sa_flags & SA_RESTORER) | 226 | if(ka->sa.sa_flags & SA_RESTORER) |
| 219 | restorer = ka->sa.sa_restorer; | 227 | restorer = ka->sa.sa_restorer; |
| 220 | 228 | ||
| 229 | /* Update SP now because the page fault handler refuses to extend | ||
| 230 | * the stack if the faulting address is too far below the current | ||
| 231 | * SP, which frame now certainly is. If there's an error, the original | ||
| 232 | * value is restored on the way out. | ||
| 233 | * When writing the sigcontext to the stack, we have to write the | ||
| 234 | * original value, so that's passed to copy_sc_to_user, which does | ||
| 235 | * the right thing with it. | ||
| 236 | */ | ||
| 237 | PT_REGS_SP(regs) = (unsigned long) frame; | ||
| 238 | |||
| 221 | err |= __put_user(restorer, &frame->pretcode); | 239 | err |= __put_user(restorer, &frame->pretcode); |
| 222 | err |= __put_user(sig, &frame->sig); | 240 | err |= __put_user(sig, &frame->sig); |
| 223 | err |= copy_sc_to_user(&frame->sc, NULL, regs); | 241 | err |= copy_sc_to_user(&frame->sc, NULL, regs, save_sp); |
| 224 | err |= __put_user(mask->sig[0], &frame->sc.oldmask); | 242 | err |= __put_user(mask->sig[0], &frame->sc.oldmask); |
| 225 | if (_NSIG_WORDS > 1) | 243 | if (_NSIG_WORDS > 1) |
| 226 | err |= __copy_to_user(&frame->extramask, &mask->sig[1], | 244 | err |= __copy_to_user(&frame->extramask, &mask->sig[1], |
| @@ -238,7 +256,7 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, | |||
| 238 | err |= __put_user(0x80cd, (short __user *)(frame->retcode+6)); | 256 | err |= __put_user(0x80cd, (short __user *)(frame->retcode+6)); |
| 239 | 257 | ||
| 240 | if(err) | 258 | if(err) |
| 241 | return(err); | 259 | goto err; |
| 242 | 260 | ||
| 243 | PT_REGS_SP(regs) = (unsigned long) frame; | 261 | PT_REGS_SP(regs) = (unsigned long) frame; |
| 244 | PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler; | 262 | PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler; |
| @@ -248,7 +266,11 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, | |||
| 248 | 266 | ||
| 249 | if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED)) | 267 | if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED)) |
| 250 | ptrace_notify(SIGTRAP); | 268 | ptrace_notify(SIGTRAP); |
| 251 | return(0); | 269 | return 0; |
| 270 | |||
| 271 | err: | ||
| 272 | PT_REGS_SP(regs) = save_sp; | ||
| 273 | return err; | ||
| 252 | } | 274 | } |
| 253 | 275 | ||
| 254 | int setup_signal_stack_si(unsigned long stack_top, int sig, | 276 | int setup_signal_stack_si(unsigned long stack_top, int sig, |
| @@ -257,6 +279,7 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
| 257 | { | 279 | { |
| 258 | struct rt_sigframe __user *frame; | 280 | struct rt_sigframe __user *frame; |
| 259 | void *restorer; | 281 | void *restorer; |
| 282 | unsigned long save_sp = PT_REGS_SP(regs); | ||
| 260 | int err = 0; | 283 | int err = 0; |
| 261 | 284 | ||
| 262 | stack_top &= -8UL; | 285 | stack_top &= -8UL; |
| @@ -268,13 +291,16 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
| 268 | if(ka->sa.sa_flags & SA_RESTORER) | 291 | if(ka->sa.sa_flags & SA_RESTORER) |
| 269 | restorer = ka->sa.sa_restorer; | 292 | restorer = ka->sa.sa_restorer; |
| 270 | 293 | ||
| 294 | /* See comment above about why this is here */ | ||
| 295 | PT_REGS_SP(regs) = (unsigned long) frame; | ||
| 296 | |||
| 271 | err |= __put_user(restorer, &frame->pretcode); | 297 | err |= __put_user(restorer, &frame->pretcode); |
| 272 | err |= __put_user(sig, &frame->sig); | 298 | err |= __put_user(sig, &frame->sig); |
| 273 | err |= __put_user(&frame->info, &frame->pinfo); | 299 | err |= __put_user(&frame->info, &frame->pinfo); |
| 274 | err |= __put_user(&frame->uc, &frame->puc); | 300 | err |= __put_user(&frame->uc, &frame->puc); |
| 275 | err |= copy_siginfo_to_user(&frame->info, info); | 301 | err |= copy_siginfo_to_user(&frame->info, info); |
| 276 | err |= copy_ucontext_to_user(&frame->uc, &frame->fpstate, mask, | 302 | err |= copy_ucontext_to_user(&frame->uc, &frame->fpstate, mask, |
| 277 | PT_REGS_SP(regs)); | 303 | save_sp); |
| 278 | 304 | ||
| 279 | /* | 305 | /* |
| 280 | * This is movl $,%eax ; int $0x80 | 306 | * This is movl $,%eax ; int $0x80 |
| @@ -288,9 +314,8 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
| 288 | err |= __put_user(0x80cd, (short __user *)(frame->retcode+5)); | 314 | err |= __put_user(0x80cd, (short __user *)(frame->retcode+5)); |
| 289 | 315 | ||
| 290 | if(err) | 316 | if(err) |
| 291 | return(err); | 317 | goto err; |
| 292 | 318 | ||
| 293 | PT_REGS_SP(regs) = (unsigned long) frame; | ||
| 294 | PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler; | 319 | PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler; |
| 295 | PT_REGS_EAX(regs) = (unsigned long) sig; | 320 | PT_REGS_EAX(regs) = (unsigned long) sig; |
| 296 | PT_REGS_EDX(regs) = (unsigned long) &frame->info; | 321 | PT_REGS_EDX(regs) = (unsigned long) &frame->info; |
| @@ -298,7 +323,11 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
| 298 | 323 | ||
| 299 | if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED)) | 324 | if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED)) |
| 300 | ptrace_notify(SIGTRAP); | 325 | ptrace_notify(SIGTRAP); |
| 301 | return(0); | 326 | return 0; |
| 327 | |||
| 328 | err: | ||
| 329 | PT_REGS_SP(regs) = save_sp; | ||
| 330 | return err; | ||
| 302 | } | 331 | } |
| 303 | 332 | ||
| 304 | long sys_sigreturn(struct pt_regs regs) | 333 | long sys_sigreturn(struct pt_regs regs) |
diff --git a/arch/um/sys-i386/user-offsets.c b/arch/um/sys-i386/user-offsets.c index 26b68675053d..6f4ef2b7fa4a 100644 --- a/arch/um/sys-i386/user-offsets.c +++ b/arch/um/sys-i386/user-offsets.c | |||
| @@ -3,12 +3,13 @@ | |||
| 3 | #include <asm/ptrace.h> | 3 | #include <asm/ptrace.h> |
| 4 | #include <asm/user.h> | 4 | #include <asm/user.h> |
| 5 | #include <linux/stddef.h> | 5 | #include <linux/stddef.h> |
| 6 | #include <sys/poll.h> | ||
| 6 | 7 | ||
| 7 | #define DEFINE(sym, val) \ | 8 | #define DEFINE(sym, val) \ |
| 8 | asm volatile("\n->" #sym " %0 " #val : : "i" (val)) | 9 | asm volatile("\n->" #sym " %0 " #val : : "i" (val)) |
| 9 | 10 | ||
| 10 | #define DEFINE_LONGS(sym, val) \ | 11 | #define DEFINE_LONGS(sym, val) \ |
| 11 | asm volatile("\n->" #sym " %0 " #val : : "i" (val/sizeof(unsigned long))) | 12 | asm volatile("\n->" #sym " %0 " #val : : "i" (val/sizeof(unsigned long))) |
| 12 | 13 | ||
| 13 | #define OFFSET(sym, str, mem) \ | 14 | #define OFFSET(sym, str, mem) \ |
| 14 | DEFINE(sym, offsetof(struct str, mem)); | 15 | DEFINE(sym, offsetof(struct str, mem)); |
| @@ -67,4 +68,9 @@ void foo(void) | |||
| 67 | DEFINE(HOST_ES, ES); | 68 | DEFINE(HOST_ES, ES); |
| 68 | DEFINE(HOST_GS, GS); | 69 | DEFINE(HOST_GS, GS); |
| 69 | DEFINE(UM_FRAME_SIZE, sizeof(struct user_regs_struct)); | 70 | DEFINE(UM_FRAME_SIZE, sizeof(struct user_regs_struct)); |
| 71 | |||
| 72 | /* XXX Duplicated between i386 and x86_64 */ | ||
| 73 | DEFINE(UM_POLLIN, POLLIN); | ||
| 74 | DEFINE(UM_POLLPRI, POLLPRI); | ||
| 75 | DEFINE(UM_POLLOUT, POLLOUT); | ||
| 70 | } | 76 | } |
diff --git a/arch/um/sys-x86_64/ptrace.c b/arch/um/sys-x86_64/ptrace.c index 74eee5c7c6dd..147bbf05cbc2 100644 --- a/arch/um/sys-x86_64/ptrace.c +++ b/arch/um/sys-x86_64/ptrace.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <asm/ptrace.h> | 8 | #include <asm/ptrace.h> |
| 9 | #include <linux/sched.h> | 9 | #include <linux/sched.h> |
| 10 | #include <linux/errno.h> | 10 | #include <linux/errno.h> |
| 11 | #include <linux/mm.h> | ||
| 11 | #include <asm/uaccess.h> | 12 | #include <asm/uaccess.h> |
| 12 | #include <asm/elf.h> | 13 | #include <asm/elf.h> |
| 13 | 14 | ||
| @@ -136,9 +137,28 @@ void arch_switch(void) | |||
| 136 | */ | 137 | */ |
| 137 | } | 138 | } |
| 138 | 139 | ||
| 140 | /* XXX Mostly copied from sys-i386 */ | ||
| 139 | int is_syscall(unsigned long addr) | 141 | int is_syscall(unsigned long addr) |
| 140 | { | 142 | { |
| 141 | panic("is_syscall"); | 143 | unsigned short instr; |
| 144 | int n; | ||
| 145 | |||
| 146 | n = copy_from_user(&instr, (void __user *) addr, sizeof(instr)); | ||
| 147 | if(n){ | ||
| 148 | /* access_process_vm() grants access to vsyscall and stub, | ||
| 149 | * while copy_from_user doesn't. Maybe access_process_vm is | ||
| 150 | * slow, but that doesn't matter, since it will be called only | ||
| 151 | * in case of singlestepping, if copy_from_user failed. | ||
| 152 | */ | ||
| 153 | n = access_process_vm(current, addr, &instr, sizeof(instr), 0); | ||
| 154 | if(n != sizeof(instr)) { | ||
| 155 | printk("is_syscall : failed to read instruction from " | ||
| 156 | "0x%lx\n", addr); | ||
| 157 | return(1); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | /* sysenter */ | ||
| 161 | return(instr == 0x050f); | ||
| 142 | } | 162 | } |
| 143 | 163 | ||
| 144 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu ) | 164 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu ) |
diff --git a/arch/um/sys-x86_64/signal.c b/arch/um/sys-x86_64/signal.c index fe1d065332b1..e75c4e1838b0 100644 --- a/arch/um/sys-x86_64/signal.c +++ b/arch/um/sys-x86_64/signal.c | |||
| @@ -55,7 +55,8 @@ static int copy_sc_from_user_skas(struct pt_regs *regs, | |||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, | 57 | int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, |
| 58 | struct pt_regs *regs, unsigned long mask) | 58 | struct pt_regs *regs, unsigned long mask, |
| 59 | unsigned long sp) | ||
| 59 | { | 60 | { |
| 60 | struct faultinfo * fi = ¤t->thread.arch.faultinfo; | 61 | struct faultinfo * fi = ¤t->thread.arch.faultinfo; |
| 61 | int err = 0; | 62 | int err = 0; |
| @@ -70,7 +71,11 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, | |||
| 70 | err |= PUTREG(regs, RDI, to, rdi); | 71 | err |= PUTREG(regs, RDI, to, rdi); |
| 71 | err |= PUTREG(regs, RSI, to, rsi); | 72 | err |= PUTREG(regs, RSI, to, rsi); |
| 72 | err |= PUTREG(regs, RBP, to, rbp); | 73 | err |= PUTREG(regs, RBP, to, rbp); |
| 73 | err |= PUTREG(regs, RSP, to, rsp); | 74 | /* Must use orignal RSP, which is passed in, rather than what's in |
| 75 | * the pt_regs, because that's already been updated to point at the | ||
| 76 | * signal frame. | ||
| 77 | */ | ||
| 78 | err |= __put_user(sp, &to->rsp); | ||
| 74 | err |= PUTREG(regs, RBX, to, rbx); | 79 | err |= PUTREG(regs, RBX, to, rbx); |
| 75 | err |= PUTREG(regs, RDX, to, rdx); | 80 | err |= PUTREG(regs, RDX, to, rdx); |
| 76 | err |= PUTREG(regs, RCX, to, rcx); | 81 | err |= PUTREG(regs, RCX, to, rcx); |
| @@ -102,7 +107,7 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, | |||
| 102 | 107 | ||
| 103 | #ifdef CONFIG_MODE_TT | 108 | #ifdef CONFIG_MODE_TT |
| 104 | int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, | 109 | int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, |
| 105 | int fpsize) | 110 | int fpsize) |
| 106 | { | 111 | { |
| 107 | struct _fpstate *to_fp, *from_fp; | 112 | struct _fpstate *to_fp, *from_fp; |
| 108 | unsigned long sigs; | 113 | unsigned long sigs; |
| @@ -120,7 +125,7 @@ int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, | |||
| 120 | } | 125 | } |
| 121 | 126 | ||
| 122 | int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, | 127 | int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, |
| 123 | struct sigcontext *from, int fpsize) | 128 | struct sigcontext *from, int fpsize, unsigned long sp) |
| 124 | { | 129 | { |
| 125 | struct _fpstate *to_fp, *from_fp; | 130 | struct _fpstate *to_fp, *from_fp; |
| 126 | int err; | 131 | int err; |
| @@ -128,11 +133,17 @@ int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, | |||
| 128 | to_fp = (fp ? fp : (struct _fpstate *) (to + 1)); | 133 | to_fp = (fp ? fp : (struct _fpstate *) (to + 1)); |
| 129 | from_fp = from->fpstate; | 134 | from_fp = from->fpstate; |
| 130 | err = copy_to_user(to, from, sizeof(*to)); | 135 | err = copy_to_user(to, from, sizeof(*to)); |
| 136 | /* The SP in the sigcontext is the updated one for the signal | ||
| 137 | * delivery. The sp passed in is the original, and this needs | ||
| 138 | * to be restored, so we stick it in separately. | ||
| 139 | */ | ||
| 140 | err |= copy_to_user(&SC_SP(to), sp, sizeof(sp)); | ||
| 141 | |||
| 131 | if(from_fp != NULL){ | 142 | if(from_fp != NULL){ |
| 132 | err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate)); | 143 | err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate)); |
| 133 | err |= copy_to_user(to_fp, from_fp, fpsize); | 144 | err |= copy_to_user(to_fp, from_fp, fpsize); |
| 134 | } | 145 | } |
| 135 | return(err); | 146 | return err; |
| 136 | } | 147 | } |
| 137 | 148 | ||
| 138 | #endif | 149 | #endif |
| @@ -148,11 +159,12 @@ static int copy_sc_from_user(struct pt_regs *to, void __user *from) | |||
| 148 | } | 159 | } |
| 149 | 160 | ||
| 150 | static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, | 161 | static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, |
| 151 | struct pt_regs *from, unsigned long mask) | 162 | struct pt_regs *from, unsigned long mask, |
| 163 | unsigned long sp) | ||
| 152 | { | 164 | { |
| 153 | return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), | 165 | return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), |
| 154 | sizeof(*fp)), | 166 | sizeof(*fp), sp), |
| 155 | copy_sc_to_user_skas(to, fp, from, mask))); | 167 | copy_sc_to_user_skas(to, fp, from, mask, sp))); |
| 156 | } | 168 | } |
| 157 | 169 | ||
| 158 | struct rt_sigframe | 170 | struct rt_sigframe |
| @@ -170,6 +182,7 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
| 170 | { | 182 | { |
| 171 | struct rt_sigframe __user *frame; | 183 | struct rt_sigframe __user *frame; |
| 172 | struct _fpstate __user *fp = NULL; | 184 | struct _fpstate __user *fp = NULL; |
| 185 | unsigned long save_sp = PT_REGS_RSP(regs); | ||
| 173 | int err = 0; | 186 | int err = 0; |
| 174 | struct task_struct *me = current; | 187 | struct task_struct *me = current; |
| 175 | 188 | ||
| @@ -193,14 +206,25 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
| 193 | goto out; | 206 | goto out; |
| 194 | } | 207 | } |
| 195 | 208 | ||
| 209 | /* Update SP now because the page fault handler refuses to extend | ||
| 210 | * the stack if the faulting address is too far below the current | ||
| 211 | * SP, which frame now certainly is. If there's an error, the original | ||
| 212 | * value is restored on the way out. | ||
| 213 | * When writing the sigcontext to the stack, we have to write the | ||
| 214 | * original value, so that's passed to copy_sc_to_user, which does | ||
| 215 | * the right thing with it. | ||
| 216 | */ | ||
| 217 | PT_REGS_RSP(regs) = (unsigned long) frame; | ||
| 218 | |||
| 196 | /* Create the ucontext. */ | 219 | /* Create the ucontext. */ |
| 197 | err |= __put_user(0, &frame->uc.uc_flags); | 220 | err |= __put_user(0, &frame->uc.uc_flags); |
| 198 | err |= __put_user(0, &frame->uc.uc_link); | 221 | err |= __put_user(0, &frame->uc.uc_link); |
| 199 | err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp); | 222 | err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp); |
| 200 | err |= __put_user(sas_ss_flags(PT_REGS_SP(regs)), | 223 | err |= __put_user(sas_ss_flags(save_sp), |
| 201 | &frame->uc.uc_stack.ss_flags); | 224 | &frame->uc.uc_stack.ss_flags); |
| 202 | err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size); | 225 | err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size); |
| 203 | err |= copy_sc_to_user(&frame->uc.uc_mcontext, fp, regs, set->sig[0]); | 226 | err |= copy_sc_to_user(&frame->uc.uc_mcontext, fp, regs, set->sig[0], |
| 227 | save_sp); | ||
| 204 | err |= __put_user(fp, &frame->uc.uc_mcontext.fpstate); | 228 | err |= __put_user(fp, &frame->uc.uc_mcontext.fpstate); |
| 205 | if (sizeof(*set) == 16) { | 229 | if (sizeof(*set) == 16) { |
| 206 | __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]); | 230 | __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]); |
| @@ -217,10 +241,10 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
| 217 | err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); | 241 | err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); |
| 218 | else | 242 | else |
| 219 | /* could use a vstub here */ | 243 | /* could use a vstub here */ |
| 220 | goto out; | 244 | goto restore_sp; |
| 221 | 245 | ||
| 222 | if (err) | 246 | if (err) |
| 223 | goto out; | 247 | goto restore_sp; |
| 224 | 248 | ||
| 225 | /* Set up registers for signal handler */ | 249 | /* Set up registers for signal handler */ |
| 226 | { | 250 | { |
| @@ -238,10 +262,12 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
| 238 | PT_REGS_RSI(regs) = (unsigned long) &frame->info; | 262 | PT_REGS_RSI(regs) = (unsigned long) &frame->info; |
| 239 | PT_REGS_RDX(regs) = (unsigned long) &frame->uc; | 263 | PT_REGS_RDX(regs) = (unsigned long) &frame->uc; |
| 240 | PT_REGS_RIP(regs) = (unsigned long) ka->sa.sa_handler; | 264 | PT_REGS_RIP(regs) = (unsigned long) ka->sa.sa_handler; |
| 241 | |||
| 242 | PT_REGS_RSP(regs) = (unsigned long) frame; | ||
| 243 | out: | 265 | out: |
| 244 | return(err); | 266 | return err; |
| 267 | |||
| 268 | restore_sp: | ||
| 269 | PT_REGS_RSP(regs) = save_sp; | ||
| 270 | return err; | ||
| 245 | } | 271 | } |
| 246 | 272 | ||
| 247 | long sys_rt_sigreturn(struct pt_regs *regs) | 273 | long sys_rt_sigreturn(struct pt_regs *regs) |
diff --git a/arch/um/sys-x86_64/user-offsets.c b/arch/um/sys-x86_64/user-offsets.c index 7bd54a921cf7..899cebb57c3f 100644 --- a/arch/um/sys-x86_64/user-offsets.c +++ b/arch/um/sys-x86_64/user-offsets.c | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #include <stdio.h> | 1 | #include <stdio.h> |
| 2 | #include <stddef.h> | 2 | #include <stddef.h> |
| 3 | #include <signal.h> | 3 | #include <signal.h> |
| 4 | #include <sys/poll.h> | ||
| 4 | #define __FRAME_OFFSETS | 5 | #define __FRAME_OFFSETS |
| 5 | #include <asm/ptrace.h> | 6 | #include <asm/ptrace.h> |
| 6 | #include <asm/types.h> | 7 | #include <asm/types.h> |
| @@ -88,4 +89,9 @@ void foo(void) | |||
| 88 | DEFINE_LONGS(HOST_IP, RIP); | 89 | DEFINE_LONGS(HOST_IP, RIP); |
| 89 | DEFINE_LONGS(HOST_SP, RSP); | 90 | DEFINE_LONGS(HOST_SP, RSP); |
| 90 | DEFINE(UM_FRAME_SIZE, sizeof(struct user_regs_struct)); | 91 | DEFINE(UM_FRAME_SIZE, sizeof(struct user_regs_struct)); |
| 92 | |||
| 93 | /* XXX Duplicated between i386 and x86_64 */ | ||
| 94 | DEFINE(UM_POLLIN, POLLIN); | ||
| 95 | DEFINE(UM_POLLPRI, POLLPRI); | ||
| 96 | DEFINE(UM_POLLOUT, POLLOUT); | ||
| 91 | } | 97 | } |
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 45efe0ca88f8..4310b4a311a5 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig | |||
| @@ -250,6 +250,15 @@ config SCHED_SMT | |||
| 250 | cost of slightly increased overhead in some places. If unsure say | 250 | cost of slightly increased overhead in some places. If unsure say |
| 251 | N here. | 251 | N here. |
| 252 | 252 | ||
| 253 | config SCHED_MC | ||
| 254 | bool "Multi-core scheduler support" | ||
| 255 | depends on SMP | ||
| 256 | default y | ||
| 257 | help | ||
| 258 | Multi-core scheduler support improves the CPU scheduler's decision | ||
| 259 | making when dealing with multi-core CPU chips at a cost of slightly | ||
| 260 | increased overhead in some places. If unsure say N here. | ||
| 261 | |||
| 253 | source "kernel/Kconfig.preempt" | 262 | source "kernel/Kconfig.preempt" |
| 254 | 263 | ||
| 255 | config NUMA | 264 | config NUMA |
| @@ -325,6 +334,10 @@ config HAVE_ARCH_EARLY_PFN_TO_NID | |||
| 325 | def_bool y | 334 | def_bool y |
| 326 | depends on NUMA | 335 | depends on NUMA |
| 327 | 336 | ||
| 337 | config OUT_OF_LINE_PFN_TO_PAGE | ||
| 338 | def_bool y | ||
| 339 | depends on DISCONTIGMEM | ||
| 340 | |||
| 328 | config NR_CPUS | 341 | config NR_CPUS |
| 329 | int "Maximum number of CPUs (2-256)" | 342 | int "Maximum number of CPUs (2-256)" |
| 330 | range 2 255 | 343 | range 2 255 |
diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S index 7549a4389fbf..35b2faccdc6c 100644 --- a/arch/x86_64/ia32/ia32entry.S +++ b/arch/x86_64/ia32/ia32entry.S | |||
| @@ -688,6 +688,8 @@ ia32_sys_call_table: | |||
| 688 | .quad sys_ni_syscall /* pselect6 for now */ | 688 | .quad sys_ni_syscall /* pselect6 for now */ |
| 689 | .quad sys_ni_syscall /* ppoll for now */ | 689 | .quad sys_ni_syscall /* ppoll for now */ |
| 690 | .quad sys_unshare /* 310 */ | 690 | .quad sys_unshare /* 310 */ |
| 691 | .quad compat_sys_set_robust_list | ||
| 692 | .quad compat_sys_get_robust_list | ||
| 691 | ia32_syscall_end: | 693 | ia32_syscall_end: |
| 692 | .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8 | 694 | .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8 |
| 693 | .quad ni_syscall | 695 | .quad ni_syscall |
diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index 0370720515f1..70dd8e5c6889 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c | |||
| @@ -66,24 +66,17 @@ EXPORT_SYMBOL(boot_option_idle_override); | |||
| 66 | void (*pm_idle)(void); | 66 | void (*pm_idle)(void); |
| 67 | static DEFINE_PER_CPU(unsigned int, cpu_idle_state); | 67 | static DEFINE_PER_CPU(unsigned int, cpu_idle_state); |
| 68 | 68 | ||
| 69 | static struct notifier_block *idle_notifier; | 69 | static ATOMIC_NOTIFIER_HEAD(idle_notifier); |
| 70 | static DEFINE_SPINLOCK(idle_notifier_lock); | ||
| 71 | 70 | ||
| 72 | void idle_notifier_register(struct notifier_block *n) | 71 | void idle_notifier_register(struct notifier_block *n) |
| 73 | { | 72 | { |
| 74 | unsigned long flags; | 73 | atomic_notifier_chain_register(&idle_notifier, n); |
| 75 | spin_lock_irqsave(&idle_notifier_lock, flags); | ||
| 76 | notifier_chain_register(&idle_notifier, n); | ||
| 77 | spin_unlock_irqrestore(&idle_notifier_lock, flags); | ||
| 78 | } | 74 | } |
| 79 | EXPORT_SYMBOL_GPL(idle_notifier_register); | 75 | EXPORT_SYMBOL_GPL(idle_notifier_register); |
| 80 | 76 | ||
| 81 | void idle_notifier_unregister(struct notifier_block *n) | 77 | void idle_notifier_unregister(struct notifier_block *n) |
| 82 | { | 78 | { |
| 83 | unsigned long flags; | 79 | atomic_notifier_chain_unregister(&idle_notifier, n); |
| 84 | spin_lock_irqsave(&idle_notifier_lock, flags); | ||
| 85 | notifier_chain_unregister(&idle_notifier, n); | ||
| 86 | spin_unlock_irqrestore(&idle_notifier_lock, flags); | ||
| 87 | } | 80 | } |
| 88 | EXPORT_SYMBOL(idle_notifier_unregister); | 81 | EXPORT_SYMBOL(idle_notifier_unregister); |
| 89 | 82 | ||
| @@ -93,13 +86,13 @@ static DEFINE_PER_CPU(enum idle_state, idle_state) = CPU_NOT_IDLE; | |||
| 93 | void enter_idle(void) | 86 | void enter_idle(void) |
| 94 | { | 87 | { |
| 95 | __get_cpu_var(idle_state) = CPU_IDLE; | 88 | __get_cpu_var(idle_state) = CPU_IDLE; |
| 96 | notifier_call_chain(&idle_notifier, IDLE_START, NULL); | 89 | atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL); |
| 97 | } | 90 | } |
| 98 | 91 | ||
| 99 | static void __exit_idle(void) | 92 | static void __exit_idle(void) |
| 100 | { | 93 | { |
| 101 | __get_cpu_var(idle_state) = CPU_NOT_IDLE; | 94 | __get_cpu_var(idle_state) = CPU_NOT_IDLE; |
| 102 | notifier_call_chain(&idle_notifier, IDLE_END, NULL); | 95 | atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL); |
| 103 | } | 96 | } |
| 104 | 97 | ||
| 105 | /* Called from interrupts to signify idle end */ | 98 | /* Called from interrupts to signify idle end */ |
diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c index a57eec8311a7..d1f3e9272c05 100644 --- a/arch/x86_64/kernel/setup.c +++ b/arch/x86_64/kernel/setup.c | |||
| @@ -962,7 +962,6 @@ static void __cpuinit detect_ht(struct cpuinfo_x86 *c) | |||
| 962 | 962 | ||
| 963 | cpuid(1, &eax, &ebx, &ecx, &edx); | 963 | cpuid(1, &eax, &ebx, &ecx, &edx); |
| 964 | 964 | ||
| 965 | c->apicid = phys_pkg_id(0); | ||
| 966 | 965 | ||
| 967 | if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY)) | 966 | if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY)) |
| 968 | return; | 967 | return; |
| @@ -1171,6 +1170,8 @@ void __cpuinit identify_cpu(struct cpuinfo_x86 *c) | |||
| 1171 | c->x86_capability[2] = cpuid_edx(0x80860001); | 1170 | c->x86_capability[2] = cpuid_edx(0x80860001); |
| 1172 | } | 1171 | } |
| 1173 | 1172 | ||
| 1173 | c->apicid = phys_pkg_id(0); | ||
| 1174 | |||
| 1174 | /* | 1175 | /* |
| 1175 | * Vendor-specific initialization. In this section we | 1176 | * Vendor-specific initialization. In this section we |
| 1176 | * canonicalize the feature flags, meaning if there are | 1177 | * canonicalize the feature flags, meaning if there are |
diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c index 66e98659d077..ea48fa638070 100644 --- a/arch/x86_64/kernel/smpboot.c +++ b/arch/x86_64/kernel/smpboot.c | |||
| @@ -68,6 +68,9 @@ u8 phys_proc_id[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = BAD_APICID }; | |||
| 68 | /* core ID of each logical CPU */ | 68 | /* core ID of each logical CPU */ |
| 69 | u8 cpu_core_id[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = BAD_APICID }; | 69 | u8 cpu_core_id[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = BAD_APICID }; |
| 70 | 70 | ||
| 71 | /* Last level cache ID of each logical CPU */ | ||
| 72 | u8 cpu_llc_id[NR_CPUS] __cpuinitdata = {[0 ... NR_CPUS-1] = BAD_APICID}; | ||
| 73 | |||
| 71 | /* Bitmask of currently online CPUs */ | 74 | /* Bitmask of currently online CPUs */ |
| 72 | cpumask_t cpu_online_map __read_mostly; | 75 | cpumask_t cpu_online_map __read_mostly; |
| 73 | 76 | ||
| @@ -445,6 +448,18 @@ void __cpuinit smp_callin(void) | |||
| 445 | cpu_set(cpuid, cpu_callin_map); | 448 | cpu_set(cpuid, cpu_callin_map); |
| 446 | } | 449 | } |
| 447 | 450 | ||
| 451 | /* maps the cpu to the sched domain representing multi-core */ | ||
| 452 | cpumask_t cpu_coregroup_map(int cpu) | ||
| 453 | { | ||
| 454 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
| 455 | /* | ||
| 456 | * For perf, we return last level cache shared map. | ||
| 457 | * TBD: when power saving sched policy is added, we will return | ||
| 458 | * cpu_core_map when power saving policy is enabled | ||
| 459 | */ | ||
| 460 | return c->llc_shared_map; | ||
| 461 | } | ||
| 462 | |||
| 448 | /* representing cpus for which sibling maps can be computed */ | 463 | /* representing cpus for which sibling maps can be computed */ |
| 449 | static cpumask_t cpu_sibling_setup_map; | 464 | static cpumask_t cpu_sibling_setup_map; |
| 450 | 465 | ||
| @@ -463,12 +478,16 @@ static inline void set_cpu_sibling_map(int cpu) | |||
| 463 | cpu_set(cpu, cpu_sibling_map[i]); | 478 | cpu_set(cpu, cpu_sibling_map[i]); |
| 464 | cpu_set(i, cpu_core_map[cpu]); | 479 | cpu_set(i, cpu_core_map[cpu]); |
| 465 | cpu_set(cpu, cpu_core_map[i]); | 480 | cpu_set(cpu, cpu_core_map[i]); |
| 481 | cpu_set(i, c[cpu].llc_shared_map); | ||
| 482 | cpu_set(cpu, c[i].llc_shared_map); | ||
| 466 | } | 483 | } |
| 467 | } | 484 | } |
| 468 | } else { | 485 | } else { |
| 469 | cpu_set(cpu, cpu_sibling_map[cpu]); | 486 | cpu_set(cpu, cpu_sibling_map[cpu]); |
| 470 | } | 487 | } |
| 471 | 488 | ||
| 489 | cpu_set(cpu, c[cpu].llc_shared_map); | ||
| 490 | |||
| 472 | if (current_cpu_data.x86_max_cores == 1) { | 491 | if (current_cpu_data.x86_max_cores == 1) { |
| 473 | cpu_core_map[cpu] = cpu_sibling_map[cpu]; | 492 | cpu_core_map[cpu] = cpu_sibling_map[cpu]; |
| 474 | c[cpu].booted_cores = 1; | 493 | c[cpu].booted_cores = 1; |
| @@ -476,6 +495,11 @@ static inline void set_cpu_sibling_map(int cpu) | |||
| 476 | } | 495 | } |
| 477 | 496 | ||
| 478 | for_each_cpu_mask(i, cpu_sibling_setup_map) { | 497 | for_each_cpu_mask(i, cpu_sibling_setup_map) { |
| 498 | if (cpu_llc_id[cpu] != BAD_APICID && | ||
| 499 | cpu_llc_id[cpu] == cpu_llc_id[i]) { | ||
| 500 | cpu_set(i, c[cpu].llc_shared_map); | ||
| 501 | cpu_set(cpu, c[i].llc_shared_map); | ||
| 502 | } | ||
| 479 | if (phys_proc_id[cpu] == phys_proc_id[i]) { | 503 | if (phys_proc_id[cpu] == phys_proc_id[i]) { |
| 480 | cpu_set(i, cpu_core_map[cpu]); | 504 | cpu_set(i, cpu_core_map[cpu]); |
| 481 | cpu_set(cpu, cpu_core_map[i]); | 505 | cpu_set(cpu, cpu_core_map[i]); |
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 7b148309c529..edaa9fe654dc 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c | |||
| @@ -69,20 +69,20 @@ asmlinkage void alignment_check(void); | |||
| 69 | asmlinkage void machine_check(void); | 69 | asmlinkage void machine_check(void); |
| 70 | asmlinkage void spurious_interrupt_bug(void); | 70 | asmlinkage void spurious_interrupt_bug(void); |
| 71 | 71 | ||
| 72 | struct notifier_block *die_chain; | 72 | ATOMIC_NOTIFIER_HEAD(die_chain); |
| 73 | static DEFINE_SPINLOCK(die_notifier_lock); | ||
| 74 | 73 | ||
| 75 | int register_die_notifier(struct notifier_block *nb) | 74 | int register_die_notifier(struct notifier_block *nb) |
| 76 | { | 75 | { |
| 77 | int err = 0; | ||
| 78 | unsigned long flags; | ||
| 79 | |||
| 80 | vmalloc_sync_all(); | 76 | vmalloc_sync_all(); |
| 81 | spin_lock_irqsave(&die_notifier_lock, flags); | 77 | return atomic_notifier_chain_register(&die_chain, nb); |
| 82 | err = notifier_chain_register(&die_chain, nb); | 78 | } |
| 83 | spin_unlock_irqrestore(&die_notifier_lock, flags); | 79 | EXPORT_SYMBOL(register_die_notifier); |
| 84 | return err; | 80 | |
| 81 | int unregister_die_notifier(struct notifier_block *nb) | ||
| 82 | { | ||
| 83 | return atomic_notifier_chain_unregister(&die_chain, nb); | ||
| 85 | } | 84 | } |
| 85 | EXPORT_SYMBOL(unregister_die_notifier); | ||
| 86 | 86 | ||
| 87 | static inline void conditional_sti(struct pt_regs *regs) | 87 | static inline void conditional_sti(struct pt_regs *regs) |
| 88 | { | 88 | { |
diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c index b04415625442..e5f7f1c34462 100644 --- a/arch/x86_64/mm/init.c +++ b/arch/x86_64/mm/init.c | |||
| @@ -72,7 +72,7 @@ void show_mem(void) | |||
| 72 | show_free_areas(); | 72 | show_free_areas(); |
| 73 | printk(KERN_INFO "Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | 73 | printk(KERN_INFO "Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); |
| 74 | 74 | ||
| 75 | for_each_pgdat(pgdat) { | 75 | for_each_online_pgdat(pgdat) { |
| 76 | for (i = 0; i < pgdat->node_spanned_pages; ++i) { | 76 | for (i = 0; i < pgdat->node_spanned_pages; ++i) { |
| 77 | page = pfn_to_page(pgdat->node_start_pfn + i); | 77 | page = pfn_to_page(pgdat->node_start_pfn + i); |
| 78 | total++; | 78 | total++; |
diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index 63c72641b737..4be82d6e2b48 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c | |||
| @@ -377,21 +377,6 @@ EXPORT_SYMBOL(node_data); | |||
| 377 | * Should do that. | 377 | * Should do that. |
| 378 | */ | 378 | */ |
| 379 | 379 | ||
| 380 | /* Requires pfn_valid(pfn) to be true */ | ||
| 381 | struct page *pfn_to_page(unsigned long pfn) | ||
| 382 | { | ||
| 383 | int nid = phys_to_nid(((unsigned long)(pfn)) << PAGE_SHIFT); | ||
| 384 | return (pfn - node_start_pfn(nid)) + NODE_DATA(nid)->node_mem_map; | ||
| 385 | } | ||
| 386 | EXPORT_SYMBOL(pfn_to_page); | ||
| 387 | |||
| 388 | unsigned long page_to_pfn(struct page *page) | ||
| 389 | { | ||
| 390 | return (long)(((page) - page_zone(page)->zone_mem_map) + | ||
| 391 | page_zone(page)->zone_start_pfn); | ||
| 392 | } | ||
| 393 | EXPORT_SYMBOL(page_to_pfn); | ||
| 394 | |||
| 395 | int pfn_valid(unsigned long pfn) | 380 | int pfn_valid(unsigned long pfn) |
| 396 | { | 381 | { |
| 397 | unsigned nid; | 382 | unsigned nid; |
diff --git a/arch/xtensa/platform-iss/setup.c b/arch/xtensa/platform-iss/setup.c index 2e6dcbf0cc04..23790a5610e2 100644 --- a/arch/xtensa/platform-iss/setup.c +++ b/arch/xtensa/platform-iss/setup.c | |||
| @@ -108,5 +108,5 @@ static struct notifier_block iss_panic_block = { | |||
| 108 | 108 | ||
| 109 | void __init platform_setup(char **p_cmdline) | 109 | void __init platform_setup(char **p_cmdline) |
| 110 | { | 110 | { |
| 111 | notifier_chain_register(&panic_notifier_list, &iss_panic_block); | 111 | atomic_notifier_chain_register(&panic_notifier_list, &iss_panic_block); |
| 112 | } | 112 | } |
diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index 7fc903b5f3cd..82469db25100 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c | |||
| @@ -785,6 +785,8 @@ void blk_queue_stack_limits(request_queue_t *t, request_queue_t *b) | |||
| 785 | t->max_hw_segments = min(t->max_hw_segments,b->max_hw_segments); | 785 | t->max_hw_segments = min(t->max_hw_segments,b->max_hw_segments); |
| 786 | t->max_segment_size = min(t->max_segment_size,b->max_segment_size); | 786 | t->max_segment_size = min(t->max_segment_size,b->max_segment_size); |
| 787 | t->hardsect_size = max(t->hardsect_size,b->hardsect_size); | 787 | t->hardsect_size = max(t->hardsect_size,b->hardsect_size); |
| 788 | if (!test_bit(QUEUE_FLAG_CLUSTER, &b->queue_flags)) | ||
| 789 | clear_bit(QUEUE_FLAG_CLUSTER, &t->queue_flags); | ||
| 788 | } | 790 | } |
| 789 | 791 | ||
| 790 | EXPORT_SYMBOL(blk_queue_stack_limits); | 792 | EXPORT_SYMBOL(blk_queue_stack_limits); |
diff --git a/drivers/Kconfig b/drivers/Kconfig index bddf431bbb72..9f5c0da57c90 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig | |||
| @@ -70,4 +70,6 @@ source "drivers/sn/Kconfig" | |||
| 70 | 70 | ||
| 71 | source "drivers/edac/Kconfig" | 71 | source "drivers/edac/Kconfig" |
| 72 | 72 | ||
| 73 | source "drivers/rtc/Kconfig" | ||
| 74 | |||
| 73 | endmenu | 75 | endmenu |
diff --git a/drivers/Makefile b/drivers/Makefile index 5c69b86db624..424955274e60 100644 --- a/drivers/Makefile +++ b/drivers/Makefile | |||
| @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_GADGET) += usb/gadget/ | |||
| 56 | obj-$(CONFIG_GAMEPORT) += input/gameport/ | 56 | obj-$(CONFIG_GAMEPORT) += input/gameport/ |
| 57 | obj-$(CONFIG_INPUT) += input/ | 57 | obj-$(CONFIG_INPUT) += input/ |
| 58 | obj-$(CONFIG_I2O) += message/ | 58 | obj-$(CONFIG_I2O) += message/ |
| 59 | obj-$(CONFIG_RTC_LIB) += rtc/ | ||
| 59 | obj-$(CONFIG_I2C) += i2c/ | 60 | obj-$(CONFIG_I2C) += i2c/ |
| 60 | obj-$(CONFIG_W1) += w1/ | 61 | obj-$(CONFIG_W1) += w1/ |
| 61 | obj-$(CONFIG_HWMON) += hwmon/ | 62 | obj-$(CONFIG_HWMON) += hwmon/ |
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 105a0d61eb1f..dd547af4681a 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
| @@ -47,16 +47,16 @@ static struct kset_uevent_ops memory_uevent_ops = { | |||
| 47 | .uevent = memory_uevent, | 47 | .uevent = memory_uevent, |
| 48 | }; | 48 | }; |
| 49 | 49 | ||
| 50 | static struct notifier_block *memory_chain; | 50 | static BLOCKING_NOTIFIER_HEAD(memory_chain); |
| 51 | 51 | ||
| 52 | int register_memory_notifier(struct notifier_block *nb) | 52 | int register_memory_notifier(struct notifier_block *nb) |
| 53 | { | 53 | { |
| 54 | return notifier_chain_register(&memory_chain, nb); | 54 | return blocking_notifier_chain_register(&memory_chain, nb); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | void unregister_memory_notifier(struct notifier_block *nb) | 57 | void unregister_memory_notifier(struct notifier_block *nb) |
| 58 | { | 58 | { |
| 59 | notifier_chain_unregister(&memory_chain, nb); | 59 | blocking_notifier_chain_unregister(&memory_chain, nb); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | /* | 62 | /* |
| @@ -140,7 +140,7 @@ static ssize_t show_mem_state(struct sys_device *dev, char *buf) | |||
| 140 | 140 | ||
| 141 | static inline int memory_notify(unsigned long val, void *v) | 141 | static inline int memory_notify(unsigned long val, void *v) |
| 142 | { | 142 | { |
| 143 | return notifier_call_chain(&memory_chain, val, v); | 143 | return blocking_notifier_call_chain(&memory_chain, val, v); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | /* | 146 | /* |
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index facc3f1d9e37..73d30bf01582 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
| @@ -696,7 +696,7 @@ config NVRAM | |||
| 696 | 696 | ||
| 697 | config RTC | 697 | config RTC |
| 698 | tristate "Enhanced Real Time Clock Support" | 698 | tristate "Enhanced Real Time Clock Support" |
| 699 | depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV | 699 | depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM |
| 700 | ---help--- | 700 | ---help--- |
| 701 | If you say Y here and create a character special file /dev/rtc with | 701 | If you say Y here and create a character special file /dev/rtc with |
| 702 | major number 10 and minor number 135 using mknod ("man mknod"), you | 702 | major number 10 and minor number 135 using mknod ("man mknod"), you |
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index b8fb87c6c29f..40eb005b9d77 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c | |||
| @@ -3744,7 +3744,7 @@ static int ipmi_init_msghandler(void) | |||
| 3744 | ipmi_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES; | 3744 | ipmi_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES; |
| 3745 | add_timer(&ipmi_timer); | 3745 | add_timer(&ipmi_timer); |
| 3746 | 3746 | ||
| 3747 | notifier_chain_register(&panic_notifier_list, &panic_block); | 3747 | atomic_notifier_chain_register(&panic_notifier_list, &panic_block); |
| 3748 | 3748 | ||
| 3749 | initialized = 1; | 3749 | initialized = 1; |
| 3750 | 3750 | ||
| @@ -3764,7 +3764,7 @@ static __exit void cleanup_ipmi(void) | |||
| 3764 | if (!initialized) | 3764 | if (!initialized) |
| 3765 | return; | 3765 | return; |
| 3766 | 3766 | ||
| 3767 | notifier_chain_unregister(&panic_notifier_list, &panic_block); | 3767 | atomic_notifier_chain_unregister(&panic_notifier_list, &panic_block); |
| 3768 | 3768 | ||
| 3769 | /* This can't be called if any interfaces exist, so no worry about | 3769 | /* This can't be called if any interfaces exist, so no worry about |
| 3770 | shutting down the interfaces. */ | 3770 | shutting down the interfaces. */ |
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 12f858dc9994..35fbd4d8ed4b 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c | |||
| @@ -237,10 +237,10 @@ struct smi_info | |||
| 237 | 237 | ||
| 238 | static int try_smi_init(struct smi_info *smi); | 238 | static int try_smi_init(struct smi_info *smi); |
| 239 | 239 | ||
| 240 | static struct notifier_block *xaction_notifier_list; | 240 | static ATOMIC_NOTIFIER_HEAD(xaction_notifier_list); |
| 241 | static int register_xaction_notifier(struct notifier_block * nb) | 241 | static int register_xaction_notifier(struct notifier_block * nb) |
| 242 | { | 242 | { |
| 243 | return notifier_chain_register(&xaction_notifier_list, nb); | 243 | return atomic_notifier_chain_register(&xaction_notifier_list, nb); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | static void si_restart_short_timer(struct smi_info *smi_info); | 246 | static void si_restart_short_timer(struct smi_info *smi_info); |
| @@ -302,7 +302,8 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) | |||
| 302 | do_gettimeofday(&t); | 302 | do_gettimeofday(&t); |
| 303 | printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); | 303 | printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); |
| 304 | #endif | 304 | #endif |
| 305 | err = notifier_call_chain(&xaction_notifier_list, 0, smi_info); | 305 | err = atomic_notifier_call_chain(&xaction_notifier_list, |
| 306 | 0, smi_info); | ||
| 306 | if (err & NOTIFY_STOP_MASK) { | 307 | if (err & NOTIFY_STOP_MASK) { |
| 307 | rv = SI_SM_CALL_WITHOUT_DELAY; | 308 | rv = SI_SM_CALL_WITHOUT_DELAY; |
| 308 | goto out; | 309 | goto out; |
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 616539310d9a..7ece9f3c8f70 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c | |||
| @@ -1158,7 +1158,8 @@ static int __init ipmi_wdog_init(void) | |||
| 1158 | } | 1158 | } |
| 1159 | 1159 | ||
| 1160 | register_reboot_notifier(&wdog_reboot_notifier); | 1160 | register_reboot_notifier(&wdog_reboot_notifier); |
| 1161 | notifier_chain_register(&panic_notifier_list, &wdog_panic_notifier); | 1161 | atomic_notifier_chain_register(&panic_notifier_list, |
| 1162 | &wdog_panic_notifier); | ||
| 1162 | 1163 | ||
| 1163 | printk(KERN_INFO PFX "driver initialized\n"); | 1164 | printk(KERN_INFO PFX "driver initialized\n"); |
| 1164 | 1165 | ||
| @@ -1176,7 +1177,8 @@ static __exit void ipmi_unregister_watchdog(void) | |||
| 1176 | release_nmi(&ipmi_nmi_handler); | 1177 | release_nmi(&ipmi_nmi_handler); |
| 1177 | #endif | 1178 | #endif |
| 1178 | 1179 | ||
| 1179 | notifier_chain_unregister(&panic_notifier_list, &wdog_panic_notifier); | 1180 | atomic_notifier_chain_unregister(&panic_notifier_list, |
| 1181 | &wdog_panic_notifier); | ||
| 1180 | unregister_reboot_notifier(&wdog_reboot_notifier); | 1182 | unregister_reboot_notifier(&wdog_reboot_notifier); |
| 1181 | 1183 | ||
| 1182 | if (! watchdog_user) | 1184 | if (! watchdog_user) |
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index aed80e6aec6d..9b6ae7dc8b8a 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
| @@ -52,9 +52,8 @@ static void handle_update(void *data); | |||
| 52 | * changes to devices when the CPU clock speed changes. | 52 | * changes to devices when the CPU clock speed changes. |
| 53 | * The mutex locks both lists. | 53 | * The mutex locks both lists. |
| 54 | */ | 54 | */ |
| 55 | static struct notifier_block *cpufreq_policy_notifier_list; | 55 | static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list); |
| 56 | static struct notifier_block *cpufreq_transition_notifier_list; | 56 | static BLOCKING_NOTIFIER_HEAD(cpufreq_transition_notifier_list); |
| 57 | static DECLARE_RWSEM (cpufreq_notifier_rwsem); | ||
| 58 | 57 | ||
| 59 | 58 | ||
| 60 | static LIST_HEAD(cpufreq_governor_list); | 59 | static LIST_HEAD(cpufreq_governor_list); |
| @@ -247,8 +246,6 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) | |||
| 247 | dprintk("notification %u of frequency transition to %u kHz\n", | 246 | dprintk("notification %u of frequency transition to %u kHz\n", |
| 248 | state, freqs->new); | 247 | state, freqs->new); |
| 249 | 248 | ||
| 250 | down_read(&cpufreq_notifier_rwsem); | ||
| 251 | |||
| 252 | policy = cpufreq_cpu_data[freqs->cpu]; | 249 | policy = cpufreq_cpu_data[freqs->cpu]; |
| 253 | switch (state) { | 250 | switch (state) { |
| 254 | 251 | ||
| @@ -266,20 +263,19 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) | |||
| 266 | freqs->old = policy->cur; | 263 | freqs->old = policy->cur; |
| 267 | } | 264 | } |
| 268 | } | 265 | } |
| 269 | notifier_call_chain(&cpufreq_transition_notifier_list, | 266 | blocking_notifier_call_chain(&cpufreq_transition_notifier_list, |
| 270 | CPUFREQ_PRECHANGE, freqs); | 267 | CPUFREQ_PRECHANGE, freqs); |
| 271 | adjust_jiffies(CPUFREQ_PRECHANGE, freqs); | 268 | adjust_jiffies(CPUFREQ_PRECHANGE, freqs); |
| 272 | break; | 269 | break; |
| 273 | 270 | ||
| 274 | case CPUFREQ_POSTCHANGE: | 271 | case CPUFREQ_POSTCHANGE: |
| 275 | adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); | 272 | adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); |
| 276 | notifier_call_chain(&cpufreq_transition_notifier_list, | 273 | blocking_notifier_call_chain(&cpufreq_transition_notifier_list, |
| 277 | CPUFREQ_POSTCHANGE, freqs); | 274 | CPUFREQ_POSTCHANGE, freqs); |
| 278 | if (likely(policy) && likely(policy->cpu == freqs->cpu)) | 275 | if (likely(policy) && likely(policy->cpu == freqs->cpu)) |
| 279 | policy->cur = freqs->new; | 276 | policy->cur = freqs->new; |
| 280 | break; | 277 | break; |
| 281 | } | 278 | } |
| 282 | up_read(&cpufreq_notifier_rwsem); | ||
| 283 | } | 279 | } |
| 284 | EXPORT_SYMBOL_GPL(cpufreq_notify_transition); | 280 | EXPORT_SYMBOL_GPL(cpufreq_notify_transition); |
| 285 | 281 | ||
| @@ -1007,7 +1003,7 @@ static int cpufreq_suspend(struct sys_device * sysdev, pm_message_t pmsg) | |||
| 1007 | freqs.old = cpu_policy->cur; | 1003 | freqs.old = cpu_policy->cur; |
| 1008 | freqs.new = cur_freq; | 1004 | freqs.new = cur_freq; |
| 1009 | 1005 | ||
| 1010 | notifier_call_chain(&cpufreq_transition_notifier_list, | 1006 | blocking_notifier_call_chain(&cpufreq_transition_notifier_list, |
| 1011 | CPUFREQ_SUSPENDCHANGE, &freqs); | 1007 | CPUFREQ_SUSPENDCHANGE, &freqs); |
| 1012 | adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs); | 1008 | adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs); |
| 1013 | 1009 | ||
| @@ -1088,7 +1084,8 @@ static int cpufreq_resume(struct sys_device * sysdev) | |||
| 1088 | freqs.old = cpu_policy->cur; | 1084 | freqs.old = cpu_policy->cur; |
| 1089 | freqs.new = cur_freq; | 1085 | freqs.new = cur_freq; |
| 1090 | 1086 | ||
| 1091 | notifier_call_chain(&cpufreq_transition_notifier_list, | 1087 | blocking_notifier_call_chain( |
| 1088 | &cpufreq_transition_notifier_list, | ||
| 1092 | CPUFREQ_RESUMECHANGE, &freqs); | 1089 | CPUFREQ_RESUMECHANGE, &freqs); |
| 1093 | adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); | 1090 | adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); |
| 1094 | 1091 | ||
| @@ -1125,24 +1122,24 @@ static struct sysdev_driver cpufreq_sysdev_driver = { | |||
| 1125 | * changes in cpufreq policy. | 1122 | * changes in cpufreq policy. |
| 1126 | * | 1123 | * |
| 1127 | * This function may sleep, and has the same return conditions as | 1124 | * This function may sleep, and has the same return conditions as |
| 1128 | * notifier_chain_register. | 1125 | * blocking_notifier_chain_register. |
| 1129 | */ | 1126 | */ |
| 1130 | int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) | 1127 | int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) |
| 1131 | { | 1128 | { |
| 1132 | int ret; | 1129 | int ret; |
| 1133 | 1130 | ||
| 1134 | down_write(&cpufreq_notifier_rwsem); | ||
| 1135 | switch (list) { | 1131 | switch (list) { |
| 1136 | case CPUFREQ_TRANSITION_NOTIFIER: | 1132 | case CPUFREQ_TRANSITION_NOTIFIER: |
| 1137 | ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb); | 1133 | ret = blocking_notifier_chain_register( |
| 1134 | &cpufreq_transition_notifier_list, nb); | ||
| 1138 | break; | 1135 | break; |
| 1139 | case CPUFREQ_POLICY_NOTIFIER: | 1136 | case CPUFREQ_POLICY_NOTIFIER: |
| 1140 | ret = notifier_chain_register(&cpufreq_policy_notifier_list, nb); | 1137 | ret = blocking_notifier_chain_register( |
| 1138 | &cpufreq_policy_notifier_list, nb); | ||
| 1141 | break; | 1139 | break; |
| 1142 | default: | 1140 | default: |
| 1143 | ret = -EINVAL; | 1141 | ret = -EINVAL; |
| 1144 | } | 1142 | } |
| 1145 | up_write(&cpufreq_notifier_rwsem); | ||
| 1146 | 1143 | ||
| 1147 | return ret; | 1144 | return ret; |
| 1148 | } | 1145 | } |
| @@ -1157,24 +1154,24 @@ EXPORT_SYMBOL(cpufreq_register_notifier); | |||
| 1157 | * Remove a driver from the CPU frequency notifier list. | 1154 | * Remove a driver from the CPU frequency notifier list. |
| 1158 | * | 1155 | * |
| 1159 | * This function may sleep, and has the same return conditions as | 1156 | * This function may sleep, and has the same return conditions as |
| 1160 | * notifier_chain_unregister. | 1157 | * blocking_notifier_chain_unregister. |
| 1161 | */ | 1158 | */ |
| 1162 | int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) | 1159 | int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) |
| 1163 | { | 1160 | { |
| 1164 | int ret; | 1161 | int ret; |
| 1165 | 1162 | ||
| 1166 | down_write(&cpufreq_notifier_rwsem); | ||
| 1167 | switch (list) { | 1163 | switch (list) { |
| 1168 | case CPUFREQ_TRANSITION_NOTIFIER: | 1164 | case CPUFREQ_TRANSITION_NOTIFIER: |
| 1169 | ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb); | 1165 | ret = blocking_notifier_chain_unregister( |
| 1166 | &cpufreq_transition_notifier_list, nb); | ||
| 1170 | break; | 1167 | break; |
| 1171 | case CPUFREQ_POLICY_NOTIFIER: | 1168 | case CPUFREQ_POLICY_NOTIFIER: |
| 1172 | ret = notifier_chain_unregister(&cpufreq_policy_notifier_list, nb); | 1169 | ret = blocking_notifier_chain_unregister( |
| 1170 | &cpufreq_policy_notifier_list, nb); | ||
| 1173 | break; | 1171 | break; |
| 1174 | default: | 1172 | default: |
| 1175 | ret = -EINVAL; | 1173 | ret = -EINVAL; |
| 1176 | } | 1174 | } |
| 1177 | up_write(&cpufreq_notifier_rwsem); | ||
| 1178 | 1175 | ||
| 1179 | return ret; | 1176 | return ret; |
| 1180 | } | 1177 | } |
| @@ -1346,29 +1343,23 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli | |||
| 1346 | if (ret) | 1343 | if (ret) |
| 1347 | goto error_out; | 1344 | goto error_out; |
| 1348 | 1345 | ||
| 1349 | down_read(&cpufreq_notifier_rwsem); | ||
| 1350 | |||
| 1351 | /* adjust if necessary - all reasons */ | 1346 | /* adjust if necessary - all reasons */ |
| 1352 | notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST, | 1347 | blocking_notifier_call_chain(&cpufreq_policy_notifier_list, |
| 1353 | policy); | 1348 | CPUFREQ_ADJUST, policy); |
| 1354 | 1349 | ||
| 1355 | /* adjust if necessary - hardware incompatibility*/ | 1350 | /* adjust if necessary - hardware incompatibility*/ |
| 1356 | notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_INCOMPATIBLE, | 1351 | blocking_notifier_call_chain(&cpufreq_policy_notifier_list, |
| 1357 | policy); | 1352 | CPUFREQ_INCOMPATIBLE, policy); |
| 1358 | 1353 | ||
| 1359 | /* verify the cpu speed can be set within this limit, | 1354 | /* verify the cpu speed can be set within this limit, |
| 1360 | which might be different to the first one */ | 1355 | which might be different to the first one */ |
| 1361 | ret = cpufreq_driver->verify(policy); | 1356 | ret = cpufreq_driver->verify(policy); |
| 1362 | if (ret) { | 1357 | if (ret) |
| 1363 | up_read(&cpufreq_notifier_rwsem); | ||
| 1364 | goto error_out; | 1358 | goto error_out; |
| 1365 | } | ||
| 1366 | 1359 | ||
| 1367 | /* notification of the new policy */ | 1360 | /* notification of the new policy */ |
| 1368 | notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY, | 1361 | blocking_notifier_call_chain(&cpufreq_policy_notifier_list, |
| 1369 | policy); | 1362 | CPUFREQ_NOTIFY, policy); |
| 1370 | |||
| 1371 | up_read(&cpufreq_notifier_rwsem); | ||
| 1372 | 1363 | ||
| 1373 | data->min = policy->min; | 1364 | data->min = policy->min; |
| 1374 | data->max = policy->max; | 1365 | data->max = policy->max; |
diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index d6543fc4a923..339f405ff708 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c | |||
| @@ -484,26 +484,15 @@ static void dcdbas_host_control(void) | |||
| 484 | static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, | 484 | static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, |
| 485 | void *unused) | 485 | void *unused) |
| 486 | { | 486 | { |
| 487 | static unsigned int notify_cnt = 0; | ||
| 488 | |||
| 489 | switch (code) { | 487 | switch (code) { |
| 490 | case SYS_DOWN: | 488 | case SYS_DOWN: |
| 491 | case SYS_HALT: | 489 | case SYS_HALT: |
| 492 | case SYS_POWER_OFF: | 490 | case SYS_POWER_OFF: |
| 493 | if (host_control_on_shutdown) { | 491 | if (host_control_on_shutdown) { |
| 494 | /* firmware is going to perform host control action */ | 492 | /* firmware is going to perform host control action */ |
| 495 | if (++notify_cnt == 2) { | 493 | printk(KERN_WARNING "Please wait for shutdown " |
| 496 | printk(KERN_WARNING | 494 | "action to complete...\n"); |
| 497 | "Please wait for shutdown " | 495 | dcdbas_host_control(); |
| 498 | "action to complete...\n"); | ||
| 499 | dcdbas_host_control(); | ||
| 500 | } | ||
| 501 | /* | ||
| 502 | * register again and initiate the host control | ||
| 503 | * action on the second notification to allow | ||
| 504 | * everyone that registered to be notified | ||
| 505 | */ | ||
| 506 | register_reboot_notifier(nb); | ||
| 507 | } | 496 | } |
| 508 | break; | 497 | break; |
| 509 | } | 498 | } |
| @@ -514,7 +503,7 @@ static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, | |||
| 514 | static struct notifier_block dcdbas_reboot_nb = { | 503 | static struct notifier_block dcdbas_reboot_nb = { |
| 515 | .notifier_call = dcdbas_reboot_notify, | 504 | .notifier_call = dcdbas_reboot_notify, |
| 516 | .next = NULL, | 505 | .next = NULL, |
| 517 | .priority = 0 | 506 | .priority = INT_MIN |
| 518 | }; | 507 | }; |
| 519 | 508 | ||
| 520 | static DCDBAS_BIN_ATTR_RW(smi_data); | 509 | static DCDBAS_BIN_ATTR_RW(smi_data); |
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index f9fae28f5612..7aa5c38f0855 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig | |||
| @@ -65,15 +65,6 @@ config SENSORS_PCF8591 | |||
| 65 | This driver can also be built as a module. If so, the module | 65 | This driver can also be built as a module. If so, the module |
| 66 | will be called pcf8591. | 66 | will be called pcf8591. |
| 67 | 67 | ||
| 68 | config SENSORS_RTC8564 | ||
| 69 | tristate "Epson 8564 RTC chip" | ||
| 70 | depends on I2C && EXPERIMENTAL | ||
| 71 | help | ||
| 72 | If you say yes here you get support for the Epson 8564 RTC chip. | ||
| 73 | |||
| 74 | This driver can also be built as a module. If so, the module | ||
| 75 | will be called i2c-rtc8564. | ||
| 76 | |||
| 77 | config ISP1301_OMAP | 68 | config ISP1301_OMAP |
| 78 | tristate "Philips ISP1301 with OMAP OTG" | 69 | tristate "Philips ISP1301 with OMAP OTG" |
| 79 | depends on I2C && ARCH_OMAP_OTG | 70 | depends on I2C && ARCH_OMAP_OTG |
| @@ -126,13 +117,4 @@ config SENSORS_MAX6875 | |||
| 126 | This driver can also be built as a module. If so, the module | 117 | This driver can also be built as a module. If so, the module |
| 127 | will be called max6875. | 118 | will be called max6875. |
| 128 | 119 | ||
| 129 | config RTC_X1205_I2C | ||
| 130 | tristate "Xicor X1205 RTC chip" | ||
| 131 | depends on I2C && EXPERIMENTAL | ||
| 132 | help | ||
| 133 | If you say yes here you get support for the Xicor X1205 RTC chip. | ||
| 134 | |||
| 135 | This driver can also be built as a module. If so, the module | ||
| 136 | will be called x1205. | ||
| 137 | |||
| 138 | endmenu | 120 | endmenu |
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 46178b57b1f1..779868ef2e26 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile | |||
| @@ -10,10 +10,8 @@ obj-$(CONFIG_SENSORS_M41T00) += m41t00.o | |||
| 10 | obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o | 10 | obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o |
| 11 | obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o | 11 | obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o |
| 12 | obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o | 12 | obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o |
| 13 | obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o | ||
| 14 | obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o | 13 | obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o |
| 15 | obj-$(CONFIG_TPS65010) += tps65010.o | 14 | obj-$(CONFIG_TPS65010) += tps65010.o |
| 16 | obj-$(CONFIG_RTC_X1205_I2C) += x1205.o | ||
| 17 | 15 | ||
| 18 | ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) | 16 | ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) |
| 19 | EXTRA_CFLAGS += -DDEBUG | 17 | EXTRA_CFLAGS += -DDEBUG |
diff --git a/drivers/i2c/chips/rtc8564.c b/drivers/i2c/chips/rtc8564.c deleted file mode 100644 index 0d8699b3f488..000000000000 --- a/drivers/i2c/chips/rtc8564.c +++ /dev/null | |||
| @@ -1,385 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * linux/drivers/i2c/chips/rtc8564.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2002-2004 Stefan Eletzhofer | ||
| 5 | * | ||
| 6 | * based on linux/drivers/acron/char/pcf8583.c | ||
| 7 | * Copyright (C) 2000 Russell King | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * Driver for system3's EPSON RTC 8564 chip | ||
| 14 | */ | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/bcd.h> | ||
| 18 | #include <linux/i2c.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <linux/string.h> | ||
| 21 | #include <linux/rtc.h> /* get the user-level API */ | ||
| 22 | #include <linux/init.h> | ||
| 23 | |||
| 24 | #include "rtc8564.h" | ||
| 25 | |||
| 26 | #ifdef DEBUG | ||
| 27 | # define _DBG(x, fmt, args...) do{ if (debug>=x) printk(KERN_DEBUG"%s: " fmt "\n", __FUNCTION__, ##args); } while(0); | ||
| 28 | #else | ||
| 29 | # define _DBG(x, fmt, args...) do { } while(0); | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #define _DBGRTCTM(x, rtctm) if (debug>=x) printk("%s: secs=%d, mins=%d, hours=%d, mday=%d, " \ | ||
| 33 | "mon=%d, year=%d, wday=%d VL=%d\n", __FUNCTION__, \ | ||
| 34 | (rtctm).secs, (rtctm).mins, (rtctm).hours, (rtctm).mday, \ | ||
| 35 | (rtctm).mon, (rtctm).year, (rtctm).wday, (rtctm).vl); | ||
| 36 | |||
| 37 | struct rtc8564_data { | ||
| 38 | struct i2c_client client; | ||
| 39 | u16 ctrl; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static inline u8 _rtc8564_ctrl1(struct i2c_client *client) | ||
| 43 | { | ||
| 44 | struct rtc8564_data *data = i2c_get_clientdata(client); | ||
| 45 | return data->ctrl & 0xff; | ||
| 46 | } | ||
| 47 | static inline u8 _rtc8564_ctrl2(struct i2c_client *client) | ||
| 48 | { | ||
| 49 | struct rtc8564_data *data = i2c_get_clientdata(client); | ||
| 50 | return (data->ctrl & 0xff00) >> 8; | ||
| 51 | } | ||
| 52 | |||
| 53 | #define CTRL1(c) _rtc8564_ctrl1(c) | ||
| 54 | #define CTRL2(c) _rtc8564_ctrl2(c) | ||
| 55 | |||
| 56 | static int debug; | ||
| 57 | module_param(debug, int, S_IRUGO | S_IWUSR); | ||
| 58 | |||
| 59 | static struct i2c_driver rtc8564_driver; | ||
| 60 | |||
| 61 | static unsigned short ignore[] = { I2C_CLIENT_END }; | ||
| 62 | static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END }; | ||
| 63 | |||
| 64 | static struct i2c_client_address_data addr_data = { | ||
| 65 | .normal_i2c = normal_addr, | ||
| 66 | .probe = ignore, | ||
| 67 | .ignore = ignore, | ||
| 68 | }; | ||
| 69 | |||
| 70 | static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem); | ||
| 71 | static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem); | ||
| 72 | |||
| 73 | static int rtc8564_read(struct i2c_client *client, unsigned char adr, | ||
| 74 | unsigned char *buf, unsigned char len) | ||
| 75 | { | ||
| 76 | int ret = -EIO; | ||
| 77 | unsigned char addr[1] = { adr }; | ||
| 78 | struct i2c_msg msgs[2] = { | ||
| 79 | {client->addr, 0, 1, addr}, | ||
| 80 | {client->addr, I2C_M_RD, len, buf} | ||
| 81 | }; | ||
| 82 | |||
| 83 | _DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, buf, len); | ||
| 84 | |||
| 85 | if (!buf) { | ||
| 86 | ret = -EINVAL; | ||
| 87 | goto done; | ||
| 88 | } | ||
| 89 | |||
| 90 | ret = i2c_transfer(client->adapter, msgs, 2); | ||
| 91 | if (ret == 2) { | ||
| 92 | ret = 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | done: | ||
| 96 | return ret; | ||
| 97 | } | ||
| 98 | |||
| 99 | static int rtc8564_write(struct i2c_client *client, unsigned char adr, | ||
| 100 | unsigned char *data, unsigned char len) | ||
| 101 | { | ||
| 102 | int ret = 0; | ||
| 103 | unsigned char _data[16]; | ||
| 104 | struct i2c_msg wr; | ||
| 105 | int i; | ||
| 106 | |||
| 107 | if (!data || len > 15) { | ||
| 108 | ret = -EINVAL; | ||
| 109 | goto done; | ||
| 110 | } | ||
| 111 | |||
| 112 | _DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, data, len); | ||
| 113 | |||
| 114 | _data[0] = adr; | ||
| 115 | for (i = 0; i < len; i++) { | ||
| 116 | _data[i + 1] = data[i]; | ||
| 117 | _DBG(5, "data[%d] = 0x%02x (%d)", i, data[i], data[i]); | ||
| 118 | } | ||
| 119 | |||
| 120 | wr.addr = client->addr; | ||
| 121 | wr.flags = 0; | ||
| 122 | wr.len = len + 1; | ||
| 123 | wr.buf = _data; | ||
| 124 | |||
| 125 | ret = i2c_transfer(client->adapter, &wr, 1); | ||
| 126 | if (ret == 1) { | ||
| 127 | ret = 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | done: | ||
| 131 | return ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | static int rtc8564_attach(struct i2c_adapter *adap, int addr, int kind) | ||
| 135 | { | ||
| 136 | int ret; | ||
| 137 | struct i2c_client *new_client; | ||
| 138 | struct rtc8564_data *d; | ||
| 139 | unsigned char data[10]; | ||
| 140 | unsigned char ad[1] = { 0 }; | ||
| 141 | struct i2c_msg ctrl_wr[1] = { | ||
| 142 | {addr, 0, 2, data} | ||
| 143 | }; | ||
| 144 | struct i2c_msg ctrl_rd[2] = { | ||
| 145 | {addr, 0, 1, ad}, | ||
| 146 | {addr, I2C_M_RD, 2, data} | ||
| 147 | }; | ||
| 148 | |||
| 149 | d = kzalloc(sizeof(struct rtc8564_data), GFP_KERNEL); | ||
| 150 | if (!d) { | ||
| 151 | ret = -ENOMEM; | ||
| 152 | goto done; | ||
| 153 | } | ||
| 154 | new_client = &d->client; | ||
| 155 | |||
| 156 | strlcpy(new_client->name, "RTC8564", I2C_NAME_SIZE); | ||
| 157 | i2c_set_clientdata(new_client, d); | ||
| 158 | new_client->addr = addr; | ||
| 159 | new_client->adapter = adap; | ||
| 160 | new_client->driver = &rtc8564_driver; | ||
| 161 | |||
| 162 | _DBG(1, "client=%p", new_client); | ||
| 163 | |||
| 164 | /* init ctrl1 reg */ | ||
| 165 | data[0] = 0; | ||
| 166 | data[1] = 0; | ||
| 167 | ret = i2c_transfer(new_client->adapter, ctrl_wr, 1); | ||
| 168 | if (ret != 1) { | ||
| 169 | printk(KERN_INFO "rtc8564: cant init ctrl1\n"); | ||
| 170 | ret = -ENODEV; | ||
| 171 | goto done; | ||
| 172 | } | ||
| 173 | |||
| 174 | /* read back ctrl1 and ctrl2 */ | ||
| 175 | ret = i2c_transfer(new_client->adapter, ctrl_rd, 2); | ||
| 176 | if (ret != 2) { | ||
| 177 | printk(KERN_INFO "rtc8564: cant read ctrl\n"); | ||
| 178 | ret = -ENODEV; | ||
| 179 | goto done; | ||
| 180 | } | ||
| 181 | |||
| 182 | d->ctrl = data[0] | (data[1] << 8); | ||
| 183 | |||
| 184 | _DBG(1, "RTC8564_REG_CTRL1=%02x, RTC8564_REG_CTRL2=%02x", | ||
| 185 | data[0], data[1]); | ||
| 186 | |||
| 187 | ret = i2c_attach_client(new_client); | ||
| 188 | done: | ||
| 189 | if (ret) { | ||
| 190 | kfree(d); | ||
| 191 | } | ||
| 192 | return ret; | ||
| 193 | } | ||
| 194 | |||
| 195 | static int rtc8564_probe(struct i2c_adapter *adap) | ||
| 196 | { | ||
| 197 | return i2c_probe(adap, &addr_data, rtc8564_attach); | ||
| 198 | } | ||
| 199 | |||
| 200 | static int rtc8564_detach(struct i2c_client *client) | ||
| 201 | { | ||
| 202 | i2c_detach_client(client); | ||
| 203 | kfree(i2c_get_clientdata(client)); | ||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | static int rtc8564_get_datetime(struct i2c_client *client, struct rtc_tm *dt) | ||
| 208 | { | ||
| 209 | int ret = -EIO; | ||
| 210 | unsigned char buf[15]; | ||
| 211 | |||
| 212 | _DBG(1, "client=%p, dt=%p", client, dt); | ||
| 213 | |||
| 214 | if (!dt) | ||
| 215 | return -EINVAL; | ||
| 216 | |||
| 217 | memset(buf, 0, sizeof(buf)); | ||
| 218 | |||
| 219 | ret = rtc8564_read(client, 0, buf, 15); | ||
| 220 | if (ret) | ||
| 221 | return ret; | ||
| 222 | |||
| 223 | /* century stored in minute alarm reg */ | ||
| 224 | dt->year = BCD2BIN(buf[RTC8564_REG_YEAR]); | ||
| 225 | dt->year += 100 * BCD2BIN(buf[RTC8564_REG_AL_MIN] & 0x3f); | ||
| 226 | dt->mday = BCD2BIN(buf[RTC8564_REG_DAY] & 0x3f); | ||
| 227 | dt->wday = BCD2BIN(buf[RTC8564_REG_WDAY] & 7); | ||
| 228 | dt->mon = BCD2BIN(buf[RTC8564_REG_MON_CENT] & 0x1f); | ||
| 229 | |||
| 230 | dt->secs = BCD2BIN(buf[RTC8564_REG_SEC] & 0x7f); | ||
| 231 | dt->vl = (buf[RTC8564_REG_SEC] & 0x80) == 0x80; | ||
| 232 | dt->mins = BCD2BIN(buf[RTC8564_REG_MIN] & 0x7f); | ||
| 233 | dt->hours = BCD2BIN(buf[RTC8564_REG_HR] & 0x3f); | ||
| 234 | |||
| 235 | _DBGRTCTM(2, *dt); | ||
| 236 | |||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | static int | ||
| 241 | rtc8564_set_datetime(struct i2c_client *client, struct rtc_tm *dt, int datetoo) | ||
| 242 | { | ||
| 243 | int ret, len = 5; | ||
| 244 | unsigned char buf[15]; | ||
| 245 | |||
| 246 | _DBG(1, "client=%p, dt=%p", client, dt); | ||
| 247 | |||
| 248 | if (!dt) | ||
| 249 | return -EINVAL; | ||
| 250 | |||
| 251 | _DBGRTCTM(2, *dt); | ||
| 252 | |||
| 253 | buf[RTC8564_REG_CTRL1] = CTRL1(client) | RTC8564_CTRL1_STOP; | ||
| 254 | buf[RTC8564_REG_CTRL2] = CTRL2(client); | ||
| 255 | buf[RTC8564_REG_SEC] = BIN2BCD(dt->secs); | ||
| 256 | buf[RTC8564_REG_MIN] = BIN2BCD(dt->mins); | ||
| 257 | buf[RTC8564_REG_HR] = BIN2BCD(dt->hours); | ||
| 258 | |||
| 259 | if (datetoo) { | ||
| 260 | len += 5; | ||
| 261 | buf[RTC8564_REG_DAY] = BIN2BCD(dt->mday); | ||
| 262 | buf[RTC8564_REG_WDAY] = BIN2BCD(dt->wday); | ||
| 263 | buf[RTC8564_REG_MON_CENT] = BIN2BCD(dt->mon) & 0x1f; | ||
| 264 | /* century stored in minute alarm reg */ | ||
| 265 | buf[RTC8564_REG_YEAR] = BIN2BCD(dt->year % 100); | ||
| 266 | buf[RTC8564_REG_AL_MIN] = BIN2BCD(dt->year / 100); | ||
| 267 | } | ||
| 268 | |||
| 269 | ret = rtc8564_write(client, 0, buf, len); | ||
| 270 | if (ret) { | ||
| 271 | _DBG(1, "error writing data! %d", ret); | ||
| 272 | } | ||
| 273 | |||
| 274 | buf[RTC8564_REG_CTRL1] = CTRL1(client); | ||
| 275 | ret = rtc8564_write(client, 0, buf, 1); | ||
| 276 | if (ret) { | ||
| 277 | _DBG(1, "error writing data! %d", ret); | ||
| 278 | } | ||
| 279 | |||
| 280 | return ret; | ||
| 281 | } | ||
| 282 | |||
| 283 | static int rtc8564_get_ctrl(struct i2c_client *client, unsigned int *ctrl) | ||
| 284 | { | ||
| 285 | struct rtc8564_data *data = i2c_get_clientdata(client); | ||
| 286 | |||
| 287 | if (!ctrl) | ||
| 288 | return -1; | ||
| 289 | |||
| 290 | *ctrl = data->ctrl; | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 294 | static int rtc8564_set_ctrl(struct i2c_client *client, unsigned int *ctrl) | ||
| 295 | { | ||
| 296 | struct rtc8564_data *data = i2c_get_clientdata(client); | ||
| 297 | unsigned char buf[2]; | ||
| 298 | |||
| 299 | if (!ctrl) | ||
| 300 | return -1; | ||
| 301 | |||
| 302 | buf[0] = *ctrl & 0xff; | ||
| 303 | buf[1] = (*ctrl & 0xff00) >> 8; | ||
| 304 | data->ctrl = *ctrl; | ||
| 305 | |||
| 306 | return rtc8564_write(client, 0, buf, 2); | ||
| 307 | } | ||
| 308 | |||
| 309 | static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem) | ||
| 310 | { | ||
| 311 | |||
| 312 | if (!mem) | ||
| 313 | return -EINVAL; | ||
| 314 | |||
| 315 | return rtc8564_read(client, mem->loc, mem->data, mem->nr); | ||
| 316 | } | ||
| 317 | |||
| 318 | static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem) | ||
| 319 | { | ||
| 320 | |||
| 321 | if (!mem) | ||
| 322 | return -EINVAL; | ||
| 323 | |||
| 324 | return rtc8564_write(client, mem->loc, mem->data, mem->nr); | ||
| 325 | } | ||
| 326 | |||
| 327 | static int | ||
| 328 | rtc8564_command(struct i2c_client *client, unsigned int cmd, void *arg) | ||
| 329 | { | ||
| 330 | |||
| 331 | _DBG(1, "cmd=%d", cmd); | ||
| 332 | |||
| 333 | switch (cmd) { | ||
| 334 | case RTC_GETDATETIME: | ||
| 335 | return rtc8564_get_datetime(client, arg); | ||
| 336 | |||
| 337 | case RTC_SETTIME: | ||
| 338 | return rtc8564_set_datetime(client, arg, 0); | ||
| 339 | |||
| 340 | case RTC_SETDATETIME: | ||
| 341 | return rtc8564_set_datetime(client, arg, 1); | ||
| 342 | |||
| 343 | case RTC_GETCTRL: | ||
| 344 | return rtc8564_get_ctrl(client, arg); | ||
| 345 | |||
| 346 | case RTC_SETCTRL: | ||
| 347 | return rtc8564_set_ctrl(client, arg); | ||
| 348 | |||
| 349 | case MEM_READ: | ||
| 350 | return rtc8564_read_mem(client, arg); | ||
| 351 | |||
| 352 | case MEM_WRITE: | ||
| 353 | return rtc8564_write_mem(client, arg); | ||
| 354 | |||
| 355 | default: | ||
| 356 | return -EINVAL; | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | static struct i2c_driver rtc8564_driver = { | ||
| 361 | .driver = { | ||
| 362 | .name = "RTC8564", | ||
| 363 | }, | ||
| 364 | .id = I2C_DRIVERID_RTC8564, | ||
| 365 | .attach_adapter = rtc8564_probe, | ||
| 366 | .detach_client = rtc8564_detach, | ||
| 367 | .command = rtc8564_command | ||
| 368 | }; | ||
| 369 | |||
| 370 | static __init int rtc8564_init(void) | ||
| 371 | { | ||
| 372 | return i2c_add_driver(&rtc8564_driver); | ||
| 373 | } | ||
| 374 | |||
| 375 | static __exit void rtc8564_exit(void) | ||
| 376 | { | ||
| 377 | i2c_del_driver(&rtc8564_driver); | ||
| 378 | } | ||
| 379 | |||
| 380 | MODULE_AUTHOR("Stefan Eletzhofer <Stefan.Eletzhofer@eletztrick.de>"); | ||
| 381 | MODULE_DESCRIPTION("EPSON RTC8564 Driver"); | ||
| 382 | MODULE_LICENSE("GPL"); | ||
| 383 | |||
| 384 | module_init(rtc8564_init); | ||
| 385 | module_exit(rtc8564_exit); | ||
diff --git a/drivers/i2c/chips/rtc8564.h b/drivers/i2c/chips/rtc8564.h deleted file mode 100644 index e5342d10b8fa..000000000000 --- a/drivers/i2c/chips/rtc8564.h +++ /dev/null | |||
| @@ -1,78 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * linux/drivers/i2c/chips/rtc8564.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2002-2004 Stefan Eletzhofer | ||
| 5 | * | ||
| 6 | * based on linux/drivers/acron/char/pcf8583.h | ||
| 7 | * Copyright (C) 2000 Russell King | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | struct rtc_tm { | ||
| 14 | unsigned char secs; | ||
| 15 | unsigned char mins; | ||
| 16 | unsigned char hours; | ||
| 17 | unsigned char mday; | ||
| 18 | unsigned char mon; | ||
| 19 | unsigned short year; /* xxxx 4 digits :) */ | ||
| 20 | unsigned char wday; | ||
| 21 | unsigned char vl; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct mem { | ||
| 25 | unsigned int loc; | ||
| 26 | unsigned int nr; | ||
| 27 | unsigned char *data; | ||
| 28 | }; | ||
| 29 | |||
| 30 | #define RTC_GETDATETIME 0 | ||
| 31 | #define RTC_SETTIME 1 | ||
| 32 | #define RTC_SETDATETIME 2 | ||
| 33 | #define RTC_GETCTRL 3 | ||
| 34 | #define RTC_SETCTRL 4 | ||
| 35 | #define MEM_READ 5 | ||
| 36 | #define MEM_WRITE 6 | ||
| 37 | |||
| 38 | #define RTC8564_REG_CTRL1 0x0 /* T 0 S 0 | T 0 0 0 */ | ||
| 39 | #define RTC8564_REG_CTRL2 0x1 /* 0 0 0 TI/TP | AF TF AIE TIE */ | ||
| 40 | #define RTC8564_REG_SEC 0x2 /* VL 4 2 1 | 8 4 2 1 */ | ||
| 41 | #define RTC8564_REG_MIN 0x3 /* x 4 2 1 | 8 4 2 1 */ | ||
| 42 | #define RTC8564_REG_HR 0x4 /* x x 2 1 | 8 4 2 1 */ | ||
| 43 | #define RTC8564_REG_DAY 0x5 /* x x 2 1 | 8 4 2 1 */ | ||
| 44 | #define RTC8564_REG_WDAY 0x6 /* x x x x | x 4 2 1 */ | ||
| 45 | #define RTC8564_REG_MON_CENT 0x7 /* C x x 1 | 8 4 2 1 */ | ||
| 46 | #define RTC8564_REG_YEAR 0x8 /* 8 4 2 1 | 8 4 2 1 */ | ||
| 47 | #define RTC8564_REG_AL_MIN 0x9 /* AE 4 2 1 | 8 4 2 1 */ | ||
| 48 | #define RTC8564_REG_AL_HR 0xa /* AE 4 2 1 | 8 4 2 1 */ | ||
| 49 | #define RTC8564_REG_AL_DAY 0xb /* AE x 2 1 | 8 4 2 1 */ | ||
| 50 | #define RTC8564_REG_AL_WDAY 0xc /* AE x x x | x 4 2 1 */ | ||
| 51 | #define RTC8564_REG_CLKOUT 0xd /* FE x x x | x x FD1 FD0 */ | ||
| 52 | #define RTC8564_REG_TCTL 0xe /* TE x x x | x x FD1 FD0 */ | ||
| 53 | #define RTC8564_REG_TIMER 0xf /* 8 bit binary */ | ||
| 54 | |||
| 55 | /* Control reg */ | ||
| 56 | #define RTC8564_CTRL1_TEST1 (1<<3) | ||
| 57 | #define RTC8564_CTRL1_STOP (1<<5) | ||
| 58 | #define RTC8564_CTRL1_TEST2 (1<<7) | ||
| 59 | |||
| 60 | #define RTC8564_CTRL2_TIE (1<<0) | ||
| 61 | #define RTC8564_CTRL2_AIE (1<<1) | ||
| 62 | #define RTC8564_CTRL2_TF (1<<2) | ||
| 63 | #define RTC8564_CTRL2_AF (1<<3) | ||
| 64 | #define RTC8564_CTRL2_TI_TP (1<<4) | ||
| 65 | |||
| 66 | /* CLKOUT frequencies */ | ||
| 67 | #define RTC8564_FD_32768HZ (0x0) | ||
| 68 | #define RTC8564_FD_1024HZ (0x1) | ||
| 69 | #define RTC8564_FD_32 (0x2) | ||
| 70 | #define RTC8564_FD_1HZ (0x3) | ||
| 71 | |||
| 72 | /* Timer CTRL */ | ||
| 73 | #define RTC8564_TD_4096HZ (0x0) | ||
| 74 | #define RTC8564_TD_64HZ (0x1) | ||
| 75 | #define RTC8564_TD_1HZ (0x2) | ||
| 76 | #define RTC8564_TD_1_60HZ (0x3) | ||
| 77 | |||
| 78 | #define I2C_DRIVERID_RTC8564 0xf000 | ||
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index d2ead1776c16..34fcabac5fdb 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c | |||
| @@ -80,7 +80,7 @@ static struct adb_driver *adb_driver_list[] = { | |||
| 80 | static struct class *adb_dev_class; | 80 | static struct class *adb_dev_class; |
| 81 | 81 | ||
| 82 | struct adb_driver *adb_controller; | 82 | struct adb_driver *adb_controller; |
| 83 | struct notifier_block *adb_client_list = NULL; | 83 | BLOCKING_NOTIFIER_HEAD(adb_client_list); |
| 84 | static int adb_got_sleep; | 84 | static int adb_got_sleep; |
| 85 | static int adb_inited; | 85 | static int adb_inited; |
| 86 | static pid_t adb_probe_task_pid; | 86 | static pid_t adb_probe_task_pid; |
| @@ -354,7 +354,8 @@ adb_notify_sleep(struct pmu_sleep_notifier *self, int when) | |||
| 354 | /* Stop autopoll */ | 354 | /* Stop autopoll */ |
| 355 | if (adb_controller->autopoll) | 355 | if (adb_controller->autopoll) |
| 356 | adb_controller->autopoll(0); | 356 | adb_controller->autopoll(0); |
| 357 | ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); | 357 | ret = blocking_notifier_call_chain(&adb_client_list, |
| 358 | ADB_MSG_POWERDOWN, NULL); | ||
| 358 | if (ret & NOTIFY_STOP_MASK) { | 359 | if (ret & NOTIFY_STOP_MASK) { |
| 359 | up(&adb_probe_mutex); | 360 | up(&adb_probe_mutex); |
| 360 | return PBOOK_SLEEP_REFUSE; | 361 | return PBOOK_SLEEP_REFUSE; |
| @@ -391,7 +392,8 @@ do_adb_reset_bus(void) | |||
| 391 | if (adb_controller->autopoll) | 392 | if (adb_controller->autopoll) |
| 392 | adb_controller->autopoll(0); | 393 | adb_controller->autopoll(0); |
| 393 | 394 | ||
| 394 | nret = notifier_call_chain(&adb_client_list, ADB_MSG_PRE_RESET, NULL); | 395 | nret = blocking_notifier_call_chain(&adb_client_list, |
| 396 | ADB_MSG_PRE_RESET, NULL); | ||
| 395 | if (nret & NOTIFY_STOP_MASK) { | 397 | if (nret & NOTIFY_STOP_MASK) { |
| 396 | if (adb_controller->autopoll) | 398 | if (adb_controller->autopoll) |
| 397 | adb_controller->autopoll(autopoll_devs); | 399 | adb_controller->autopoll(autopoll_devs); |
| @@ -426,7 +428,8 @@ do_adb_reset_bus(void) | |||
| 426 | } | 428 | } |
| 427 | up(&adb_handler_sem); | 429 | up(&adb_handler_sem); |
| 428 | 430 | ||
| 429 | nret = notifier_call_chain(&adb_client_list, ADB_MSG_POST_RESET, NULL); | 431 | nret = blocking_notifier_call_chain(&adb_client_list, |
| 432 | ADB_MSG_POST_RESET, NULL); | ||
| 430 | if (nret & NOTIFY_STOP_MASK) | 433 | if (nret & NOTIFY_STOP_MASK) |
| 431 | return -EBUSY; | 434 | return -EBUSY; |
| 432 | 435 | ||
diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c index c0b46bceb5df..f5779a73184d 100644 --- a/drivers/macintosh/adbhid.c +++ b/drivers/macintosh/adbhid.c | |||
| @@ -1214,7 +1214,8 @@ static int __init adbhid_init(void) | |||
| 1214 | 1214 | ||
| 1215 | adbhid_probe(); | 1215 | adbhid_probe(); |
| 1216 | 1216 | ||
| 1217 | notifier_chain_register(&adb_client_list, &adbhid_adb_notifier); | 1217 | blocking_notifier_chain_register(&adb_client_list, |
| 1218 | &adbhid_adb_notifier); | ||
| 1218 | 1219 | ||
| 1219 | return 0; | 1220 | return 0; |
| 1220 | } | 1221 | } |
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 4f5f3abc9cb3..0b5ff553e39a 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c | |||
| @@ -187,7 +187,7 @@ extern int disable_kernel_backlight; | |||
| 187 | 187 | ||
| 188 | int __fake_sleep; | 188 | int __fake_sleep; |
| 189 | int asleep; | 189 | int asleep; |
| 190 | struct notifier_block *sleep_notifier_list; | 190 | BLOCKING_NOTIFIER_HEAD(sleep_notifier_list); |
| 191 | 191 | ||
| 192 | #ifdef CONFIG_ADB | 192 | #ifdef CONFIG_ADB |
| 193 | static int adb_dev_map = 0; | 193 | static int adb_dev_map = 0; |
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c index f08e52f2107b..35b70323e7e3 100644 --- a/drivers/macintosh/via-pmu68k.c +++ b/drivers/macintosh/via-pmu68k.c | |||
| @@ -102,7 +102,7 @@ static int pmu_kind = PMU_UNKNOWN; | |||
| 102 | static int pmu_fully_inited = 0; | 102 | static int pmu_fully_inited = 0; |
| 103 | 103 | ||
| 104 | int asleep; | 104 | int asleep; |
| 105 | struct notifier_block *sleep_notifier_list; | 105 | BLOCKING_NOTIFIER_HEAD(sleep_notifier_list); |
| 106 | 106 | ||
| 107 | static int pmu_probe(void); | 107 | static int pmu_probe(void); |
| 108 | static int pmu_init(void); | 108 | static int pmu_init(void); |
| @@ -913,7 +913,8 @@ int powerbook_sleep(void) | |||
| 913 | struct adb_request sleep_req; | 913 | struct adb_request sleep_req; |
| 914 | 914 | ||
| 915 | /* Notify device drivers */ | 915 | /* Notify device drivers */ |
| 916 | ret = notifier_call_chain(&sleep_notifier_list, PBOOK_SLEEP, NULL); | 916 | ret = blocking_notifier_call_chain(&sleep_notifier_list, |
| 917 | PBOOK_SLEEP, NULL); | ||
| 917 | if (ret & NOTIFY_STOP_MASK) | 918 | if (ret & NOTIFY_STOP_MASK) |
| 918 | return -EBUSY; | 919 | return -EBUSY; |
| 919 | 920 | ||
| @@ -984,7 +985,7 @@ int powerbook_sleep(void) | |||
| 984 | enable_irq(i); | 985 | enable_irq(i); |
| 985 | 986 | ||
| 986 | /* Notify drivers */ | 987 | /* Notify drivers */ |
| 987 | notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL); | 988 | blocking_notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL); |
| 988 | 989 | ||
| 989 | /* reenable ADB autopoll */ | 990 | /* reenable ADB autopoll */ |
| 990 | pmu_adb_autopoll(adb_dev_map); | 991 | pmu_adb_autopoll(adb_dev_map); |
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 6c0ba04bc57a..ab3faa702d58 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c | |||
| @@ -52,7 +52,7 @@ | |||
| 52 | static LIST_HEAD(wf_controls); | 52 | static LIST_HEAD(wf_controls); |
| 53 | static LIST_HEAD(wf_sensors); | 53 | static LIST_HEAD(wf_sensors); |
| 54 | static DEFINE_MUTEX(wf_lock); | 54 | static DEFINE_MUTEX(wf_lock); |
| 55 | static struct notifier_block *wf_client_list; | 55 | static BLOCKING_NOTIFIER_HEAD(wf_client_list); |
| 56 | static int wf_client_count; | 56 | static int wf_client_count; |
| 57 | static unsigned int wf_overtemp; | 57 | static unsigned int wf_overtemp; |
| 58 | static unsigned int wf_overtemp_counter; | 58 | static unsigned int wf_overtemp_counter; |
| @@ -68,7 +68,7 @@ static struct platform_device wf_platform_device = { | |||
| 68 | 68 | ||
| 69 | static inline void wf_notify(int event, void *param) | 69 | static inline void wf_notify(int event, void *param) |
| 70 | { | 70 | { |
| 71 | notifier_call_chain(&wf_client_list, event, param); | 71 | blocking_notifier_call_chain(&wf_client_list, event, param); |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | int wf_critical_overtemp(void) | 74 | int wf_critical_overtemp(void) |
| @@ -398,7 +398,7 @@ int wf_register_client(struct notifier_block *nb) | |||
| 398 | struct wf_sensor *sr; | 398 | struct wf_sensor *sr; |
| 399 | 399 | ||
| 400 | mutex_lock(&wf_lock); | 400 | mutex_lock(&wf_lock); |
| 401 | rc = notifier_chain_register(&wf_client_list, nb); | 401 | rc = blocking_notifier_chain_register(&wf_client_list, nb); |
| 402 | if (rc != 0) | 402 | if (rc != 0) |
| 403 | goto bail; | 403 | goto bail; |
| 404 | wf_client_count++; | 404 | wf_client_count++; |
| @@ -417,7 +417,7 @@ EXPORT_SYMBOL_GPL(wf_register_client); | |||
| 417 | int wf_unregister_client(struct notifier_block *nb) | 417 | int wf_unregister_client(struct notifier_block *nb) |
| 418 | { | 418 | { |
| 419 | mutex_lock(&wf_lock); | 419 | mutex_lock(&wf_lock); |
| 420 | notifier_chain_unregister(&wf_client_list, nb); | 420 | blocking_notifier_chain_unregister(&wf_client_list, nb); |
| 421 | wf_client_count++; | 421 | wf_client_count++; |
| 422 | if (wf_client_count == 0) | 422 | if (wf_client_count == 0) |
| 423 | wf_stop_thread(); | 423 | wf_stop_thread(); |
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index ac43f98062fd..fd2aae150ccc 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig | |||
| @@ -127,6 +127,32 @@ config MD_RAID5 | |||
| 127 | 127 | ||
| 128 | If unsure, say Y. | 128 | If unsure, say Y. |
| 129 | 129 | ||
| 130 | config MD_RAID5_RESHAPE | ||
| 131 | bool "Support adding drives to a raid-5 array (experimental)" | ||
| 132 | depends on MD_RAID5 && EXPERIMENTAL | ||
| 133 | ---help--- | ||
| 134 | A RAID-5 set can be expanded by adding extra drives. This | ||
| 135 | requires "restriping" the array which means (almost) every | ||
| 136 | block must be written to a different place. | ||
| 137 | |||
| 138 | This option allows such restriping to be done while the array | ||
| 139 | is online. However it is still EXPERIMENTAL code. It should | ||
| 140 | work, but please be sure that you have backups. | ||
| 141 | |||
| 142 | You will need a version of mdadm newer than 2.3.1. During the | ||
| 143 | early stage of reshape there is a critical section where live data | ||
| 144 | is being over-written. A crash during this time needs extra care | ||
| 145 | for recovery. The newer mdadm takes a copy of the data in the | ||
| 146 | critical section and will restore it, if necessary, after a crash. | ||
| 147 | |||
| 148 | The mdadm usage is e.g. | ||
| 149 | mdadm --grow /dev/md1 --raid-disks=6 | ||
| 150 | to grow '/dev/md1' to having 6 disks. | ||
| 151 | |||
| 152 | Note: The array can only be expanded, not contracted. | ||
| 153 | There should be enough spares already present to make the new | ||
| 154 | array workable. | ||
| 155 | |||
| 130 | config MD_RAID6 | 156 | config MD_RAID6 |
| 131 | tristate "RAID-6 mode" | 157 | tristate "RAID-6 mode" |
| 132 | depends on BLK_DEV_MD | 158 | depends on BLK_DEV_MD |
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 259e86f26549..61a590bb6241 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c | |||
| @@ -518,6 +518,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 518 | char *ivopts; | 518 | char *ivopts; |
| 519 | unsigned int crypto_flags; | 519 | unsigned int crypto_flags; |
| 520 | unsigned int key_size; | 520 | unsigned int key_size; |
| 521 | unsigned long long tmpll; | ||
| 521 | 522 | ||
| 522 | if (argc != 5) { | 523 | if (argc != 5) { |
| 523 | ti->error = PFX "Not enough arguments"; | 524 | ti->error = PFX "Not enough arguments"; |
| @@ -633,15 +634,17 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 633 | goto bad5; | 634 | goto bad5; |
| 634 | } | 635 | } |
| 635 | 636 | ||
| 636 | if (sscanf(argv[2], SECTOR_FORMAT, &cc->iv_offset) != 1) { | 637 | if (sscanf(argv[2], "%llu", &tmpll) != 1) { |
| 637 | ti->error = PFX "Invalid iv_offset sector"; | 638 | ti->error = PFX "Invalid iv_offset sector"; |
| 638 | goto bad5; | 639 | goto bad5; |
| 639 | } | 640 | } |
| 641 | cc->iv_offset = tmpll; | ||
| 640 | 642 | ||
| 641 | if (sscanf(argv[4], SECTOR_FORMAT, &cc->start) != 1) { | 643 | if (sscanf(argv[4], "%llu", &tmpll) != 1) { |
| 642 | ti->error = PFX "Invalid device sector"; | 644 | ti->error = PFX "Invalid device sector"; |
| 643 | goto bad5; | 645 | goto bad5; |
| 644 | } | 646 | } |
| 647 | cc->start = tmpll; | ||
| 645 | 648 | ||
| 646 | if (dm_get_device(ti, argv[3], cc->start, ti->len, | 649 | if (dm_get_device(ti, argv[3], cc->start, ti->len, |
| 647 | dm_table_get_mode(ti->table), &cc->dev)) { | 650 | dm_table_get_mode(ti->table), &cc->dev)) { |
| @@ -885,8 +888,8 @@ static int crypt_status(struct dm_target *ti, status_type_t type, | |||
| 885 | result[sz++] = '-'; | 888 | result[sz++] = '-'; |
| 886 | } | 889 | } |
| 887 | 890 | ||
| 888 | DMEMIT(" " SECTOR_FORMAT " %s " SECTOR_FORMAT, | 891 | DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset, |
| 889 | cc->iv_offset, cc->dev->name, cc->start); | 892 | cc->dev->name, (unsigned long long)cc->start); |
| 890 | break; | 893 | break; |
| 891 | } | 894 | } |
| 892 | return 0; | 895 | return 0; |
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 442e2be6052e..8edd6435414d 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
| 16 | #include <linux/devfs_fs_kernel.h> | 16 | #include <linux/devfs_fs_kernel.h> |
| 17 | #include <linux/dm-ioctl.h> | 17 | #include <linux/dm-ioctl.h> |
| 18 | #include <linux/hdreg.h> | ||
| 18 | 19 | ||
| 19 | #include <asm/uaccess.h> | 20 | #include <asm/uaccess.h> |
| 20 | 21 | ||
| @@ -244,9 +245,9 @@ static void __hash_remove(struct hash_cell *hc) | |||
| 244 | dm_table_put(table); | 245 | dm_table_put(table); |
| 245 | } | 246 | } |
| 246 | 247 | ||
| 247 | dm_put(hc->md); | ||
| 248 | if (hc->new_map) | 248 | if (hc->new_map) |
| 249 | dm_table_put(hc->new_map); | 249 | dm_table_put(hc->new_map); |
| 250 | dm_put(hc->md); | ||
| 250 | free_cell(hc); | 251 | free_cell(hc); |
| 251 | } | 252 | } |
| 252 | 253 | ||
| @@ -600,12 +601,22 @@ static int dev_create(struct dm_ioctl *param, size_t param_size) | |||
| 600 | */ | 601 | */ |
| 601 | static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) | 602 | static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) |
| 602 | { | 603 | { |
| 604 | struct mapped_device *md; | ||
| 605 | void *mdptr = NULL; | ||
| 606 | |||
| 603 | if (*param->uuid) | 607 | if (*param->uuid) |
| 604 | return __get_uuid_cell(param->uuid); | 608 | return __get_uuid_cell(param->uuid); |
| 605 | else if (*param->name) | 609 | |
| 610 | if (*param->name) | ||
| 606 | return __get_name_cell(param->name); | 611 | return __get_name_cell(param->name); |
| 607 | else | 612 | |
| 608 | return dm_get_mdptr(huge_decode_dev(param->dev)); | 613 | md = dm_get_md(huge_decode_dev(param->dev)); |
| 614 | if (md) { | ||
| 615 | mdptr = dm_get_mdptr(md); | ||
| 616 | dm_put(md); | ||
| 617 | } | ||
| 618 | |||
| 619 | return mdptr; | ||
| 609 | } | 620 | } |
| 610 | 621 | ||
| 611 | static struct mapped_device *find_device(struct dm_ioctl *param) | 622 | static struct mapped_device *find_device(struct dm_ioctl *param) |
| @@ -690,6 +701,54 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size) | |||
| 690 | return dm_hash_rename(param->name, new_name); | 701 | return dm_hash_rename(param->name, new_name); |
| 691 | } | 702 | } |
| 692 | 703 | ||
| 704 | static int dev_set_geometry(struct dm_ioctl *param, size_t param_size) | ||
| 705 | { | ||
| 706 | int r = -EINVAL, x; | ||
| 707 | struct mapped_device *md; | ||
| 708 | struct hd_geometry geometry; | ||
| 709 | unsigned long indata[4]; | ||
| 710 | char *geostr = (char *) param + param->data_start; | ||
| 711 | |||
| 712 | md = find_device(param); | ||
| 713 | if (!md) | ||
| 714 | return -ENXIO; | ||
| 715 | |||
| 716 | if (geostr < (char *) (param + 1) || | ||
| 717 | invalid_str(geostr, (void *) param + param_size)) { | ||
| 718 | DMWARN("Invalid geometry supplied."); | ||
| 719 | goto out; | ||
| 720 | } | ||
| 721 | |||
| 722 | x = sscanf(geostr, "%lu %lu %lu %lu", indata, | ||
| 723 | indata + 1, indata + 2, indata + 3); | ||
| 724 | |||
| 725 | if (x != 4) { | ||
| 726 | DMWARN("Unable to interpret geometry settings."); | ||
| 727 | goto out; | ||
| 728 | } | ||
| 729 | |||
| 730 | if (indata[0] > 65535 || indata[1] > 255 || | ||
| 731 | indata[2] > 255 || indata[3] > ULONG_MAX) { | ||
| 732 | DMWARN("Geometry exceeds range limits."); | ||
| 733 | goto out; | ||
| 734 | } | ||
| 735 | |||
| 736 | geometry.cylinders = indata[0]; | ||
| 737 | geometry.heads = indata[1]; | ||
| 738 | geometry.sectors = indata[2]; | ||
| 739 | geometry.start = indata[3]; | ||
| 740 | |||
| 741 | r = dm_set_geometry(md, &geometry); | ||
| 742 | if (!r) | ||
| 743 | r = __dev_status(md, param); | ||
| 744 | |||
| 745 | param->data_size = 0; | ||
| 746 | |||
| 747 | out: | ||
| 748 | dm_put(md); | ||
| 749 | return r; | ||
| 750 | } | ||
| 751 | |||
| 693 | static int do_suspend(struct dm_ioctl *param) | 752 | static int do_suspend(struct dm_ioctl *param) |
| 694 | { | 753 | { |
| 695 | int r = 0; | 754 | int r = 0; |
| @@ -975,33 +1034,43 @@ static int table_load(struct dm_ioctl *param, size_t param_size) | |||
| 975 | int r; | 1034 | int r; |
| 976 | struct hash_cell *hc; | 1035 | struct hash_cell *hc; |
| 977 | struct dm_table *t; | 1036 | struct dm_table *t; |
| 1037 | struct mapped_device *md; | ||
| 978 | 1038 | ||
| 979 | r = dm_table_create(&t, get_mode(param), param->target_count); | 1039 | md = find_device(param); |
| 1040 | if (!md) | ||
| 1041 | return -ENXIO; | ||
| 1042 | |||
| 1043 | r = dm_table_create(&t, get_mode(param), param->target_count, md); | ||
| 980 | if (r) | 1044 | if (r) |
| 981 | return r; | 1045 | goto out; |
| 982 | 1046 | ||
| 983 | r = populate_table(t, param, param_size); | 1047 | r = populate_table(t, param, param_size); |
| 984 | if (r) { | 1048 | if (r) { |
| 985 | dm_table_put(t); | 1049 | dm_table_put(t); |
| 986 | return r; | 1050 | goto out; |
| 987 | } | 1051 | } |
| 988 | 1052 | ||
| 989 | down_write(&_hash_lock); | 1053 | down_write(&_hash_lock); |
| 990 | hc = __find_device_hash_cell(param); | 1054 | hc = dm_get_mdptr(md); |
| 991 | if (!hc) { | 1055 | if (!hc || hc->md != md) { |
| 992 | DMWARN("device doesn't appear to be in the dev hash table."); | 1056 | DMWARN("device has been removed from the dev hash table."); |
| 993 | up_write(&_hash_lock); | ||
| 994 | dm_table_put(t); | 1057 | dm_table_put(t); |
| 995 | return -ENXIO; | 1058 | up_write(&_hash_lock); |
| 1059 | r = -ENXIO; | ||
| 1060 | goto out; | ||
| 996 | } | 1061 | } |
| 997 | 1062 | ||
| 998 | if (hc->new_map) | 1063 | if (hc->new_map) |
| 999 | dm_table_put(hc->new_map); | 1064 | dm_table_put(hc->new_map); |
| 1000 | hc->new_map = t; | 1065 | hc->new_map = t; |
| 1066 | up_write(&_hash_lock); | ||
| 1067 | |||
| 1001 | param->flags |= DM_INACTIVE_PRESENT_FLAG; | 1068 | param->flags |= DM_INACTIVE_PRESENT_FLAG; |
| 1069 | r = __dev_status(md, param); | ||
| 1070 | |||
| 1071 | out: | ||
| 1072 | dm_put(md); | ||
| 1002 | 1073 | ||
| 1003 | r = __dev_status(hc->md, param); | ||
| 1004 | up_write(&_hash_lock); | ||
| 1005 | return r; | 1074 | return r; |
| 1006 | } | 1075 | } |
| 1007 | 1076 | ||
| @@ -1214,7 +1283,8 @@ static ioctl_fn lookup_ioctl(unsigned int cmd) | |||
| 1214 | 1283 | ||
| 1215 | {DM_LIST_VERSIONS_CMD, list_versions}, | 1284 | {DM_LIST_VERSIONS_CMD, list_versions}, |
| 1216 | 1285 | ||
| 1217 | {DM_TARGET_MSG_CMD, target_message} | 1286 | {DM_TARGET_MSG_CMD, target_message}, |
| 1287 | {DM_DEV_SET_GEOMETRY_CMD, dev_set_geometry} | ||
| 1218 | }; | 1288 | }; |
| 1219 | 1289 | ||
| 1220 | return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; | 1290 | return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; |
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 6a2cd5dc8a63..daf586c0898d 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c | |||
| @@ -26,6 +26,7 @@ struct linear_c { | |||
| 26 | static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) | 26 | static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
| 27 | { | 27 | { |
| 28 | struct linear_c *lc; | 28 | struct linear_c *lc; |
| 29 | unsigned long long tmp; | ||
| 29 | 30 | ||
| 30 | if (argc != 2) { | 31 | if (argc != 2) { |
| 31 | ti->error = "dm-linear: Invalid argument count"; | 32 | ti->error = "dm-linear: Invalid argument count"; |
| @@ -38,10 +39,11 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 38 | return -ENOMEM; | 39 | return -ENOMEM; |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | if (sscanf(argv[1], SECTOR_FORMAT, &lc->start) != 1) { | 42 | if (sscanf(argv[1], "%llu", &tmp) != 1) { |
| 42 | ti->error = "dm-linear: Invalid device sector"; | 43 | ti->error = "dm-linear: Invalid device sector"; |
| 43 | goto bad; | 44 | goto bad; |
| 44 | } | 45 | } |
| 46 | lc->start = tmp; | ||
| 45 | 47 | ||
| 46 | if (dm_get_device(ti, argv[0], lc->start, ti->len, | 48 | if (dm_get_device(ti, argv[0], lc->start, ti->len, |
| 47 | dm_table_get_mode(ti->table), &lc->dev)) { | 49 | dm_table_get_mode(ti->table), &lc->dev)) { |
| @@ -87,8 +89,8 @@ static int linear_status(struct dm_target *ti, status_type_t type, | |||
| 87 | break; | 89 | break; |
| 88 | 90 | ||
| 89 | case STATUSTYPE_TABLE: | 91 | case STATUSTYPE_TABLE: |
| 90 | snprintf(result, maxlen, "%s " SECTOR_FORMAT, lc->dev->name, | 92 | snprintf(result, maxlen, "%s %llu", lc->dev->name, |
| 91 | lc->start); | 93 | (unsigned long long)lc->start); |
| 92 | break; | 94 | break; |
| 93 | } | 95 | } |
| 94 | return 0; | 96 | return 0; |
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 4e90f231fbfb..d12cf3e5e076 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c | |||
| @@ -402,9 +402,21 @@ static void rh_dec(struct region_hash *rh, region_t region) | |||
| 402 | 402 | ||
| 403 | spin_lock_irqsave(&rh->region_lock, flags); | 403 | spin_lock_irqsave(&rh->region_lock, flags); |
| 404 | if (atomic_dec_and_test(®->pending)) { | 404 | if (atomic_dec_and_test(®->pending)) { |
| 405 | /* | ||
| 406 | * There is no pending I/O for this region. | ||
| 407 | * We can move the region to corresponding list for next action. | ||
| 408 | * At this point, the region is not yet connected to any list. | ||
| 409 | * | ||
| 410 | * If the state is RH_NOSYNC, the region should be kept off | ||
| 411 | * from clean list. | ||
| 412 | * The hash entry for RH_NOSYNC will remain in memory | ||
| 413 | * until the region is recovered or the map is reloaded. | ||
| 414 | */ | ||
| 415 | |||
| 416 | /* do nothing for RH_NOSYNC */ | ||
| 405 | if (reg->state == RH_RECOVERING) { | 417 | if (reg->state == RH_RECOVERING) { |
| 406 | list_add_tail(®->list, &rh->quiesced_regions); | 418 | list_add_tail(®->list, &rh->quiesced_regions); |
| 407 | } else { | 419 | } else if (reg->state == RH_DIRTY) { |
| 408 | reg->state = RH_CLEAN; | 420 | reg->state = RH_CLEAN; |
| 409 | list_add(®->list, &rh->clean_regions); | 421 | list_add(®->list, &rh->clean_regions); |
| 410 | } | 422 | } |
| @@ -922,9 +934,9 @@ static inline int _check_region_size(struct dm_target *ti, uint32_t size) | |||
| 922 | static int get_mirror(struct mirror_set *ms, struct dm_target *ti, | 934 | static int get_mirror(struct mirror_set *ms, struct dm_target *ti, |
| 923 | unsigned int mirror, char **argv) | 935 | unsigned int mirror, char **argv) |
| 924 | { | 936 | { |
| 925 | sector_t offset; | 937 | unsigned long long offset; |
| 926 | 938 | ||
| 927 | if (sscanf(argv[1], SECTOR_FORMAT, &offset) != 1) { | 939 | if (sscanf(argv[1], "%llu", &offset) != 1) { |
| 928 | ti->error = "dm-mirror: Invalid offset"; | 940 | ti->error = "dm-mirror: Invalid offset"; |
| 929 | return -EINVAL; | 941 | return -EINVAL; |
| 930 | } | 942 | } |
| @@ -1191,16 +1203,17 @@ static int mirror_status(struct dm_target *ti, status_type_t type, | |||
| 1191 | for (m = 0; m < ms->nr_mirrors; m++) | 1203 | for (m = 0; m < ms->nr_mirrors; m++) |
| 1192 | DMEMIT("%s ", ms->mirror[m].dev->name); | 1204 | DMEMIT("%s ", ms->mirror[m].dev->name); |
| 1193 | 1205 | ||
| 1194 | DMEMIT(SECTOR_FORMAT "/" SECTOR_FORMAT, | 1206 | DMEMIT("%llu/%llu", |
| 1195 | ms->rh.log->type->get_sync_count(ms->rh.log), | 1207 | (unsigned long long)ms->rh.log->type-> |
| 1196 | ms->nr_regions); | 1208 | get_sync_count(ms->rh.log), |
| 1209 | (unsigned long long)ms->nr_regions); | ||
| 1197 | break; | 1210 | break; |
| 1198 | 1211 | ||
| 1199 | case STATUSTYPE_TABLE: | 1212 | case STATUSTYPE_TABLE: |
| 1200 | DMEMIT("%d ", ms->nr_mirrors); | 1213 | DMEMIT("%d ", ms->nr_mirrors); |
| 1201 | for (m = 0; m < ms->nr_mirrors; m++) | 1214 | for (m = 0; m < ms->nr_mirrors; m++) |
| 1202 | DMEMIT("%s " SECTOR_FORMAT " ", | 1215 | DMEMIT("%s %llu ", ms->mirror[m].dev->name, |
| 1203 | ms->mirror[m].dev->name, ms->mirror[m].offset); | 1216 | (unsigned long long)ms->mirror[m].offset); |
| 1204 | } | 1217 | } |
| 1205 | 1218 | ||
| 1206 | return 0; | 1219 | return 0; |
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 7401540086df..08312b46463a 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
| @@ -49,11 +49,26 @@ struct pending_exception { | |||
| 49 | struct bio_list snapshot_bios; | 49 | struct bio_list snapshot_bios; |
| 50 | 50 | ||
| 51 | /* | 51 | /* |
| 52 | * Other pending_exceptions that are processing this | 52 | * Short-term queue of pending exceptions prior to submission. |
| 53 | * chunk. When this list is empty, we know we can | ||
| 54 | * complete the origins. | ||
| 55 | */ | 53 | */ |
| 56 | struct list_head siblings; | 54 | struct list_head list; |
| 55 | |||
| 56 | /* | ||
| 57 | * The primary pending_exception is the one that holds | ||
| 58 | * the sibling_count and the list of origin_bios for a | ||
| 59 | * group of pending_exceptions. It is always last to get freed. | ||
| 60 | * These fields get set up when writing to the origin. | ||
| 61 | */ | ||
| 62 | struct pending_exception *primary_pe; | ||
| 63 | |||
| 64 | /* | ||
| 65 | * Number of pending_exceptions processing this chunk. | ||
| 66 | * When this drops to zero we must complete the origin bios. | ||
| 67 | * If incrementing or decrementing this, hold pe->snap->lock for | ||
| 68 | * the sibling concerned and not pe->primary_pe->snap->lock unless | ||
| 69 | * they are the same. | ||
| 70 | */ | ||
| 71 | atomic_t sibling_count; | ||
| 57 | 72 | ||
| 58 | /* Pointer back to snapshot context */ | 73 | /* Pointer back to snapshot context */ |
| 59 | struct dm_snapshot *snap; | 74 | struct dm_snapshot *snap; |
| @@ -377,6 +392,8 @@ static void read_snapshot_metadata(struct dm_snapshot *s) | |||
| 377 | down_write(&s->lock); | 392 | down_write(&s->lock); |
| 378 | s->valid = 0; | 393 | s->valid = 0; |
| 379 | up_write(&s->lock); | 394 | up_write(&s->lock); |
| 395 | |||
| 396 | dm_table_event(s->table); | ||
| 380 | } | 397 | } |
| 381 | } | 398 | } |
| 382 | 399 | ||
| @@ -542,8 +559,12 @@ static void snapshot_dtr(struct dm_target *ti) | |||
| 542 | { | 559 | { |
| 543 | struct dm_snapshot *s = (struct dm_snapshot *) ti->private; | 560 | struct dm_snapshot *s = (struct dm_snapshot *) ti->private; |
| 544 | 561 | ||
| 562 | /* Prevent further origin writes from using this snapshot. */ | ||
| 563 | /* After this returns there can be no new kcopyd jobs. */ | ||
| 545 | unregister_snapshot(s); | 564 | unregister_snapshot(s); |
| 546 | 565 | ||
| 566 | kcopyd_client_destroy(s->kcopyd_client); | ||
| 567 | |||
| 547 | exit_exception_table(&s->pending, pending_cache); | 568 | exit_exception_table(&s->pending, pending_cache); |
| 548 | exit_exception_table(&s->complete, exception_cache); | 569 | exit_exception_table(&s->complete, exception_cache); |
| 549 | 570 | ||
| @@ -552,7 +573,7 @@ static void snapshot_dtr(struct dm_target *ti) | |||
| 552 | 573 | ||
| 553 | dm_put_device(ti, s->origin); | 574 | dm_put_device(ti, s->origin); |
| 554 | dm_put_device(ti, s->cow); | 575 | dm_put_device(ti, s->cow); |
| 555 | kcopyd_client_destroy(s->kcopyd_client); | 576 | |
| 556 | kfree(s); | 577 | kfree(s); |
| 557 | } | 578 | } |
| 558 | 579 | ||
| @@ -586,78 +607,117 @@ static void error_bios(struct bio *bio) | |||
| 586 | } | 607 | } |
| 587 | } | 608 | } |
| 588 | 609 | ||
| 610 | static inline void error_snapshot_bios(struct pending_exception *pe) | ||
| 611 | { | ||
| 612 | error_bios(bio_list_get(&pe->snapshot_bios)); | ||
| 613 | } | ||
| 614 | |||
| 589 | static struct bio *__flush_bios(struct pending_exception *pe) | 615 | static struct bio *__flush_bios(struct pending_exception *pe) |
| 590 | { | 616 | { |
| 591 | struct pending_exception *sibling; | 617 | /* |
| 618 | * If this pe is involved in a write to the origin and | ||
| 619 | * it is the last sibling to complete then release | ||
| 620 | * the bios for the original write to the origin. | ||
| 621 | */ | ||
| 622 | |||
| 623 | if (pe->primary_pe && | ||
| 624 | atomic_dec_and_test(&pe->primary_pe->sibling_count)) | ||
| 625 | return bio_list_get(&pe->primary_pe->origin_bios); | ||
| 626 | |||
| 627 | return NULL; | ||
| 628 | } | ||
| 629 | |||
| 630 | static void __invalidate_snapshot(struct dm_snapshot *s, | ||
| 631 | struct pending_exception *pe, int err) | ||
| 632 | { | ||
| 633 | if (!s->valid) | ||
| 634 | return; | ||
| 592 | 635 | ||
| 593 | if (list_empty(&pe->siblings)) | 636 | if (err == -EIO) |
| 594 | return bio_list_get(&pe->origin_bios); | 637 | DMERR("Invalidating snapshot: Error reading/writing."); |
| 638 | else if (err == -ENOMEM) | ||
| 639 | DMERR("Invalidating snapshot: Unable to allocate exception."); | ||
| 595 | 640 | ||
| 596 | sibling = list_entry(pe->siblings.next, | 641 | if (pe) |
| 597 | struct pending_exception, siblings); | 642 | remove_exception(&pe->e); |
| 598 | 643 | ||
| 599 | list_del(&pe->siblings); | 644 | if (s->store.drop_snapshot) |
| 645 | s->store.drop_snapshot(&s->store); | ||
| 600 | 646 | ||
| 601 | /* This is fine as long as kcopyd is single-threaded. If kcopyd | 647 | s->valid = 0; |
| 602 | * becomes multi-threaded, we'll need some locking here. | ||
| 603 | */ | ||
| 604 | bio_list_merge(&sibling->origin_bios, &pe->origin_bios); | ||
| 605 | 648 | ||
| 606 | return NULL; | 649 | dm_table_event(s->table); |
| 607 | } | 650 | } |
| 608 | 651 | ||
| 609 | static void pending_complete(struct pending_exception *pe, int success) | 652 | static void pending_complete(struct pending_exception *pe, int success) |
| 610 | { | 653 | { |
| 611 | struct exception *e; | 654 | struct exception *e; |
| 655 | struct pending_exception *primary_pe; | ||
| 612 | struct dm_snapshot *s = pe->snap; | 656 | struct dm_snapshot *s = pe->snap; |
| 613 | struct bio *flush = NULL; | 657 | struct bio *flush = NULL; |
| 614 | 658 | ||
| 615 | if (success) { | 659 | if (!success) { |
| 616 | e = alloc_exception(); | 660 | /* Read/write error - snapshot is unusable */ |
| 617 | if (!e) { | ||
| 618 | DMWARN("Unable to allocate exception."); | ||
| 619 | down_write(&s->lock); | ||
| 620 | s->store.drop_snapshot(&s->store); | ||
| 621 | s->valid = 0; | ||
| 622 | flush = __flush_bios(pe); | ||
| 623 | up_write(&s->lock); | ||
| 624 | |||
| 625 | error_bios(bio_list_get(&pe->snapshot_bios)); | ||
| 626 | goto out; | ||
| 627 | } | ||
| 628 | *e = pe->e; | ||
| 629 | |||
| 630 | /* | ||
| 631 | * Add a proper exception, and remove the | ||
| 632 | * in-flight exception from the list. | ||
| 633 | */ | ||
| 634 | down_write(&s->lock); | 661 | down_write(&s->lock); |
| 635 | insert_exception(&s->complete, e); | 662 | __invalidate_snapshot(s, pe, -EIO); |
| 636 | remove_exception(&pe->e); | ||
| 637 | flush = __flush_bios(pe); | 663 | flush = __flush_bios(pe); |
| 638 | |||
| 639 | /* Submit any pending write bios */ | ||
| 640 | up_write(&s->lock); | 664 | up_write(&s->lock); |
| 641 | 665 | ||
| 642 | flush_bios(bio_list_get(&pe->snapshot_bios)); | 666 | error_snapshot_bios(pe); |
| 643 | } else { | 667 | goto out; |
| 644 | /* Read/write error - snapshot is unusable */ | 668 | } |
| 669 | |||
| 670 | e = alloc_exception(); | ||
| 671 | if (!e) { | ||
| 645 | down_write(&s->lock); | 672 | down_write(&s->lock); |
| 646 | if (s->valid) | 673 | __invalidate_snapshot(s, pe, -ENOMEM); |
| 647 | DMERR("Error reading/writing snapshot"); | ||
| 648 | s->store.drop_snapshot(&s->store); | ||
| 649 | s->valid = 0; | ||
| 650 | remove_exception(&pe->e); | ||
| 651 | flush = __flush_bios(pe); | 674 | flush = __flush_bios(pe); |
| 652 | up_write(&s->lock); | 675 | up_write(&s->lock); |
| 653 | 676 | ||
| 654 | error_bios(bio_list_get(&pe->snapshot_bios)); | 677 | error_snapshot_bios(pe); |
| 678 | goto out; | ||
| 679 | } | ||
| 680 | *e = pe->e; | ||
| 655 | 681 | ||
| 656 | dm_table_event(s->table); | 682 | /* |
| 683 | * Add a proper exception, and remove the | ||
| 684 | * in-flight exception from the list. | ||
| 685 | */ | ||
| 686 | down_write(&s->lock); | ||
| 687 | if (!s->valid) { | ||
| 688 | flush = __flush_bios(pe); | ||
| 689 | up_write(&s->lock); | ||
| 690 | |||
| 691 | free_exception(e); | ||
| 692 | |||
| 693 | error_snapshot_bios(pe); | ||
| 694 | goto out; | ||
| 657 | } | 695 | } |
| 658 | 696 | ||
| 697 | insert_exception(&s->complete, e); | ||
| 698 | remove_exception(&pe->e); | ||
| 699 | flush = __flush_bios(pe); | ||
| 700 | |||
| 701 | up_write(&s->lock); | ||
| 702 | |||
| 703 | /* Submit any pending write bios */ | ||
| 704 | flush_bios(bio_list_get(&pe->snapshot_bios)); | ||
| 705 | |||
| 659 | out: | 706 | out: |
| 660 | free_pending_exception(pe); | 707 | primary_pe = pe->primary_pe; |
| 708 | |||
| 709 | /* | ||
| 710 | * Free the pe if it's not linked to an origin write or if | ||
| 711 | * it's not itself a primary pe. | ||
| 712 | */ | ||
| 713 | if (!primary_pe || primary_pe != pe) | ||
| 714 | free_pending_exception(pe); | ||
| 715 | |||
| 716 | /* | ||
| 717 | * Free the primary pe if nothing references it. | ||
| 718 | */ | ||
| 719 | if (primary_pe && !atomic_read(&primary_pe->sibling_count)) | ||
| 720 | free_pending_exception(primary_pe); | ||
| 661 | 721 | ||
| 662 | if (flush) | 722 | if (flush) |
| 663 | flush_bios(flush); | 723 | flush_bios(flush); |
| @@ -734,38 +794,45 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio) | |||
| 734 | if (e) { | 794 | if (e) { |
| 735 | /* cast the exception to a pending exception */ | 795 | /* cast the exception to a pending exception */ |
| 736 | pe = container_of(e, struct pending_exception, e); | 796 | pe = container_of(e, struct pending_exception, e); |
| 797 | goto out; | ||
| 798 | } | ||
| 737 | 799 | ||
| 738 | } else { | 800 | /* |
| 739 | /* | 801 | * Create a new pending exception, we don't want |
| 740 | * Create a new pending exception, we don't want | 802 | * to hold the lock while we do this. |
| 741 | * to hold the lock while we do this. | 803 | */ |
| 742 | */ | 804 | up_write(&s->lock); |
| 743 | up_write(&s->lock); | 805 | pe = alloc_pending_exception(); |
| 744 | pe = alloc_pending_exception(); | 806 | down_write(&s->lock); |
| 745 | down_write(&s->lock); | ||
| 746 | 807 | ||
| 747 | e = lookup_exception(&s->pending, chunk); | 808 | if (!s->valid) { |
| 748 | if (e) { | 809 | free_pending_exception(pe); |
| 749 | free_pending_exception(pe); | 810 | return NULL; |
| 750 | pe = container_of(e, struct pending_exception, e); | 811 | } |
| 751 | } else { | ||
| 752 | pe->e.old_chunk = chunk; | ||
| 753 | bio_list_init(&pe->origin_bios); | ||
| 754 | bio_list_init(&pe->snapshot_bios); | ||
| 755 | INIT_LIST_HEAD(&pe->siblings); | ||
| 756 | pe->snap = s; | ||
| 757 | pe->started = 0; | ||
| 758 | |||
| 759 | if (s->store.prepare_exception(&s->store, &pe->e)) { | ||
| 760 | free_pending_exception(pe); | ||
| 761 | s->valid = 0; | ||
| 762 | return NULL; | ||
| 763 | } | ||
| 764 | 812 | ||
| 765 | insert_exception(&s->pending, &pe->e); | 813 | e = lookup_exception(&s->pending, chunk); |
| 766 | } | 814 | if (e) { |
| 815 | free_pending_exception(pe); | ||
| 816 | pe = container_of(e, struct pending_exception, e); | ||
| 817 | goto out; | ||
| 818 | } | ||
| 819 | |||
| 820 | pe->e.old_chunk = chunk; | ||
| 821 | bio_list_init(&pe->origin_bios); | ||
| 822 | bio_list_init(&pe->snapshot_bios); | ||
| 823 | pe->primary_pe = NULL; | ||
| 824 | atomic_set(&pe->sibling_count, 1); | ||
| 825 | pe->snap = s; | ||
| 826 | pe->started = 0; | ||
| 827 | |||
| 828 | if (s->store.prepare_exception(&s->store, &pe->e)) { | ||
| 829 | free_pending_exception(pe); | ||
| 830 | return NULL; | ||
| 767 | } | 831 | } |
| 768 | 832 | ||
| 833 | insert_exception(&s->pending, &pe->e); | ||
| 834 | |||
| 835 | out: | ||
| 769 | return pe; | 836 | return pe; |
| 770 | } | 837 | } |
| 771 | 838 | ||
| @@ -782,13 +849,15 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, | |||
| 782 | { | 849 | { |
| 783 | struct exception *e; | 850 | struct exception *e; |
| 784 | struct dm_snapshot *s = (struct dm_snapshot *) ti->private; | 851 | struct dm_snapshot *s = (struct dm_snapshot *) ti->private; |
| 852 | int copy_needed = 0; | ||
| 785 | int r = 1; | 853 | int r = 1; |
| 786 | chunk_t chunk; | 854 | chunk_t chunk; |
| 787 | struct pending_exception *pe; | 855 | struct pending_exception *pe = NULL; |
| 788 | 856 | ||
| 789 | chunk = sector_to_chunk(s, bio->bi_sector); | 857 | chunk = sector_to_chunk(s, bio->bi_sector); |
| 790 | 858 | ||
| 791 | /* Full snapshots are not usable */ | 859 | /* Full snapshots are not usable */ |
| 860 | /* To get here the table must be live so s->active is always set. */ | ||
| 792 | if (!s->valid) | 861 | if (!s->valid) |
| 793 | return -EIO; | 862 | return -EIO; |
| 794 | 863 | ||
| @@ -806,36 +875,41 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, | |||
| 806 | * to copy an exception */ | 875 | * to copy an exception */ |
| 807 | down_write(&s->lock); | 876 | down_write(&s->lock); |
| 808 | 877 | ||
| 878 | if (!s->valid) { | ||
| 879 | r = -EIO; | ||
| 880 | goto out_unlock; | ||
| 881 | } | ||
| 882 | |||
| 809 | /* If the block is already remapped - use that, else remap it */ | 883 | /* If the block is already remapped - use that, else remap it */ |
| 810 | e = lookup_exception(&s->complete, chunk); | 884 | e = lookup_exception(&s->complete, chunk); |
| 811 | if (e) { | 885 | if (e) { |
| 812 | remap_exception(s, e, bio); | 886 | remap_exception(s, e, bio); |
| 813 | up_write(&s->lock); | 887 | goto out_unlock; |
| 814 | 888 | } | |
| 815 | } else { | 889 | |
| 816 | pe = __find_pending_exception(s, bio); | 890 | pe = __find_pending_exception(s, bio); |
| 817 | 891 | if (!pe) { | |
| 818 | if (!pe) { | 892 | __invalidate_snapshot(s, pe, -ENOMEM); |
| 819 | if (s->store.drop_snapshot) | 893 | r = -EIO; |
| 820 | s->store.drop_snapshot(&s->store); | 894 | goto out_unlock; |
| 821 | s->valid = 0; | 895 | } |
| 822 | r = -EIO; | 896 | |
| 823 | up_write(&s->lock); | 897 | remap_exception(s, &pe->e, bio); |
| 824 | } else { | 898 | bio_list_add(&pe->snapshot_bios, bio); |
| 825 | remap_exception(s, &pe->e, bio); | 899 | |
| 826 | bio_list_add(&pe->snapshot_bios, bio); | 900 | if (!pe->started) { |
| 827 | 901 | /* this is protected by snap->lock */ | |
| 828 | if (!pe->started) { | 902 | pe->started = 1; |
| 829 | /* this is protected by snap->lock */ | 903 | copy_needed = 1; |
| 830 | pe->started = 1; | ||
| 831 | up_write(&s->lock); | ||
| 832 | start_copy(pe); | ||
| 833 | } else | ||
| 834 | up_write(&s->lock); | ||
| 835 | r = 0; | ||
| 836 | } | ||
| 837 | } | 904 | } |
| 838 | 905 | ||
| 906 | r = 0; | ||
| 907 | |||
| 908 | out_unlock: | ||
| 909 | up_write(&s->lock); | ||
| 910 | |||
| 911 | if (copy_needed) | ||
| 912 | start_copy(pe); | ||
| 839 | } else { | 913 | } else { |
| 840 | /* | 914 | /* |
| 841 | * FIXME: this read path scares me because we | 915 | * FIXME: this read path scares me because we |
| @@ -847,6 +921,11 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, | |||
| 847 | /* Do reads */ | 921 | /* Do reads */ |
| 848 | down_read(&s->lock); | 922 | down_read(&s->lock); |
| 849 | 923 | ||
| 924 | if (!s->valid) { | ||
| 925 | up_read(&s->lock); | ||
| 926 | return -EIO; | ||
| 927 | } | ||
| 928 | |||
| 850 | /* See if it it has been remapped */ | 929 | /* See if it it has been remapped */ |
| 851 | e = lookup_exception(&s->complete, chunk); | 930 | e = lookup_exception(&s->complete, chunk); |
| 852 | if (e) | 931 | if (e) |
| @@ -884,9 +963,9 @@ static int snapshot_status(struct dm_target *ti, status_type_t type, | |||
| 884 | snap->store.fraction_full(&snap->store, | 963 | snap->store.fraction_full(&snap->store, |
| 885 | &numerator, | 964 | &numerator, |
| 886 | &denominator); | 965 | &denominator); |
| 887 | snprintf(result, maxlen, | 966 | snprintf(result, maxlen, "%llu/%llu", |
| 888 | SECTOR_FORMAT "/" SECTOR_FORMAT, | 967 | (unsigned long long)numerator, |
| 889 | numerator, denominator); | 968 | (unsigned long long)denominator); |
| 890 | } | 969 | } |
| 891 | else | 970 | else |
| 892 | snprintf(result, maxlen, "Unknown"); | 971 | snprintf(result, maxlen, "Unknown"); |
| @@ -899,9 +978,10 @@ static int snapshot_status(struct dm_target *ti, status_type_t type, | |||
| 899 | * to make private copies if the output is to | 978 | * to make private copies if the output is to |
| 900 | * make sense. | 979 | * make sense. |
| 901 | */ | 980 | */ |
| 902 | snprintf(result, maxlen, "%s %s %c " SECTOR_FORMAT, | 981 | snprintf(result, maxlen, "%s %s %c %llu", |
| 903 | snap->origin->name, snap->cow->name, | 982 | snap->origin->name, snap->cow->name, |
| 904 | snap->type, snap->chunk_size); | 983 | snap->type, |
| 984 | (unsigned long long)snap->chunk_size); | ||
| 905 | break; | 985 | break; |
| 906 | } | 986 | } |
| 907 | 987 | ||
| @@ -911,40 +991,27 @@ static int snapshot_status(struct dm_target *ti, status_type_t type, | |||
| 911 | /*----------------------------------------------------------------- | 991 | /*----------------------------------------------------------------- |
| 912 | * Origin methods | 992 | * Origin methods |
| 913 | *---------------------------------------------------------------*/ | 993 | *---------------------------------------------------------------*/ |
| 914 | static void list_merge(struct list_head *l1, struct list_head *l2) | ||
| 915 | { | ||
| 916 | struct list_head *l1_n, *l2_p; | ||
| 917 | |||
| 918 | l1_n = l1->next; | ||
| 919 | l2_p = l2->prev; | ||
| 920 | |||
| 921 | l1->next = l2; | ||
| 922 | l2->prev = l1; | ||
| 923 | |||
| 924 | l2_p->next = l1_n; | ||
| 925 | l1_n->prev = l2_p; | ||
| 926 | } | ||
| 927 | |||
| 928 | static int __origin_write(struct list_head *snapshots, struct bio *bio) | 994 | static int __origin_write(struct list_head *snapshots, struct bio *bio) |
| 929 | { | 995 | { |
| 930 | int r = 1, first = 1; | 996 | int r = 1, first = 0; |
| 931 | struct dm_snapshot *snap; | 997 | struct dm_snapshot *snap; |
| 932 | struct exception *e; | 998 | struct exception *e; |
| 933 | struct pending_exception *pe, *last = NULL; | 999 | struct pending_exception *pe, *next_pe, *primary_pe = NULL; |
| 934 | chunk_t chunk; | 1000 | chunk_t chunk; |
| 1001 | LIST_HEAD(pe_queue); | ||
| 935 | 1002 | ||
| 936 | /* Do all the snapshots on this origin */ | 1003 | /* Do all the snapshots on this origin */ |
| 937 | list_for_each_entry (snap, snapshots, list) { | 1004 | list_for_each_entry (snap, snapshots, list) { |
| 938 | 1005 | ||
| 1006 | down_write(&snap->lock); | ||
| 1007 | |||
| 939 | /* Only deal with valid and active snapshots */ | 1008 | /* Only deal with valid and active snapshots */ |
| 940 | if (!snap->valid || !snap->active) | 1009 | if (!snap->valid || !snap->active) |
| 941 | continue; | 1010 | goto next_snapshot; |
| 942 | 1011 | ||
| 943 | /* Nothing to do if writing beyond end of snapshot */ | 1012 | /* Nothing to do if writing beyond end of snapshot */ |
| 944 | if (bio->bi_sector >= dm_table_get_size(snap->table)) | 1013 | if (bio->bi_sector >= dm_table_get_size(snap->table)) |
| 945 | continue; | 1014 | goto next_snapshot; |
| 946 | |||
| 947 | down_write(&snap->lock); | ||
| 948 | 1015 | ||
| 949 | /* | 1016 | /* |
| 950 | * Remember, different snapshots can have | 1017 | * Remember, different snapshots can have |
| @@ -956,49 +1023,75 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio) | |||
| 956 | * Check exception table to see if block | 1023 | * Check exception table to see if block |
| 957 | * is already remapped in this snapshot | 1024 | * is already remapped in this snapshot |
| 958 | * and trigger an exception if not. | 1025 | * and trigger an exception if not. |
| 1026 | * | ||
| 1027 | * sibling_count is initialised to 1 so pending_complete() | ||
| 1028 | * won't destroy the primary_pe while we're inside this loop. | ||
| 959 | */ | 1029 | */ |
| 960 | e = lookup_exception(&snap->complete, chunk); | 1030 | e = lookup_exception(&snap->complete, chunk); |
| 961 | if (!e) { | 1031 | if (e) |
| 962 | pe = __find_pending_exception(snap, bio); | 1032 | goto next_snapshot; |
| 963 | if (!pe) { | 1033 | |
| 964 | snap->store.drop_snapshot(&snap->store); | 1034 | pe = __find_pending_exception(snap, bio); |
| 965 | snap->valid = 0; | 1035 | if (!pe) { |
| 966 | 1036 | __invalidate_snapshot(snap, pe, ENOMEM); | |
| 967 | } else { | 1037 | goto next_snapshot; |
| 968 | if (last) | 1038 | } |
| 969 | list_merge(&pe->siblings, | 1039 | |
| 970 | &last->siblings); | 1040 | if (!primary_pe) { |
| 971 | 1041 | /* | |
| 972 | last = pe; | 1042 | * Either every pe here has same |
| 973 | r = 0; | 1043 | * primary_pe or none has one yet. |
| 1044 | */ | ||
| 1045 | if (pe->primary_pe) | ||
| 1046 | primary_pe = pe->primary_pe; | ||
| 1047 | else { | ||
| 1048 | primary_pe = pe; | ||
| 1049 | first = 1; | ||
| 974 | } | 1050 | } |
| 1051 | |||
| 1052 | bio_list_add(&primary_pe->origin_bios, bio); | ||
| 1053 | |||
| 1054 | r = 0; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | if (!pe->primary_pe) { | ||
| 1058 | atomic_inc(&primary_pe->sibling_count); | ||
| 1059 | pe->primary_pe = primary_pe; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | if (!pe->started) { | ||
| 1063 | pe->started = 1; | ||
| 1064 | list_add_tail(&pe->list, &pe_queue); | ||
| 975 | } | 1065 | } |
| 976 | 1066 | ||
| 1067 | next_snapshot: | ||
| 977 | up_write(&snap->lock); | 1068 | up_write(&snap->lock); |
| 978 | } | 1069 | } |
| 979 | 1070 | ||
| 1071 | if (!primary_pe) | ||
| 1072 | goto out; | ||
| 1073 | |||
| 980 | /* | 1074 | /* |
| 981 | * Now that we have a complete pe list we can start the copying. | 1075 | * If this is the first time we're processing this chunk and |
| 1076 | * sibling_count is now 1 it means all the pending exceptions | ||
| 1077 | * got completed while we were in the loop above, so it falls to | ||
| 1078 | * us here to remove the primary_pe and submit any origin_bios. | ||
| 982 | */ | 1079 | */ |
| 983 | if (last) { | 1080 | |
| 984 | pe = last; | 1081 | if (first && atomic_dec_and_test(&primary_pe->sibling_count)) { |
| 985 | do { | 1082 | flush_bios(bio_list_get(&primary_pe->origin_bios)); |
| 986 | down_write(&pe->snap->lock); | 1083 | free_pending_exception(primary_pe); |
| 987 | if (first) | 1084 | /* If we got here, pe_queue is necessarily empty. */ |
| 988 | bio_list_add(&pe->origin_bios, bio); | 1085 | goto out; |
| 989 | if (!pe->started) { | ||
| 990 | pe->started = 1; | ||
| 991 | up_write(&pe->snap->lock); | ||
| 992 | start_copy(pe); | ||
| 993 | } else | ||
| 994 | up_write(&pe->snap->lock); | ||
| 995 | first = 0; | ||
| 996 | pe = list_entry(pe->siblings.next, | ||
| 997 | struct pending_exception, siblings); | ||
| 998 | |||
| 999 | } while (pe != last); | ||
| 1000 | } | 1086 | } |
| 1001 | 1087 | ||
| 1088 | /* | ||
| 1089 | * Now that we have a complete pe list we can start the copying. | ||
| 1090 | */ | ||
| 1091 | list_for_each_entry_safe(pe, next_pe, &pe_queue, list) | ||
| 1092 | start_copy(pe); | ||
| 1093 | |||
| 1094 | out: | ||
| 1002 | return r; | 1095 | return r; |
| 1003 | } | 1096 | } |
| 1004 | 1097 | ||
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index 697aacafb02a..08328a8f5a3c 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c | |||
| @@ -49,9 +49,9 @@ static inline struct stripe_c *alloc_context(unsigned int stripes) | |||
| 49 | static int get_stripe(struct dm_target *ti, struct stripe_c *sc, | 49 | static int get_stripe(struct dm_target *ti, struct stripe_c *sc, |
| 50 | unsigned int stripe, char **argv) | 50 | unsigned int stripe, char **argv) |
| 51 | { | 51 | { |
| 52 | sector_t start; | 52 | unsigned long long start; |
| 53 | 53 | ||
| 54 | if (sscanf(argv[1], SECTOR_FORMAT, &start) != 1) | 54 | if (sscanf(argv[1], "%llu", &start) != 1) |
| 55 | return -EINVAL; | 55 | return -EINVAL; |
| 56 | 56 | ||
| 57 | if (dm_get_device(ti, argv[0], start, sc->stripe_width, | 57 | if (dm_get_device(ti, argv[0], start, sc->stripe_width, |
| @@ -103,7 +103,7 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
| 103 | return -EINVAL; | 103 | return -EINVAL; |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | if (((uint32_t)ti->len) & (chunk_size - 1)) { | 106 | if (ti->len & (chunk_size - 1)) { |
| 107 | ti->error = "dm-stripe: Target length not divisible by " | 107 | ti->error = "dm-stripe: Target length not divisible by " |
| 108 | "chunk size"; | 108 | "chunk size"; |
| 109 | return -EINVAL; | 109 | return -EINVAL; |
| @@ -201,10 +201,11 @@ static int stripe_status(struct dm_target *ti, | |||
| 201 | break; | 201 | break; |
| 202 | 202 | ||
| 203 | case STATUSTYPE_TABLE: | 203 | case STATUSTYPE_TABLE: |
| 204 | DMEMIT("%d " SECTOR_FORMAT, sc->stripes, sc->chunk_mask + 1); | 204 | DMEMIT("%d %llu", sc->stripes, |
| 205 | (unsigned long long)sc->chunk_mask + 1); | ||
| 205 | for (i = 0; i < sc->stripes; i++) | 206 | for (i = 0; i < sc->stripes; i++) |
| 206 | DMEMIT(" %s " SECTOR_FORMAT, sc->stripe[i].dev->name, | 207 | DMEMIT(" %s %llu", sc->stripe[i].dev->name, |
| 207 | sc->stripe[i].physical_start); | 208 | (unsigned long long)sc->stripe[i].physical_start); |
| 208 | break; | 209 | break; |
| 209 | } | 210 | } |
| 210 | return 0; | 211 | return 0; |
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 907b08ddb783..8f56a54cf0ce 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/ctype.h> | 14 | #include <linux/ctype.h> |
| 15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
| 16 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
| 17 | #include <linux/mutex.h> | ||
| 17 | #include <asm/atomic.h> | 18 | #include <asm/atomic.h> |
| 18 | 19 | ||
| 19 | #define MAX_DEPTH 16 | 20 | #define MAX_DEPTH 16 |
| @@ -22,6 +23,7 @@ | |||
| 22 | #define CHILDREN_PER_NODE (KEYS_PER_NODE + 1) | 23 | #define CHILDREN_PER_NODE (KEYS_PER_NODE + 1) |
| 23 | 24 | ||
| 24 | struct dm_table { | 25 | struct dm_table { |
| 26 | struct mapped_device *md; | ||
| 25 | atomic_t holders; | 27 | atomic_t holders; |
| 26 | 28 | ||
| 27 | /* btree table */ | 29 | /* btree table */ |
| @@ -97,6 +99,8 @@ static void combine_restrictions_low(struct io_restrictions *lhs, | |||
| 97 | 99 | ||
| 98 | lhs->seg_boundary_mask = | 100 | lhs->seg_boundary_mask = |
| 99 | min_not_zero(lhs->seg_boundary_mask, rhs->seg_boundary_mask); | 101 | min_not_zero(lhs->seg_boundary_mask, rhs->seg_boundary_mask); |
| 102 | |||
| 103 | lhs->no_cluster |= rhs->no_cluster; | ||
| 100 | } | 104 | } |
| 101 | 105 | ||
| 102 | /* | 106 | /* |
| @@ -204,7 +208,8 @@ static int alloc_targets(struct dm_table *t, unsigned int num) | |||
| 204 | return 0; | 208 | return 0; |
| 205 | } | 209 | } |
| 206 | 210 | ||
| 207 | int dm_table_create(struct dm_table **result, int mode, unsigned num_targets) | 211 | int dm_table_create(struct dm_table **result, int mode, |
| 212 | unsigned num_targets, struct mapped_device *md) | ||
| 208 | { | 213 | { |
| 209 | struct dm_table *t = kmalloc(sizeof(*t), GFP_KERNEL); | 214 | struct dm_table *t = kmalloc(sizeof(*t), GFP_KERNEL); |
| 210 | 215 | ||
| @@ -227,6 +232,7 @@ int dm_table_create(struct dm_table **result, int mode, unsigned num_targets) | |||
| 227 | } | 232 | } |
| 228 | 233 | ||
| 229 | t->mode = mode; | 234 | t->mode = mode; |
| 235 | t->md = md; | ||
| 230 | *result = t; | 236 | *result = t; |
| 231 | return 0; | 237 | return 0; |
| 232 | } | 238 | } |
| @@ -345,7 +351,7 @@ static struct dm_dev *find_device(struct list_head *l, dev_t dev) | |||
| 345 | /* | 351 | /* |
| 346 | * Open a device so we can use it as a map destination. | 352 | * Open a device so we can use it as a map destination. |
| 347 | */ | 353 | */ |
| 348 | static int open_dev(struct dm_dev *d, dev_t dev) | 354 | static int open_dev(struct dm_dev *d, dev_t dev, struct mapped_device *md) |
| 349 | { | 355 | { |
| 350 | static char *_claim_ptr = "I belong to device-mapper"; | 356 | static char *_claim_ptr = "I belong to device-mapper"; |
| 351 | struct block_device *bdev; | 357 | struct block_device *bdev; |
| @@ -357,7 +363,7 @@ static int open_dev(struct dm_dev *d, dev_t dev) | |||
| 357 | bdev = open_by_devnum(dev, d->mode); | 363 | bdev = open_by_devnum(dev, d->mode); |
| 358 | if (IS_ERR(bdev)) | 364 | if (IS_ERR(bdev)) |
| 359 | return PTR_ERR(bdev); | 365 | return PTR_ERR(bdev); |
| 360 | r = bd_claim(bdev, _claim_ptr); | 366 | r = bd_claim_by_disk(bdev, _claim_ptr, dm_disk(md)); |
| 361 | if (r) | 367 | if (r) |
| 362 | blkdev_put(bdev); | 368 | blkdev_put(bdev); |
| 363 | else | 369 | else |
| @@ -368,12 +374,12 @@ static int open_dev(struct dm_dev *d, dev_t dev) | |||
| 368 | /* | 374 | /* |
| 369 | * Close a device that we've been using. | 375 | * Close a device that we've been using. |
| 370 | */ | 376 | */ |
| 371 | static void close_dev(struct dm_dev *d) | 377 | static void close_dev(struct dm_dev *d, struct mapped_device *md) |
| 372 | { | 378 | { |
| 373 | if (!d->bdev) | 379 | if (!d->bdev) |
| 374 | return; | 380 | return; |
| 375 | 381 | ||
| 376 | bd_release(d->bdev); | 382 | bd_release_from_disk(d->bdev, dm_disk(md)); |
| 377 | blkdev_put(d->bdev); | 383 | blkdev_put(d->bdev); |
| 378 | d->bdev = NULL; | 384 | d->bdev = NULL; |
| 379 | } | 385 | } |
| @@ -394,7 +400,7 @@ static int check_device_area(struct dm_dev *dd, sector_t start, sector_t len) | |||
| 394 | * careful to leave things as they were if we fail to reopen the | 400 | * careful to leave things as they were if we fail to reopen the |
| 395 | * device. | 401 | * device. |
| 396 | */ | 402 | */ |
| 397 | static int upgrade_mode(struct dm_dev *dd, int new_mode) | 403 | static int upgrade_mode(struct dm_dev *dd, int new_mode, struct mapped_device *md) |
| 398 | { | 404 | { |
| 399 | int r; | 405 | int r; |
| 400 | struct dm_dev dd_copy; | 406 | struct dm_dev dd_copy; |
| @@ -404,9 +410,9 @@ static int upgrade_mode(struct dm_dev *dd, int new_mode) | |||
| 404 | 410 | ||
| 405 | dd->mode |= new_mode; | 411 | dd->mode |= new_mode; |
| 406 | dd->bdev = NULL; | 412 | dd->bdev = NULL; |
| 407 | r = open_dev(dd, dev); | 413 | r = open_dev(dd, dev, md); |
| 408 | if (!r) | 414 | if (!r) |
| 409 | close_dev(&dd_copy); | 415 | close_dev(&dd_copy, md); |
| 410 | else | 416 | else |
| 411 | *dd = dd_copy; | 417 | *dd = dd_copy; |
| 412 | 418 | ||
| @@ -448,7 +454,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, | |||
| 448 | dd->mode = mode; | 454 | dd->mode = mode; |
| 449 | dd->bdev = NULL; | 455 | dd->bdev = NULL; |
| 450 | 456 | ||
| 451 | if ((r = open_dev(dd, dev))) { | 457 | if ((r = open_dev(dd, dev, t->md))) { |
| 452 | kfree(dd); | 458 | kfree(dd); |
| 453 | return r; | 459 | return r; |
| 454 | } | 460 | } |
| @@ -459,7 +465,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, | |||
| 459 | list_add(&dd->list, &t->devices); | 465 | list_add(&dd->list, &t->devices); |
| 460 | 466 | ||
| 461 | } else if (dd->mode != (mode | dd->mode)) { | 467 | } else if (dd->mode != (mode | dd->mode)) { |
| 462 | r = upgrade_mode(dd, mode); | 468 | r = upgrade_mode(dd, mode, t->md); |
| 463 | if (r) | 469 | if (r) |
| 464 | return r; | 470 | return r; |
| 465 | } | 471 | } |
| @@ -523,6 +529,8 @@ int dm_get_device(struct dm_target *ti, const char *path, sector_t start, | |||
| 523 | rs->seg_boundary_mask = | 529 | rs->seg_boundary_mask = |
| 524 | min_not_zero(rs->seg_boundary_mask, | 530 | min_not_zero(rs->seg_boundary_mask, |
| 525 | q->seg_boundary_mask); | 531 | q->seg_boundary_mask); |
| 532 | |||
| 533 | rs->no_cluster |= !test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags); | ||
| 526 | } | 534 | } |
| 527 | 535 | ||
| 528 | return r; | 536 | return r; |
| @@ -534,7 +542,7 @@ int dm_get_device(struct dm_target *ti, const char *path, sector_t start, | |||
| 534 | void dm_put_device(struct dm_target *ti, struct dm_dev *dd) | 542 | void dm_put_device(struct dm_target *ti, struct dm_dev *dd) |
| 535 | { | 543 | { |
| 536 | if (atomic_dec_and_test(&dd->count)) { | 544 | if (atomic_dec_and_test(&dd->count)) { |
| 537 | close_dev(dd); | 545 | close_dev(dd, ti->table->md); |
| 538 | list_del(&dd->list); | 546 | list_del(&dd->list); |
| 539 | kfree(dd); | 547 | kfree(dd); |
| 540 | } | 548 | } |
| @@ -763,14 +771,14 @@ int dm_table_complete(struct dm_table *t) | |||
| 763 | return r; | 771 | return r; |
| 764 | } | 772 | } |
| 765 | 773 | ||
| 766 | static DECLARE_MUTEX(_event_lock); | 774 | static DEFINE_MUTEX(_event_lock); |
| 767 | void dm_table_event_callback(struct dm_table *t, | 775 | void dm_table_event_callback(struct dm_table *t, |
| 768 | void (*fn)(void *), void *context) | 776 | void (*fn)(void *), void *context) |
| 769 | { | 777 | { |
| 770 | down(&_event_lock); | 778 | mutex_lock(&_event_lock); |
| 771 | t->event_fn = fn; | 779 | t->event_fn = fn; |
| 772 | t->event_context = context; | 780 | t->event_context = context; |
| 773 | up(&_event_lock); | 781 | mutex_unlock(&_event_lock); |
| 774 | } | 782 | } |
| 775 | 783 | ||
| 776 | void dm_table_event(struct dm_table *t) | 784 | void dm_table_event(struct dm_table *t) |
| @@ -781,10 +789,10 @@ void dm_table_event(struct dm_table *t) | |||
| 781 | */ | 789 | */ |
| 782 | BUG_ON(in_interrupt()); | 790 | BUG_ON(in_interrupt()); |
| 783 | 791 | ||
| 784 | down(&_event_lock); | 792 | mutex_lock(&_event_lock); |
| 785 | if (t->event_fn) | 793 | if (t->event_fn) |
| 786 | t->event_fn(t->event_context); | 794 | t->event_fn(t->event_context); |
| 787 | up(&_event_lock); | 795 | mutex_unlock(&_event_lock); |
| 788 | } | 796 | } |
| 789 | 797 | ||
| 790 | sector_t dm_table_get_size(struct dm_table *t) | 798 | sector_t dm_table_get_size(struct dm_table *t) |
| @@ -832,6 +840,11 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q) | |||
| 832 | q->hardsect_size = t->limits.hardsect_size; | 840 | q->hardsect_size = t->limits.hardsect_size; |
| 833 | q->max_segment_size = t->limits.max_segment_size; | 841 | q->max_segment_size = t->limits.max_segment_size; |
| 834 | q->seg_boundary_mask = t->limits.seg_boundary_mask; | 842 | q->seg_boundary_mask = t->limits.seg_boundary_mask; |
| 843 | if (t->limits.no_cluster) | ||
| 844 | q->queue_flags &= ~(1 << QUEUE_FLAG_CLUSTER); | ||
| 845 | else | ||
| 846 | q->queue_flags |= (1 << QUEUE_FLAG_CLUSTER); | ||
| 847 | |||
| 835 | } | 848 | } |
| 836 | 849 | ||
| 837 | unsigned int dm_table_get_num_targets(struct dm_table *t) | 850 | unsigned int dm_table_get_num_targets(struct dm_table *t) |
| @@ -943,12 +956,20 @@ int dm_table_flush_all(struct dm_table *t) | |||
| 943 | return ret; | 956 | return ret; |
| 944 | } | 957 | } |
| 945 | 958 | ||
| 959 | struct mapped_device *dm_table_get_md(struct dm_table *t) | ||
| 960 | { | ||
| 961 | dm_get(t->md); | ||
| 962 | |||
| 963 | return t->md; | ||
| 964 | } | ||
| 965 | |||
| 946 | EXPORT_SYMBOL(dm_vcalloc); | 966 | EXPORT_SYMBOL(dm_vcalloc); |
| 947 | EXPORT_SYMBOL(dm_get_device); | 967 | EXPORT_SYMBOL(dm_get_device); |
| 948 | EXPORT_SYMBOL(dm_put_device); | 968 | EXPORT_SYMBOL(dm_put_device); |
| 949 | EXPORT_SYMBOL(dm_table_event); | 969 | EXPORT_SYMBOL(dm_table_event); |
| 950 | EXPORT_SYMBOL(dm_table_get_size); | 970 | EXPORT_SYMBOL(dm_table_get_size); |
| 951 | EXPORT_SYMBOL(dm_table_get_mode); | 971 | EXPORT_SYMBOL(dm_table_get_mode); |
| 972 | EXPORT_SYMBOL(dm_table_get_md); | ||
| 952 | EXPORT_SYMBOL(dm_table_put); | 973 | EXPORT_SYMBOL(dm_table_put); |
| 953 | EXPORT_SYMBOL(dm_table_get); | 974 | EXPORT_SYMBOL(dm_table_get); |
| 954 | EXPORT_SYMBOL(dm_table_unplug_all); | 975 | EXPORT_SYMBOL(dm_table_unplug_all); |
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index a64798ef481e..4d710b7a133b 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
| 12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
| 13 | #include <linux/mutex.h> | ||
| 13 | #include <linux/moduleparam.h> | 14 | #include <linux/moduleparam.h> |
| 14 | #include <linux/blkpg.h> | 15 | #include <linux/blkpg.h> |
| 15 | #include <linux/bio.h> | 16 | #include <linux/bio.h> |
| @@ -17,6 +18,7 @@ | |||
| 17 | #include <linux/mempool.h> | 18 | #include <linux/mempool.h> |
| 18 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
| 19 | #include <linux/idr.h> | 20 | #include <linux/idr.h> |
| 21 | #include <linux/hdreg.h> | ||
| 20 | #include <linux/blktrace_api.h> | 22 | #include <linux/blktrace_api.h> |
| 21 | 23 | ||
| 22 | static const char *_name = DM_NAME; | 24 | static const char *_name = DM_NAME; |
| @@ -69,6 +71,7 @@ struct mapped_device { | |||
| 69 | 71 | ||
| 70 | request_queue_t *queue; | 72 | request_queue_t *queue; |
| 71 | struct gendisk *disk; | 73 | struct gendisk *disk; |
| 74 | char name[16]; | ||
| 72 | 75 | ||
| 73 | void *interface_ptr; | 76 | void *interface_ptr; |
| 74 | 77 | ||
| @@ -101,6 +104,9 @@ struct mapped_device { | |||
| 101 | */ | 104 | */ |
| 102 | struct super_block *frozen_sb; | 105 | struct super_block *frozen_sb; |
| 103 | struct block_device *suspended_bdev; | 106 | struct block_device *suspended_bdev; |
| 107 | |||
| 108 | /* forced geometry settings */ | ||
| 109 | struct hd_geometry geometry; | ||
| 104 | }; | 110 | }; |
| 105 | 111 | ||
| 106 | #define MIN_IOS 256 | 112 | #define MIN_IOS 256 |
| @@ -226,6 +232,13 @@ static int dm_blk_close(struct inode *inode, struct file *file) | |||
| 226 | return 0; | 232 | return 0; |
| 227 | } | 233 | } |
| 228 | 234 | ||
| 235 | static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) | ||
| 236 | { | ||
| 237 | struct mapped_device *md = bdev->bd_disk->private_data; | ||
| 238 | |||
| 239 | return dm_get_geometry(md, geo); | ||
| 240 | } | ||
| 241 | |||
| 229 | static inline struct dm_io *alloc_io(struct mapped_device *md) | 242 | static inline struct dm_io *alloc_io(struct mapped_device *md) |
| 230 | { | 243 | { |
| 231 | return mempool_alloc(md->io_pool, GFP_NOIO); | 244 | return mempool_alloc(md->io_pool, GFP_NOIO); |
| @@ -312,6 +325,33 @@ struct dm_table *dm_get_table(struct mapped_device *md) | |||
| 312 | return t; | 325 | return t; |
| 313 | } | 326 | } |
| 314 | 327 | ||
| 328 | /* | ||
| 329 | * Get the geometry associated with a dm device | ||
| 330 | */ | ||
| 331 | int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo) | ||
| 332 | { | ||
| 333 | *geo = md->geometry; | ||
| 334 | |||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | |||
| 338 | /* | ||
| 339 | * Set the geometry of a device. | ||
| 340 | */ | ||
| 341 | int dm_set_geometry(struct mapped_device *md, struct hd_geometry *geo) | ||
| 342 | { | ||
| 343 | sector_t sz = (sector_t)geo->cylinders * geo->heads * geo->sectors; | ||
| 344 | |||
| 345 | if (geo->start > sz) { | ||
| 346 | DMWARN("Start sector is beyond the geometry limits."); | ||
| 347 | return -EINVAL; | ||
| 348 | } | ||
| 349 | |||
| 350 | md->geometry = *geo; | ||
| 351 | |||
| 352 | return 0; | ||
| 353 | } | ||
| 354 | |||
| 315 | /*----------------------------------------------------------------- | 355 | /*----------------------------------------------------------------- |
| 316 | * CRUD START: | 356 | * CRUD START: |
| 317 | * A more elegant soln is in the works that uses the queue | 357 | * A more elegant soln is in the works that uses the queue |
| @@ -704,14 +744,14 @@ static int dm_any_congested(void *congested_data, int bdi_bits) | |||
| 704 | /*----------------------------------------------------------------- | 744 | /*----------------------------------------------------------------- |
| 705 | * An IDR is used to keep track of allocated minor numbers. | 745 | * An IDR is used to keep track of allocated minor numbers. |
| 706 | *---------------------------------------------------------------*/ | 746 | *---------------------------------------------------------------*/ |
| 707 | static DECLARE_MUTEX(_minor_lock); | 747 | static DEFINE_MUTEX(_minor_lock); |
| 708 | static DEFINE_IDR(_minor_idr); | 748 | static DEFINE_IDR(_minor_idr); |
| 709 | 749 | ||
| 710 | static void free_minor(unsigned int minor) | 750 | static void free_minor(unsigned int minor) |
| 711 | { | 751 | { |
| 712 | down(&_minor_lock); | 752 | mutex_lock(&_minor_lock); |
| 713 | idr_remove(&_minor_idr, minor); | 753 | idr_remove(&_minor_idr, minor); |
| 714 | up(&_minor_lock); | 754 | mutex_unlock(&_minor_lock); |
| 715 | } | 755 | } |
| 716 | 756 | ||
| 717 | /* | 757 | /* |
| @@ -724,7 +764,7 @@ static int specific_minor(struct mapped_device *md, unsigned int minor) | |||
| 724 | if (minor >= (1 << MINORBITS)) | 764 | if (minor >= (1 << MINORBITS)) |
| 725 | return -EINVAL; | 765 | return -EINVAL; |
| 726 | 766 | ||
| 727 | down(&_minor_lock); | 767 | mutex_lock(&_minor_lock); |
| 728 | 768 | ||
| 729 | if (idr_find(&_minor_idr, minor)) { | 769 | if (idr_find(&_minor_idr, minor)) { |
| 730 | r = -EBUSY; | 770 | r = -EBUSY; |
| @@ -749,7 +789,7 @@ static int specific_minor(struct mapped_device *md, unsigned int minor) | |||
| 749 | } | 789 | } |
| 750 | 790 | ||
| 751 | out: | 791 | out: |
| 752 | up(&_minor_lock); | 792 | mutex_unlock(&_minor_lock); |
| 753 | return r; | 793 | return r; |
| 754 | } | 794 | } |
| 755 | 795 | ||
| @@ -758,7 +798,7 @@ static int next_free_minor(struct mapped_device *md, unsigned int *minor) | |||
| 758 | int r; | 798 | int r; |
| 759 | unsigned int m; | 799 | unsigned int m; |
| 760 | 800 | ||
| 761 | down(&_minor_lock); | 801 | mutex_lock(&_minor_lock); |
| 762 | 802 | ||
| 763 | r = idr_pre_get(&_minor_idr, GFP_KERNEL); | 803 | r = idr_pre_get(&_minor_idr, GFP_KERNEL); |
| 764 | if (!r) { | 804 | if (!r) { |
| @@ -780,7 +820,7 @@ static int next_free_minor(struct mapped_device *md, unsigned int *minor) | |||
| 780 | *minor = m; | 820 | *minor = m; |
| 781 | 821 | ||
| 782 | out: | 822 | out: |
| 783 | up(&_minor_lock); | 823 | mutex_unlock(&_minor_lock); |
| 784 | return r; | 824 | return r; |
| 785 | } | 825 | } |
| 786 | 826 | ||
| @@ -842,6 +882,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent) | |||
| 842 | md->disk->private_data = md; | 882 | md->disk->private_data = md; |
| 843 | sprintf(md->disk->disk_name, "dm-%d", minor); | 883 | sprintf(md->disk->disk_name, "dm-%d", minor); |
| 844 | add_disk(md->disk); | 884 | add_disk(md->disk); |
| 885 | format_dev_t(md->name, MKDEV(_major, minor)); | ||
| 845 | 886 | ||
| 846 | atomic_set(&md->pending, 0); | 887 | atomic_set(&md->pending, 0); |
| 847 | init_waitqueue_head(&md->wait); | 888 | init_waitqueue_head(&md->wait); |
| @@ -904,6 +945,13 @@ static int __bind(struct mapped_device *md, struct dm_table *t) | |||
| 904 | sector_t size; | 945 | sector_t size; |
| 905 | 946 | ||
| 906 | size = dm_table_get_size(t); | 947 | size = dm_table_get_size(t); |
| 948 | |||
| 949 | /* | ||
| 950 | * Wipe any geometry if the size of the table changed. | ||
| 951 | */ | ||
| 952 | if (size != get_capacity(md->disk)) | ||
| 953 | memset(&md->geometry, 0, sizeof(md->geometry)); | ||
| 954 | |||
| 907 | __set_size(md, size); | 955 | __set_size(md, size); |
| 908 | if (size == 0) | 956 | if (size == 0) |
| 909 | return 0; | 957 | return 0; |
| @@ -967,13 +1015,13 @@ static struct mapped_device *dm_find_md(dev_t dev) | |||
| 967 | if (MAJOR(dev) != _major || minor >= (1 << MINORBITS)) | 1015 | if (MAJOR(dev) != _major || minor >= (1 << MINORBITS)) |
| 968 | return NULL; | 1016 | return NULL; |
| 969 | 1017 | ||
| 970 | down(&_minor_lock); | 1018 | mutex_lock(&_minor_lock); |
| 971 | 1019 | ||
| 972 | md = idr_find(&_minor_idr, minor); | 1020 | md = idr_find(&_minor_idr, minor); |
| 973 | if (!md || (dm_disk(md)->first_minor != minor)) | 1021 | if (!md || (dm_disk(md)->first_minor != minor)) |
| 974 | md = NULL; | 1022 | md = NULL; |
| 975 | 1023 | ||
| 976 | up(&_minor_lock); | 1024 | mutex_unlock(&_minor_lock); |
| 977 | 1025 | ||
| 978 | return md; | 1026 | return md; |
| 979 | } | 1027 | } |
| @@ -988,15 +1036,9 @@ struct mapped_device *dm_get_md(dev_t dev) | |||
| 988 | return md; | 1036 | return md; |
| 989 | } | 1037 | } |
| 990 | 1038 | ||
| 991 | void *dm_get_mdptr(dev_t dev) | 1039 | void *dm_get_mdptr(struct mapped_device *md) |
| 992 | { | 1040 | { |
| 993 | struct mapped_device *md; | 1041 | return md->interface_ptr; |
| 994 | void *mdptr = NULL; | ||
| 995 | |||
| 996 | md = dm_find_md(dev); | ||
| 997 | if (md) | ||
| 998 | mdptr = md->interface_ptr; | ||
| 999 | return mdptr; | ||
| 1000 | } | 1042 | } |
| 1001 | 1043 | ||
| 1002 | void dm_set_mdptr(struct mapped_device *md, void *ptr) | 1044 | void dm_set_mdptr(struct mapped_device *md, void *ptr) |
| @@ -1011,18 +1053,18 @@ void dm_get(struct mapped_device *md) | |||
| 1011 | 1053 | ||
| 1012 | void dm_put(struct mapped_device *md) | 1054 | void dm_put(struct mapped_device *md) |
| 1013 | { | 1055 | { |
| 1014 | struct dm_table *map = dm_get_table(md); | 1056 | struct dm_table *map; |
| 1015 | 1057 | ||
| 1016 | if (atomic_dec_and_test(&md->holders)) { | 1058 | if (atomic_dec_and_test(&md->holders)) { |
| 1059 | map = dm_get_table(md); | ||
| 1017 | if (!dm_suspended(md)) { | 1060 | if (!dm_suspended(md)) { |
| 1018 | dm_table_presuspend_targets(map); | 1061 | dm_table_presuspend_targets(map); |
| 1019 | dm_table_postsuspend_targets(map); | 1062 | dm_table_postsuspend_targets(map); |
| 1020 | } | 1063 | } |
| 1021 | __unbind(md); | 1064 | __unbind(md); |
| 1065 | dm_table_put(map); | ||
| 1022 | free_dev(md); | 1066 | free_dev(md); |
| 1023 | } | 1067 | } |
| 1024 | |||
| 1025 | dm_table_put(map); | ||
| 1026 | } | 1068 | } |
| 1027 | 1069 | ||
| 1028 | /* | 1070 | /* |
| @@ -1107,6 +1149,7 @@ int dm_suspend(struct mapped_device *md, int do_lockfs) | |||
| 1107 | { | 1149 | { |
| 1108 | struct dm_table *map = NULL; | 1150 | struct dm_table *map = NULL; |
| 1109 | DECLARE_WAITQUEUE(wait, current); | 1151 | DECLARE_WAITQUEUE(wait, current); |
| 1152 | struct bio *def; | ||
| 1110 | int r = -EINVAL; | 1153 | int r = -EINVAL; |
| 1111 | 1154 | ||
| 1112 | down(&md->suspend_lock); | 1155 | down(&md->suspend_lock); |
| @@ -1166,9 +1209,11 @@ int dm_suspend(struct mapped_device *md, int do_lockfs) | |||
| 1166 | /* were we interrupted ? */ | 1209 | /* were we interrupted ? */ |
| 1167 | r = -EINTR; | 1210 | r = -EINTR; |
| 1168 | if (atomic_read(&md->pending)) { | 1211 | if (atomic_read(&md->pending)) { |
| 1212 | clear_bit(DMF_BLOCK_IO, &md->flags); | ||
| 1213 | def = bio_list_get(&md->deferred); | ||
| 1214 | __flush_deferred_io(md, def); | ||
| 1169 | up_write(&md->io_lock); | 1215 | up_write(&md->io_lock); |
| 1170 | unlock_fs(md); | 1216 | unlock_fs(md); |
| 1171 | clear_bit(DMF_BLOCK_IO, &md->flags); | ||
| 1172 | goto out; | 1217 | goto out; |
| 1173 | } | 1218 | } |
| 1174 | up_write(&md->io_lock); | 1219 | up_write(&md->io_lock); |
| @@ -1262,6 +1307,7 @@ int dm_suspended(struct mapped_device *md) | |||
| 1262 | static struct block_device_operations dm_blk_dops = { | 1307 | static struct block_device_operations dm_blk_dops = { |
| 1263 | .open = dm_blk_open, | 1308 | .open = dm_blk_open, |
| 1264 | .release = dm_blk_close, | 1309 | .release = dm_blk_close, |
| 1310 | .getgeo = dm_blk_getgeo, | ||
| 1265 | .owner = THIS_MODULE | 1311 | .owner = THIS_MODULE |
| 1266 | }; | 1312 | }; |
| 1267 | 1313 | ||
diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 4eaf075da217..fd90bc8f9e45 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/device-mapper.h> | 14 | #include <linux/device-mapper.h> |
| 15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
| 16 | #include <linux/blkdev.h> | 16 | #include <linux/blkdev.h> |
| 17 | #include <linux/hdreg.h> | ||
| 17 | 18 | ||
| 18 | #define DM_NAME "device-mapper" | 19 | #define DM_NAME "device-mapper" |
| 19 | #define DMWARN(f, x...) printk(KERN_WARNING DM_NAME ": " f "\n" , ## x) | 20 | #define DMWARN(f, x...) printk(KERN_WARNING DM_NAME ": " f "\n" , ## x) |
| @@ -23,16 +24,6 @@ | |||
| 23 | #define DMEMIT(x...) sz += ((sz >= maxlen) ? \ | 24 | #define DMEMIT(x...) sz += ((sz >= maxlen) ? \ |
| 24 | 0 : scnprintf(result + sz, maxlen - sz, x)) | 25 | 0 : scnprintf(result + sz, maxlen - sz, x)) |
| 25 | 26 | ||
| 26 | /* | ||
| 27 | * FIXME: I think this should be with the definition of sector_t | ||
| 28 | * in types.h. | ||
| 29 | */ | ||
| 30 | #ifdef CONFIG_LBD | ||
| 31 | #define SECTOR_FORMAT "%llu" | ||
| 32 | #else | ||
| 33 | #define SECTOR_FORMAT "%lu" | ||
| 34 | #endif | ||
| 35 | |||
| 36 | #define SECTOR_SHIFT 9 | 27 | #define SECTOR_SHIFT 9 |
| 37 | 28 | ||
| 38 | /* | 29 | /* |
| @@ -57,7 +48,7 @@ struct mapped_device; | |||
| 57 | int dm_create(struct mapped_device **md); | 48 | int dm_create(struct mapped_device **md); |
| 58 | int dm_create_with_minor(unsigned int minor, struct mapped_device **md); | 49 | int dm_create_with_minor(unsigned int minor, struct mapped_device **md); |
| 59 | void dm_set_mdptr(struct mapped_device *md, void *ptr); | 50 | void dm_set_mdptr(struct mapped_device *md, void *ptr); |
| 60 | void *dm_get_mdptr(dev_t dev); | 51 | void *dm_get_mdptr(struct mapped_device *md); |
| 61 | struct mapped_device *dm_get_md(dev_t dev); | 52 | struct mapped_device *dm_get_md(dev_t dev); |
| 62 | 53 | ||
| 63 | /* | 54 | /* |
| @@ -95,11 +86,18 @@ int dm_wait_event(struct mapped_device *md, int event_nr); | |||
| 95 | struct gendisk *dm_disk(struct mapped_device *md); | 86 | struct gendisk *dm_disk(struct mapped_device *md); |
| 96 | int dm_suspended(struct mapped_device *md); | 87 | int dm_suspended(struct mapped_device *md); |
| 97 | 88 | ||
| 89 | /* | ||
| 90 | * Geometry functions. | ||
| 91 | */ | ||
| 92 | int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo); | ||
| 93 | int dm_set_geometry(struct mapped_device *md, struct hd_geometry *geo); | ||
| 94 | |||
| 98 | /*----------------------------------------------------------------- | 95 | /*----------------------------------------------------------------- |
| 99 | * Functions for manipulating a table. Tables are also reference | 96 | * Functions for manipulating a table. Tables are also reference |
| 100 | * counted. | 97 | * counted. |
| 101 | *---------------------------------------------------------------*/ | 98 | *---------------------------------------------------------------*/ |
| 102 | int dm_table_create(struct dm_table **result, int mode, unsigned num_targets); | 99 | int dm_table_create(struct dm_table **result, int mode, |
| 100 | unsigned num_targets, struct mapped_device *md); | ||
| 103 | 101 | ||
| 104 | void dm_table_get(struct dm_table *t); | 102 | void dm_table_get(struct dm_table *t); |
| 105 | void dm_table_put(struct dm_table *t); | 103 | void dm_table_put(struct dm_table *t); |
| @@ -117,6 +115,7 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q); | |||
| 117 | unsigned int dm_table_get_num_targets(struct dm_table *t); | 115 | unsigned int dm_table_get_num_targets(struct dm_table *t); |
| 118 | struct list_head *dm_table_get_devices(struct dm_table *t); | 116 | struct list_head *dm_table_get_devices(struct dm_table *t); |
| 119 | int dm_table_get_mode(struct dm_table *t); | 117 | int dm_table_get_mode(struct dm_table *t); |
| 118 | struct mapped_device *dm_table_get_md(struct dm_table *t); | ||
| 120 | void dm_table_presuspend_targets(struct dm_table *t); | 119 | void dm_table_presuspend_targets(struct dm_table *t); |
| 121 | void dm_table_postsuspend_targets(struct dm_table *t); | 120 | void dm_table_postsuspend_targets(struct dm_table *t); |
| 122 | void dm_table_resume_targets(struct dm_table *t); | 121 | void dm_table_resume_targets(struct dm_table *t); |
diff --git a/drivers/md/kcopyd.c b/drivers/md/kcopyd.c index 9dcb2c8a3853..72480a48d88b 100644 --- a/drivers/md/kcopyd.c +++ b/drivers/md/kcopyd.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
| 23 | #include <linux/vmalloc.h> | 23 | #include <linux/vmalloc.h> |
| 24 | #include <linux/workqueue.h> | 24 | #include <linux/workqueue.h> |
| 25 | #include <linux/mutex.h> | ||
| 25 | 26 | ||
| 26 | #include "kcopyd.h" | 27 | #include "kcopyd.h" |
| 27 | 28 | ||
| @@ -44,6 +45,9 @@ struct kcopyd_client { | |||
| 44 | struct page_list *pages; | 45 | struct page_list *pages; |
| 45 | unsigned int nr_pages; | 46 | unsigned int nr_pages; |
| 46 | unsigned int nr_free_pages; | 47 | unsigned int nr_free_pages; |
| 48 | |||
| 49 | wait_queue_head_t destroyq; | ||
| 50 | atomic_t nr_jobs; | ||
| 47 | }; | 51 | }; |
| 48 | 52 | ||
| 49 | static struct page_list *alloc_pl(void) | 53 | static struct page_list *alloc_pl(void) |
| @@ -292,10 +296,15 @@ static int run_complete_job(struct kcopyd_job *job) | |||
| 292 | int read_err = job->read_err; | 296 | int read_err = job->read_err; |
| 293 | unsigned int write_err = job->write_err; | 297 | unsigned int write_err = job->write_err; |
| 294 | kcopyd_notify_fn fn = job->fn; | 298 | kcopyd_notify_fn fn = job->fn; |
| 299 | struct kcopyd_client *kc = job->kc; | ||
| 295 | 300 | ||
| 296 | kcopyd_put_pages(job->kc, job->pages); | 301 | kcopyd_put_pages(kc, job->pages); |
| 297 | mempool_free(job, _job_pool); | 302 | mempool_free(job, _job_pool); |
| 298 | fn(read_err, write_err, context); | 303 | fn(read_err, write_err, context); |
| 304 | |||
| 305 | if (atomic_dec_and_test(&kc->nr_jobs)) | ||
| 306 | wake_up(&kc->destroyq); | ||
| 307 | |||
| 299 | return 0; | 308 | return 0; |
| 300 | } | 309 | } |
| 301 | 310 | ||
| @@ -430,6 +439,7 @@ static void do_work(void *ignored) | |||
| 430 | */ | 439 | */ |
| 431 | static void dispatch_job(struct kcopyd_job *job) | 440 | static void dispatch_job(struct kcopyd_job *job) |
| 432 | { | 441 | { |
| 442 | atomic_inc(&job->kc->nr_jobs); | ||
| 433 | push(&_pages_jobs, job); | 443 | push(&_pages_jobs, job); |
| 434 | wake(); | 444 | wake(); |
| 435 | } | 445 | } |
| @@ -572,21 +582,21 @@ int kcopyd_cancel(struct kcopyd_job *job, int block) | |||
| 572 | /*----------------------------------------------------------------- | 582 | /*----------------------------------------------------------------- |
| 573 | * Unit setup | 583 | * Unit setup |
| 574 | *---------------------------------------------------------------*/ | 584 | *---------------------------------------------------------------*/ |
| 575 | static DECLARE_MUTEX(_client_lock); | 585 | static DEFINE_MUTEX(_client_lock); |
| 576 | static LIST_HEAD(_clients); | 586 | static LIST_HEAD(_clients); |
| 577 | 587 | ||
| 578 | static void client_add(struct kcopyd_client *kc) | 588 | static void client_add(struct kcopyd_client *kc) |
| 579 | { | 589 | { |
| 580 | down(&_client_lock); | 590 | mutex_lock(&_client_lock); |
| 581 | list_add(&kc->list, &_clients); | 591 | list_add(&kc->list, &_clients); |
| 582 | up(&_client_lock); | 592 | mutex_unlock(&_client_lock); |
| 583 | } | 593 | } |
| 584 | 594 | ||
| 585 | static void client_del(struct kcopyd_client *kc) | 595 | static void client_del(struct kcopyd_client *kc) |
| 586 | { | 596 | { |
| 587 | down(&_client_lock); | 597 | mutex_lock(&_client_lock); |
| 588 | list_del(&kc->list); | 598 | list_del(&kc->list); |
| 589 | up(&_client_lock); | 599 | mutex_unlock(&_client_lock); |
| 590 | } | 600 | } |
| 591 | 601 | ||
| 592 | static DEFINE_MUTEX(kcopyd_init_lock); | 602 | static DEFINE_MUTEX(kcopyd_init_lock); |
| @@ -669,6 +679,9 @@ int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) | |||
| 669 | return r; | 679 | return r; |
| 670 | } | 680 | } |
| 671 | 681 | ||
| 682 | init_waitqueue_head(&kc->destroyq); | ||
| 683 | atomic_set(&kc->nr_jobs, 0); | ||
| 684 | |||
| 672 | client_add(kc); | 685 | client_add(kc); |
| 673 | *result = kc; | 686 | *result = kc; |
| 674 | return 0; | 687 | return 0; |
| @@ -676,6 +689,9 @@ int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) | |||
| 676 | 689 | ||
| 677 | void kcopyd_client_destroy(struct kcopyd_client *kc) | 690 | void kcopyd_client_destroy(struct kcopyd_client *kc) |
| 678 | { | 691 | { |
| 692 | /* Wait for completion of all jobs submitted by this client. */ | ||
| 693 | wait_event(kc->destroyq, !atomic_read(&kc->nr_jobs)); | ||
| 694 | |||
| 679 | dm_io_put(kc->nr_pages); | 695 | dm_io_put(kc->nr_pages); |
| 680 | client_free_pages(kc); | 696 | client_free_pages(kc); |
| 681 | client_del(kc); | 697 | client_del(kc); |
diff --git a/drivers/md/md.c b/drivers/md/md.c index 5ed2228745cb..039e071c1007 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | #include <linux/buffer_head.h> /* for invalidate_bdev */ | 43 | #include <linux/buffer_head.h> /* for invalidate_bdev */ |
| 44 | #include <linux/suspend.h> | 44 | #include <linux/suspend.h> |
| 45 | #include <linux/poll.h> | 45 | #include <linux/poll.h> |
| 46 | #include <linux/mutex.h> | ||
| 46 | 47 | ||
| 47 | #include <linux/init.h> | 48 | #include <linux/init.h> |
| 48 | 49 | ||
| @@ -158,11 +159,12 @@ static int start_readonly; | |||
| 158 | */ | 159 | */ |
| 159 | static DECLARE_WAIT_QUEUE_HEAD(md_event_waiters); | 160 | static DECLARE_WAIT_QUEUE_HEAD(md_event_waiters); |
| 160 | static atomic_t md_event_count; | 161 | static atomic_t md_event_count; |
| 161 | static void md_new_event(mddev_t *mddev) | 162 | void md_new_event(mddev_t *mddev) |
| 162 | { | 163 | { |
| 163 | atomic_inc(&md_event_count); | 164 | atomic_inc(&md_event_count); |
| 164 | wake_up(&md_event_waiters); | 165 | wake_up(&md_event_waiters); |
| 165 | } | 166 | } |
| 167 | EXPORT_SYMBOL_GPL(md_new_event); | ||
| 166 | 168 | ||
| 167 | /* | 169 | /* |
| 168 | * Enables to iterate over all existing md arrays | 170 | * Enables to iterate over all existing md arrays |
| @@ -253,7 +255,7 @@ static mddev_t * mddev_find(dev_t unit) | |||
| 253 | else | 255 | else |
| 254 | new->md_minor = MINOR(unit) >> MdpMinorShift; | 256 | new->md_minor = MINOR(unit) >> MdpMinorShift; |
| 255 | 257 | ||
| 256 | init_MUTEX(&new->reconfig_sem); | 258 | mutex_init(&new->reconfig_mutex); |
| 257 | INIT_LIST_HEAD(&new->disks); | 259 | INIT_LIST_HEAD(&new->disks); |
| 258 | INIT_LIST_HEAD(&new->all_mddevs); | 260 | INIT_LIST_HEAD(&new->all_mddevs); |
| 259 | init_timer(&new->safemode_timer); | 261 | init_timer(&new->safemode_timer); |
| @@ -266,6 +268,7 @@ static mddev_t * mddev_find(dev_t unit) | |||
| 266 | kfree(new); | 268 | kfree(new); |
| 267 | return NULL; | 269 | return NULL; |
| 268 | } | 270 | } |
| 271 | set_bit(QUEUE_FLAG_CLUSTER, &new->queue->queue_flags); | ||
| 269 | 272 | ||
| 270 | blk_queue_make_request(new->queue, md_fail_request); | 273 | blk_queue_make_request(new->queue, md_fail_request); |
| 271 | 274 | ||
| @@ -274,22 +277,22 @@ static mddev_t * mddev_find(dev_t unit) | |||
| 274 | 277 | ||
| 275 | static inline int mddev_lock(mddev_t * mddev) | 278 | static inline int mddev_lock(mddev_t * mddev) |
| 276 | { | 279 | { |
| 277 | return down_interruptible(&mddev->reconfig_sem); | 280 | return mutex_lock_interruptible(&mddev->reconfig_mutex); |
| 278 | } | 281 | } |
| 279 | 282 | ||
| 280 | static inline void mddev_lock_uninterruptible(mddev_t * mddev) | 283 | static inline void mddev_lock_uninterruptible(mddev_t * mddev) |
| 281 | { | 284 | { |
| 282 | down(&mddev->reconfig_sem); | 285 | mutex_lock(&mddev->reconfig_mutex); |
| 283 | } | 286 | } |
| 284 | 287 | ||
| 285 | static inline int mddev_trylock(mddev_t * mddev) | 288 | static inline int mddev_trylock(mddev_t * mddev) |
| 286 | { | 289 | { |
| 287 | return down_trylock(&mddev->reconfig_sem); | 290 | return mutex_trylock(&mddev->reconfig_mutex); |
| 288 | } | 291 | } |
| 289 | 292 | ||
| 290 | static inline void mddev_unlock(mddev_t * mddev) | 293 | static inline void mddev_unlock(mddev_t * mddev) |
| 291 | { | 294 | { |
| 292 | up(&mddev->reconfig_sem); | 295 | mutex_unlock(&mddev->reconfig_mutex); |
| 293 | 296 | ||
| 294 | md_wakeup_thread(mddev->thread); | 297 | md_wakeup_thread(mddev->thread); |
| 295 | } | 298 | } |
| @@ -660,7 +663,8 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version | |||
| 660 | } | 663 | } |
| 661 | 664 | ||
| 662 | if (sb->major_version != 0 || | 665 | if (sb->major_version != 0 || |
| 663 | sb->minor_version != 90) { | 666 | sb->minor_version < 90 || |
| 667 | sb->minor_version > 91) { | ||
| 664 | printk(KERN_WARNING "Bad version number %d.%d on %s\n", | 668 | printk(KERN_WARNING "Bad version number %d.%d on %s\n", |
| 665 | sb->major_version, sb->minor_version, | 669 | sb->major_version, sb->minor_version, |
| 666 | b); | 670 | b); |
| @@ -745,6 +749,20 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 745 | mddev->bitmap_offset = 0; | 749 | mddev->bitmap_offset = 0; |
| 746 | mddev->default_bitmap_offset = MD_SB_BYTES >> 9; | 750 | mddev->default_bitmap_offset = MD_SB_BYTES >> 9; |
| 747 | 751 | ||
| 752 | if (mddev->minor_version >= 91) { | ||
| 753 | mddev->reshape_position = sb->reshape_position; | ||
| 754 | mddev->delta_disks = sb->delta_disks; | ||
| 755 | mddev->new_level = sb->new_level; | ||
| 756 | mddev->new_layout = sb->new_layout; | ||
| 757 | mddev->new_chunk = sb->new_chunk; | ||
| 758 | } else { | ||
| 759 | mddev->reshape_position = MaxSector; | ||
| 760 | mddev->delta_disks = 0; | ||
| 761 | mddev->new_level = mddev->level; | ||
| 762 | mddev->new_layout = mddev->layout; | ||
| 763 | mddev->new_chunk = mddev->chunk_size; | ||
| 764 | } | ||
| 765 | |||
| 748 | if (sb->state & (1<<MD_SB_CLEAN)) | 766 | if (sb->state & (1<<MD_SB_CLEAN)) |
| 749 | mddev->recovery_cp = MaxSector; | 767 | mddev->recovery_cp = MaxSector; |
| 750 | else { | 768 | else { |
| @@ -764,7 +782,8 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 764 | 782 | ||
| 765 | if (sb->state & (1<<MD_SB_BITMAP_PRESENT) && | 783 | if (sb->state & (1<<MD_SB_BITMAP_PRESENT) && |
| 766 | mddev->bitmap_file == NULL) { | 784 | mddev->bitmap_file == NULL) { |
| 767 | if (mddev->level != 1 && mddev->level != 5 && mddev->level != 6 | 785 | if (mddev->level != 1 && mddev->level != 4 |
| 786 | && mddev->level != 5 && mddev->level != 6 | ||
| 768 | && mddev->level != 10) { | 787 | && mddev->level != 10) { |
| 769 | /* FIXME use a better test */ | 788 | /* FIXME use a better test */ |
| 770 | printk(KERN_WARNING "md: bitmaps not supported for this level.\n"); | 789 | printk(KERN_WARNING "md: bitmaps not supported for this level.\n"); |
| @@ -838,7 +857,6 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 838 | 857 | ||
| 839 | sb->md_magic = MD_SB_MAGIC; | 858 | sb->md_magic = MD_SB_MAGIC; |
| 840 | sb->major_version = mddev->major_version; | 859 | sb->major_version = mddev->major_version; |
| 841 | sb->minor_version = mddev->minor_version; | ||
| 842 | sb->patch_version = mddev->patch_version; | 860 | sb->patch_version = mddev->patch_version; |
| 843 | sb->gvalid_words = 0; /* ignored */ | 861 | sb->gvalid_words = 0; /* ignored */ |
| 844 | memcpy(&sb->set_uuid0, mddev->uuid+0, 4); | 862 | memcpy(&sb->set_uuid0, mddev->uuid+0, 4); |
| @@ -857,6 +875,17 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 857 | sb->events_hi = (mddev->events>>32); | 875 | sb->events_hi = (mddev->events>>32); |
| 858 | sb->events_lo = (u32)mddev->events; | 876 | sb->events_lo = (u32)mddev->events; |
| 859 | 877 | ||
| 878 | if (mddev->reshape_position == MaxSector) | ||
| 879 | sb->minor_version = 90; | ||
| 880 | else { | ||
| 881 | sb->minor_version = 91; | ||
| 882 | sb->reshape_position = mddev->reshape_position; | ||
| 883 | sb->new_level = mddev->new_level; | ||
| 884 | sb->delta_disks = mddev->delta_disks; | ||
| 885 | sb->new_layout = mddev->new_layout; | ||
| 886 | sb->new_chunk = mddev->new_chunk; | ||
| 887 | } | ||
| 888 | mddev->minor_version = sb->minor_version; | ||
| 860 | if (mddev->in_sync) | 889 | if (mddev->in_sync) |
| 861 | { | 890 | { |
| 862 | sb->recovery_cp = mddev->recovery_cp; | 891 | sb->recovery_cp = mddev->recovery_cp; |
| @@ -893,10 +922,9 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 893 | d->raid_disk = rdev2->raid_disk; | 922 | d->raid_disk = rdev2->raid_disk; |
| 894 | else | 923 | else |
| 895 | d->raid_disk = rdev2->desc_nr; /* compatibility */ | 924 | d->raid_disk = rdev2->desc_nr; /* compatibility */ |
| 896 | if (test_bit(Faulty, &rdev2->flags)) { | 925 | if (test_bit(Faulty, &rdev2->flags)) |
| 897 | d->state = (1<<MD_DISK_FAULTY); | 926 | d->state = (1<<MD_DISK_FAULTY); |
| 898 | failed++; | 927 | else if (test_bit(In_sync, &rdev2->flags)) { |
| 899 | } else if (test_bit(In_sync, &rdev2->flags)) { | ||
| 900 | d->state = (1<<MD_DISK_ACTIVE); | 928 | d->state = (1<<MD_DISK_ACTIVE); |
| 901 | d->state |= (1<<MD_DISK_SYNC); | 929 | d->state |= (1<<MD_DISK_SYNC); |
| 902 | active++; | 930 | active++; |
| @@ -1102,6 +1130,20 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 1102 | } | 1130 | } |
| 1103 | mddev->bitmap_offset = (__s32)le32_to_cpu(sb->bitmap_offset); | 1131 | mddev->bitmap_offset = (__s32)le32_to_cpu(sb->bitmap_offset); |
| 1104 | } | 1132 | } |
| 1133 | if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) { | ||
| 1134 | mddev->reshape_position = le64_to_cpu(sb->reshape_position); | ||
| 1135 | mddev->delta_disks = le32_to_cpu(sb->delta_disks); | ||
| 1136 | mddev->new_level = le32_to_cpu(sb->new_level); | ||
| 1137 | mddev->new_layout = le32_to_cpu(sb->new_layout); | ||
| 1138 | mddev->new_chunk = le32_to_cpu(sb->new_chunk)<<9; | ||
| 1139 | } else { | ||
| 1140 | mddev->reshape_position = MaxSector; | ||
| 1141 | mddev->delta_disks = 0; | ||
| 1142 | mddev->new_level = mddev->level; | ||
| 1143 | mddev->new_layout = mddev->layout; | ||
| 1144 | mddev->new_chunk = mddev->chunk_size; | ||
| 1145 | } | ||
| 1146 | |||
| 1105 | } else if (mddev->pers == NULL) { | 1147 | } else if (mddev->pers == NULL) { |
| 1106 | /* Insist of good event counter while assembling */ | 1148 | /* Insist of good event counter while assembling */ |
| 1107 | __u64 ev1 = le64_to_cpu(sb->events); | 1149 | __u64 ev1 = le64_to_cpu(sb->events); |
| @@ -1173,6 +1215,14 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 1173 | sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset); | 1215 | sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset); |
| 1174 | sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET); | 1216 | sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET); |
| 1175 | } | 1217 | } |
| 1218 | if (mddev->reshape_position != MaxSector) { | ||
| 1219 | sb->feature_map |= cpu_to_le32(MD_FEATURE_RESHAPE_ACTIVE); | ||
| 1220 | sb->reshape_position = cpu_to_le64(mddev->reshape_position); | ||
| 1221 | sb->new_layout = cpu_to_le32(mddev->new_layout); | ||
| 1222 | sb->delta_disks = cpu_to_le32(mddev->delta_disks); | ||
| 1223 | sb->new_level = cpu_to_le32(mddev->new_level); | ||
| 1224 | sb->new_chunk = cpu_to_le32(mddev->new_chunk>>9); | ||
| 1225 | } | ||
| 1176 | 1226 | ||
| 1177 | max_dev = 0; | 1227 | max_dev = 0; |
| 1178 | ITERATE_RDEV(mddev,rdev2,tmp) | 1228 | ITERATE_RDEV(mddev,rdev2,tmp) |
| @@ -1301,6 +1351,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) | |||
| 1301 | else | 1351 | else |
| 1302 | ko = &rdev->bdev->bd_disk->kobj; | 1352 | ko = &rdev->bdev->bd_disk->kobj; |
| 1303 | sysfs_create_link(&rdev->kobj, ko, "block"); | 1353 | sysfs_create_link(&rdev->kobj, ko, "block"); |
| 1354 | bd_claim_by_disk(rdev->bdev, rdev, mddev->gendisk); | ||
| 1304 | return 0; | 1355 | return 0; |
| 1305 | } | 1356 | } |
| 1306 | 1357 | ||
| @@ -1311,6 +1362,7 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) | |||
| 1311 | MD_BUG(); | 1362 | MD_BUG(); |
| 1312 | return; | 1363 | return; |
| 1313 | } | 1364 | } |
| 1365 | bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk); | ||
| 1314 | list_del_init(&rdev->same_set); | 1366 | list_del_init(&rdev->same_set); |
| 1315 | printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); | 1367 | printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); |
| 1316 | rdev->mddev = NULL; | 1368 | rdev->mddev = NULL; |
| @@ -1493,7 +1545,7 @@ static void sync_sbs(mddev_t * mddev) | |||
| 1493 | } | 1545 | } |
| 1494 | } | 1546 | } |
| 1495 | 1547 | ||
| 1496 | static void md_update_sb(mddev_t * mddev) | 1548 | void md_update_sb(mddev_t * mddev) |
| 1497 | { | 1549 | { |
| 1498 | int err; | 1550 | int err; |
| 1499 | struct list_head *tmp; | 1551 | struct list_head *tmp; |
| @@ -1570,6 +1622,7 @@ repeat: | |||
| 1570 | wake_up(&mddev->sb_wait); | 1622 | wake_up(&mddev->sb_wait); |
| 1571 | 1623 | ||
| 1572 | } | 1624 | } |
| 1625 | EXPORT_SYMBOL_GPL(md_update_sb); | ||
| 1573 | 1626 | ||
| 1574 | /* words written to sysfs files may, or my not, be \n terminated. | 1627 | /* words written to sysfs files may, or my not, be \n terminated. |
| 1575 | * We want to accept with case. For this we use cmd_match. | 1628 | * We want to accept with case. For this we use cmd_match. |
| @@ -2162,7 +2215,9 @@ action_show(mddev_t *mddev, char *page) | |||
| 2162 | char *type = "idle"; | 2215 | char *type = "idle"; |
| 2163 | if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || | 2216 | if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || |
| 2164 | test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) { | 2217 | test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) { |
| 2165 | if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { | 2218 | if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) |
| 2219 | type = "reshape"; | ||
| 2220 | else if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { | ||
| 2166 | if (!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) | 2221 | if (!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) |
| 2167 | type = "resync"; | 2222 | type = "resync"; |
| 2168 | else if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) | 2223 | else if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) |
| @@ -2193,7 +2248,14 @@ action_store(mddev_t *mddev, const char *page, size_t len) | |||
| 2193 | return -EBUSY; | 2248 | return -EBUSY; |
| 2194 | else if (cmd_match(page, "resync") || cmd_match(page, "recover")) | 2249 | else if (cmd_match(page, "resync") || cmd_match(page, "recover")) |
| 2195 | set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); | 2250 | set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); |
| 2196 | else { | 2251 | else if (cmd_match(page, "reshape")) { |
| 2252 | int err; | ||
| 2253 | if (mddev->pers->start_reshape == NULL) | ||
| 2254 | return -EINVAL; | ||
| 2255 | err = mddev->pers->start_reshape(mddev); | ||
| 2256 | if (err) | ||
| 2257 | return err; | ||
| 2258 | } else { | ||
| 2197 | if (cmd_match(page, "check")) | 2259 | if (cmd_match(page, "check")) |
| 2198 | set_bit(MD_RECOVERY_CHECK, &mddev->recovery); | 2260 | set_bit(MD_RECOVERY_CHECK, &mddev->recovery); |
| 2199 | else if (cmd_match(page, "repair")) | 2261 | else if (cmd_match(page, "repair")) |
| @@ -2304,6 +2366,63 @@ sync_completed_show(mddev_t *mddev, char *page) | |||
| 2304 | static struct md_sysfs_entry | 2366 | static struct md_sysfs_entry |
| 2305 | md_sync_completed = __ATTR_RO(sync_completed); | 2367 | md_sync_completed = __ATTR_RO(sync_completed); |
| 2306 | 2368 | ||
| 2369 | static ssize_t | ||
| 2370 | suspend_lo_show(mddev_t *mddev, char *page) | ||
| 2371 | { | ||
| 2372 | return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_lo); | ||
| 2373 | } | ||
| 2374 | |||
| 2375 | static ssize_t | ||
| 2376 | suspend_lo_store(mddev_t *mddev, const char *buf, size_t len) | ||
| 2377 | { | ||
| 2378 | char *e; | ||
| 2379 | unsigned long long new = simple_strtoull(buf, &e, 10); | ||
| 2380 | |||
| 2381 | if (mddev->pers->quiesce == NULL) | ||
| 2382 | return -EINVAL; | ||
| 2383 | if (buf == e || (*e && *e != '\n')) | ||
| 2384 | return -EINVAL; | ||
| 2385 | if (new >= mddev->suspend_hi || | ||
| 2386 | (new > mddev->suspend_lo && new < mddev->suspend_hi)) { | ||
| 2387 | mddev->suspend_lo = new; | ||
| 2388 | mddev->pers->quiesce(mddev, 2); | ||
| 2389 | return len; | ||
| 2390 | } else | ||
| 2391 | return -EINVAL; | ||
| 2392 | } | ||
| 2393 | static struct md_sysfs_entry md_suspend_lo = | ||
| 2394 | __ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store); | ||
| 2395 | |||
| 2396 | |||
| 2397 | static ssize_t | ||
| 2398 | suspend_hi_show(mddev_t *mddev, char *page) | ||
| 2399 | { | ||
| 2400 | return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_hi); | ||
| 2401 | } | ||
| 2402 | |||
| 2403 | static ssize_t | ||
| 2404 | suspend_hi_store(mddev_t *mddev, const char *buf, size_t len) | ||
| 2405 | { | ||
| 2406 | char *e; | ||
| 2407 | unsigned long long new = simple_strtoull(buf, &e, 10); | ||
| 2408 | |||
| 2409 | if (mddev->pers->quiesce == NULL) | ||
| 2410 | return -EINVAL; | ||
| 2411 | if (buf == e || (*e && *e != '\n')) | ||
| 2412 | return -EINVAL; | ||
| 2413 | if ((new <= mddev->suspend_lo && mddev->suspend_lo >= mddev->suspend_hi) || | ||
| 2414 | (new > mddev->suspend_lo && new > mddev->suspend_hi)) { | ||
| 2415 | mddev->suspend_hi = new; | ||
| 2416 | mddev->pers->quiesce(mddev, 1); | ||
| 2417 | mddev->pers->quiesce(mddev, 0); | ||
| 2418 | return len; | ||
| 2419 | } else | ||
| 2420 | return -EINVAL; | ||
| 2421 | } | ||
| 2422 | static struct md_sysfs_entry md_suspend_hi = | ||
| 2423 | __ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store); | ||
| 2424 | |||
| 2425 | |||
| 2307 | static struct attribute *md_default_attrs[] = { | 2426 | static struct attribute *md_default_attrs[] = { |
| 2308 | &md_level.attr, | 2427 | &md_level.attr, |
| 2309 | &md_raid_disks.attr, | 2428 | &md_raid_disks.attr, |
| @@ -2321,6 +2440,8 @@ static struct attribute *md_redundancy_attrs[] = { | |||
| 2321 | &md_sync_max.attr, | 2440 | &md_sync_max.attr, |
| 2322 | &md_sync_speed.attr, | 2441 | &md_sync_speed.attr, |
| 2323 | &md_sync_completed.attr, | 2442 | &md_sync_completed.attr, |
| 2443 | &md_suspend_lo.attr, | ||
| 2444 | &md_suspend_hi.attr, | ||
| 2324 | NULL, | 2445 | NULL, |
| 2325 | }; | 2446 | }; |
| 2326 | static struct attribute_group md_redundancy_group = { | 2447 | static struct attribute_group md_redundancy_group = { |
| @@ -2380,7 +2501,7 @@ int mdp_major = 0; | |||
| 2380 | 2501 | ||
| 2381 | static struct kobject *md_probe(dev_t dev, int *part, void *data) | 2502 | static struct kobject *md_probe(dev_t dev, int *part, void *data) |
| 2382 | { | 2503 | { |
| 2383 | static DECLARE_MUTEX(disks_sem); | 2504 | static DEFINE_MUTEX(disks_mutex); |
| 2384 | mddev_t *mddev = mddev_find(dev); | 2505 | mddev_t *mddev = mddev_find(dev); |
| 2385 | struct gendisk *disk; | 2506 | struct gendisk *disk; |
| 2386 | int partitioned = (MAJOR(dev) != MD_MAJOR); | 2507 | int partitioned = (MAJOR(dev) != MD_MAJOR); |
| @@ -2390,15 +2511,15 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) | |||
| 2390 | if (!mddev) | 2511 | if (!mddev) |
| 2391 | return NULL; | 2512 | return NULL; |
| 2392 | 2513 | ||
| 2393 | down(&disks_sem); | 2514 | mutex_lock(&disks_mutex); |
| 2394 | if (mddev->gendisk) { | 2515 | if (mddev->gendisk) { |
| 2395 | up(&disks_sem); | 2516 | mutex_unlock(&disks_mutex); |
| 2396 | mddev_put(mddev); | 2517 | mddev_put(mddev); |
| 2397 | return NULL; | 2518 | return NULL; |
| 2398 | } | 2519 | } |
| 2399 | disk = alloc_disk(1 << shift); | 2520 | disk = alloc_disk(1 << shift); |
| 2400 | if (!disk) { | 2521 | if (!disk) { |
| 2401 | up(&disks_sem); | 2522 | mutex_unlock(&disks_mutex); |
| 2402 | mddev_put(mddev); | 2523 | mddev_put(mddev); |
| 2403 | return NULL; | 2524 | return NULL; |
| 2404 | } | 2525 | } |
| @@ -2416,7 +2537,7 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) | |||
| 2416 | disk->queue = mddev->queue; | 2537 | disk->queue = mddev->queue; |
| 2417 | add_disk(disk); | 2538 | add_disk(disk); |
| 2418 | mddev->gendisk = disk; | 2539 | mddev->gendisk = disk; |
| 2419 | up(&disks_sem); | 2540 | mutex_unlock(&disks_mutex); |
| 2420 | mddev->kobj.parent = &disk->kobj; | 2541 | mddev->kobj.parent = &disk->kobj; |
| 2421 | mddev->kobj.k_name = NULL; | 2542 | mddev->kobj.k_name = NULL; |
| 2422 | snprintf(mddev->kobj.name, KOBJ_NAME_LEN, "%s", "md"); | 2543 | snprintf(mddev->kobj.name, KOBJ_NAME_LEN, "%s", "md"); |
| @@ -2539,6 +2660,14 @@ static int do_md_run(mddev_t * mddev) | |||
| 2539 | mddev->level = pers->level; | 2660 | mddev->level = pers->level; |
| 2540 | strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel)); | 2661 | strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel)); |
| 2541 | 2662 | ||
| 2663 | if (mddev->reshape_position != MaxSector && | ||
| 2664 | pers->start_reshape == NULL) { | ||
| 2665 | /* This personality cannot handle reshaping... */ | ||
| 2666 | mddev->pers = NULL; | ||
| 2667 | module_put(pers->owner); | ||
| 2668 | return -EINVAL; | ||
| 2669 | } | ||
| 2670 | |||
| 2542 | mddev->recovery = 0; | 2671 | mddev->recovery = 0; |
| 2543 | mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */ | 2672 | mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */ |
| 2544 | mddev->barriers_work = 1; | 2673 | mddev->barriers_work = 1; |
| @@ -2772,7 +2901,6 @@ static void autorun_array(mddev_t *mddev) | |||
| 2772 | */ | 2901 | */ |
| 2773 | static void autorun_devices(int part) | 2902 | static void autorun_devices(int part) |
| 2774 | { | 2903 | { |
| 2775 | struct list_head candidates; | ||
| 2776 | struct list_head *tmp; | 2904 | struct list_head *tmp; |
| 2777 | mdk_rdev_t *rdev0, *rdev; | 2905 | mdk_rdev_t *rdev0, *rdev; |
| 2778 | mddev_t *mddev; | 2906 | mddev_t *mddev; |
| @@ -2781,6 +2909,7 @@ static void autorun_devices(int part) | |||
| 2781 | printk(KERN_INFO "md: autorun ...\n"); | 2909 | printk(KERN_INFO "md: autorun ...\n"); |
| 2782 | while (!list_empty(&pending_raid_disks)) { | 2910 | while (!list_empty(&pending_raid_disks)) { |
| 2783 | dev_t dev; | 2911 | dev_t dev; |
| 2912 | LIST_HEAD(candidates); | ||
| 2784 | rdev0 = list_entry(pending_raid_disks.next, | 2913 | rdev0 = list_entry(pending_raid_disks.next, |
| 2785 | mdk_rdev_t, same_set); | 2914 | mdk_rdev_t, same_set); |
| 2786 | 2915 | ||
| @@ -3427,11 +3556,18 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) | |||
| 3427 | mddev->default_bitmap_offset = MD_SB_BYTES >> 9; | 3556 | mddev->default_bitmap_offset = MD_SB_BYTES >> 9; |
| 3428 | mddev->bitmap_offset = 0; | 3557 | mddev->bitmap_offset = 0; |
| 3429 | 3558 | ||
| 3559 | mddev->reshape_position = MaxSector; | ||
| 3560 | |||
| 3430 | /* | 3561 | /* |
| 3431 | * Generate a 128 bit UUID | 3562 | * Generate a 128 bit UUID |
| 3432 | */ | 3563 | */ |
| 3433 | get_random_bytes(mddev->uuid, 16); | 3564 | get_random_bytes(mddev->uuid, 16); |
| 3434 | 3565 | ||
| 3566 | mddev->new_level = mddev->level; | ||
| 3567 | mddev->new_chunk = mddev->chunk_size; | ||
| 3568 | mddev->new_layout = mddev->layout; | ||
| 3569 | mddev->delta_disks = 0; | ||
| 3570 | |||
| 3435 | return 0; | 3571 | return 0; |
| 3436 | } | 3572 | } |
| 3437 | 3573 | ||
| @@ -3440,6 +3576,7 @@ static int update_size(mddev_t *mddev, unsigned long size) | |||
| 3440 | mdk_rdev_t * rdev; | 3576 | mdk_rdev_t * rdev; |
| 3441 | int rv; | 3577 | int rv; |
| 3442 | struct list_head *tmp; | 3578 | struct list_head *tmp; |
| 3579 | int fit = (size == 0); | ||
| 3443 | 3580 | ||
| 3444 | if (mddev->pers->resize == NULL) | 3581 | if (mddev->pers->resize == NULL) |
| 3445 | return -EINVAL; | 3582 | return -EINVAL; |
| @@ -3457,7 +3594,6 @@ static int update_size(mddev_t *mddev, unsigned long size) | |||
| 3457 | return -EBUSY; | 3594 | return -EBUSY; |
| 3458 | ITERATE_RDEV(mddev,rdev,tmp) { | 3595 | ITERATE_RDEV(mddev,rdev,tmp) { |
| 3459 | sector_t avail; | 3596 | sector_t avail; |
| 3460 | int fit = (size == 0); | ||
| 3461 | if (rdev->sb_offset > rdev->data_offset) | 3597 | if (rdev->sb_offset > rdev->data_offset) |
| 3462 | avail = (rdev->sb_offset*2) - rdev->data_offset; | 3598 | avail = (rdev->sb_offset*2) - rdev->data_offset; |
| 3463 | else | 3599 | else |
| @@ -3487,14 +3623,16 @@ static int update_raid_disks(mddev_t *mddev, int raid_disks) | |||
| 3487 | { | 3623 | { |
| 3488 | int rv; | 3624 | int rv; |
| 3489 | /* change the number of raid disks */ | 3625 | /* change the number of raid disks */ |
| 3490 | if (mddev->pers->reshape == NULL) | 3626 | if (mddev->pers->check_reshape == NULL) |
| 3491 | return -EINVAL; | 3627 | return -EINVAL; |
| 3492 | if (raid_disks <= 0 || | 3628 | if (raid_disks <= 0 || |
| 3493 | raid_disks >= mddev->max_disks) | 3629 | raid_disks >= mddev->max_disks) |
| 3494 | return -EINVAL; | 3630 | return -EINVAL; |
| 3495 | if (mddev->sync_thread) | 3631 | if (mddev->sync_thread || mddev->reshape_position != MaxSector) |
| 3496 | return -EBUSY; | 3632 | return -EBUSY; |
| 3497 | rv = mddev->pers->reshape(mddev, raid_disks); | 3633 | mddev->delta_disks = raid_disks - mddev->raid_disks; |
| 3634 | |||
| 3635 | rv = mddev->pers->check_reshape(mddev); | ||
| 3498 | return rv; | 3636 | return rv; |
| 3499 | } | 3637 | } |
| 3500 | 3638 | ||
| @@ -4041,7 +4179,10 @@ static void status_unused(struct seq_file *seq) | |||
| 4041 | 4179 | ||
| 4042 | static void status_resync(struct seq_file *seq, mddev_t * mddev) | 4180 | static void status_resync(struct seq_file *seq, mddev_t * mddev) |
| 4043 | { | 4181 | { |
| 4044 | unsigned long max_blocks, resync, res, dt, db, rt; | 4182 | sector_t max_blocks, resync, res; |
| 4183 | unsigned long dt, db, rt; | ||
| 4184 | int scale; | ||
| 4185 | unsigned int per_milli; | ||
| 4045 | 4186 | ||
| 4046 | resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2; | 4187 | resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2; |
| 4047 | 4188 | ||
| @@ -4057,9 +4198,22 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev) | |||
| 4057 | MD_BUG(); | 4198 | MD_BUG(); |
| 4058 | return; | 4199 | return; |
| 4059 | } | 4200 | } |
| 4060 | res = (resync/1024)*1000/(max_blocks/1024 + 1); | 4201 | /* Pick 'scale' such that (resync>>scale)*1000 will fit |
| 4202 | * in a sector_t, and (max_blocks>>scale) will fit in a | ||
| 4203 | * u32, as those are the requirements for sector_div. | ||
| 4204 | * Thus 'scale' must be at least 10 | ||
| 4205 | */ | ||
| 4206 | scale = 10; | ||
| 4207 | if (sizeof(sector_t) > sizeof(unsigned long)) { | ||
| 4208 | while ( max_blocks/2 > (1ULL<<(scale+32))) | ||
| 4209 | scale++; | ||
| 4210 | } | ||
| 4211 | res = (resync>>scale)*1000; | ||
| 4212 | sector_div(res, (u32)((max_blocks>>scale)+1)); | ||
| 4213 | |||
| 4214 | per_milli = res; | ||
| 4061 | { | 4215 | { |
| 4062 | int i, x = res/50, y = 20-x; | 4216 | int i, x = per_milli/50, y = 20-x; |
| 4063 | seq_printf(seq, "["); | 4217 | seq_printf(seq, "["); |
| 4064 | for (i = 0; i < x; i++) | 4218 | for (i = 0; i < x; i++) |
| 4065 | seq_printf(seq, "="); | 4219 | seq_printf(seq, "="); |
| @@ -4068,10 +4222,14 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev) | |||
| 4068 | seq_printf(seq, "."); | 4222 | seq_printf(seq, "."); |
| 4069 | seq_printf(seq, "] "); | 4223 | seq_printf(seq, "] "); |
| 4070 | } | 4224 | } |
| 4071 | seq_printf(seq, " %s =%3lu.%lu%% (%lu/%lu)", | 4225 | seq_printf(seq, " %s =%3u.%u%% (%llu/%llu)", |
| 4226 | (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)? | ||
| 4227 | "reshape" : | ||
| 4072 | (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ? | 4228 | (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ? |
| 4073 | "resync" : "recovery"), | 4229 | "resync" : "recovery")), |
| 4074 | res/10, res % 10, resync, max_blocks); | 4230 | per_milli/10, per_milli % 10, |
| 4231 | (unsigned long long) resync, | ||
| 4232 | (unsigned long long) max_blocks); | ||
| 4075 | 4233 | ||
| 4076 | /* | 4234 | /* |
| 4077 | * We do not want to overflow, so the order of operands and | 4235 | * We do not want to overflow, so the order of operands and |
| @@ -4085,7 +4243,7 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev) | |||
| 4085 | dt = ((jiffies - mddev->resync_mark) / HZ); | 4243 | dt = ((jiffies - mddev->resync_mark) / HZ); |
| 4086 | if (!dt) dt++; | 4244 | if (!dt) dt++; |
| 4087 | db = resync - (mddev->resync_mark_cnt/2); | 4245 | db = resync - (mddev->resync_mark_cnt/2); |
| 4088 | rt = (dt * ((max_blocks-resync) / (db/100+1)))/100; | 4246 | rt = (dt * ((unsigned long)(max_blocks-resync) / (db/100+1)))/100; |
| 4089 | 4247 | ||
| 4090 | seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); | 4248 | seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); |
| 4091 | 4249 | ||
| @@ -4442,7 +4600,7 @@ static DECLARE_WAIT_QUEUE_HEAD(resync_wait); | |||
| 4442 | 4600 | ||
| 4443 | #define SYNC_MARKS 10 | 4601 | #define SYNC_MARKS 10 |
| 4444 | #define SYNC_MARK_STEP (3*HZ) | 4602 | #define SYNC_MARK_STEP (3*HZ) |
| 4445 | static void md_do_sync(mddev_t *mddev) | 4603 | void md_do_sync(mddev_t *mddev) |
| 4446 | { | 4604 | { |
| 4447 | mddev_t *mddev2; | 4605 | mddev_t *mddev2; |
| 4448 | unsigned int currspeed = 0, | 4606 | unsigned int currspeed = 0, |
| @@ -4522,7 +4680,9 @@ static void md_do_sync(mddev_t *mddev) | |||
| 4522 | */ | 4680 | */ |
| 4523 | max_sectors = mddev->resync_max_sectors; | 4681 | max_sectors = mddev->resync_max_sectors; |
| 4524 | mddev->resync_mismatches = 0; | 4682 | mddev->resync_mismatches = 0; |
| 4525 | } else | 4683 | } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) |
| 4684 | max_sectors = mddev->size << 1; | ||
| 4685 | else | ||
| 4526 | /* recovery follows the physical size of devices */ | 4686 | /* recovery follows the physical size of devices */ |
| 4527 | max_sectors = mddev->size << 1; | 4687 | max_sectors = mddev->size << 1; |
| 4528 | 4688 | ||
| @@ -4658,6 +4818,8 @@ static void md_do_sync(mddev_t *mddev) | |||
| 4658 | mddev->pers->sync_request(mddev, max_sectors, &skipped, 1); | 4818 | mddev->pers->sync_request(mddev, max_sectors, &skipped, 1); |
| 4659 | 4819 | ||
| 4660 | if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) && | 4820 | if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) && |
| 4821 | test_bit(MD_RECOVERY_SYNC, &mddev->recovery) && | ||
| 4822 | !test_bit(MD_RECOVERY_CHECK, &mddev->recovery) && | ||
| 4661 | mddev->curr_resync > 2 && | 4823 | mddev->curr_resync > 2 && |
| 4662 | mddev->curr_resync >= mddev->recovery_cp) { | 4824 | mddev->curr_resync >= mddev->recovery_cp) { |
| 4663 | if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { | 4825 | if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { |
| @@ -4675,6 +4837,7 @@ static void md_do_sync(mddev_t *mddev) | |||
| 4675 | set_bit(MD_RECOVERY_DONE, &mddev->recovery); | 4837 | set_bit(MD_RECOVERY_DONE, &mddev->recovery); |
| 4676 | md_wakeup_thread(mddev->thread); | 4838 | md_wakeup_thread(mddev->thread); |
| 4677 | } | 4839 | } |
| 4840 | EXPORT_SYMBOL_GPL(md_do_sync); | ||
| 4678 | 4841 | ||
| 4679 | 4842 | ||
| 4680 | /* | 4843 | /* |
| @@ -4730,7 +4893,7 @@ void md_check_recovery(mddev_t *mddev) | |||
| 4730 | )) | 4893 | )) |
| 4731 | return; | 4894 | return; |
| 4732 | 4895 | ||
| 4733 | if (mddev_trylock(mddev)==0) { | 4896 | if (mddev_trylock(mddev)) { |
| 4734 | int spares =0; | 4897 | int spares =0; |
| 4735 | 4898 | ||
| 4736 | spin_lock_irq(&mddev->write_lock); | 4899 | spin_lock_irq(&mddev->write_lock); |
| @@ -4866,7 +5029,7 @@ static int md_notify_reboot(struct notifier_block *this, | |||
| 4866 | printk(KERN_INFO "md: stopping all md devices.\n"); | 5029 | printk(KERN_INFO "md: stopping all md devices.\n"); |
| 4867 | 5030 | ||
| 4868 | ITERATE_MDDEV(mddev,tmp) | 5031 | ITERATE_MDDEV(mddev,tmp) |
| 4869 | if (mddev_trylock(mddev)==0) | 5032 | if (mddev_trylock(mddev)) |
| 4870 | do_md_stop (mddev, 1); | 5033 | do_md_stop (mddev, 1); |
| 4871 | /* | 5034 | /* |
| 4872 | * certain more exotic SCSI devices are known to be | 5035 | * certain more exotic SCSI devices are known to be |
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 5d88329e3c7a..3cb0872a845d 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c | |||
| @@ -1402,6 +1402,9 @@ static void raid1d(mddev_t *mddev) | |||
| 1402 | clear_bit(R1BIO_BarrierRetry, &r1_bio->state); | 1402 | clear_bit(R1BIO_BarrierRetry, &r1_bio->state); |
| 1403 | clear_bit(R1BIO_Barrier, &r1_bio->state); | 1403 | clear_bit(R1BIO_Barrier, &r1_bio->state); |
| 1404 | for (i=0; i < conf->raid_disks; i++) | 1404 | for (i=0; i < conf->raid_disks; i++) |
| 1405 | if (r1_bio->bios[i]) | ||
| 1406 | atomic_inc(&r1_bio->remaining); | ||
| 1407 | for (i=0; i < conf->raid_disks; i++) | ||
| 1405 | if (r1_bio->bios[i]) { | 1408 | if (r1_bio->bios[i]) { |
| 1406 | struct bio_vec *bvec; | 1409 | struct bio_vec *bvec; |
| 1407 | int j; | 1410 | int j; |
| @@ -1789,6 +1792,11 @@ static int run(mddev_t *mddev) | |||
| 1789 | mdname(mddev), mddev->level); | 1792 | mdname(mddev), mddev->level); |
| 1790 | goto out; | 1793 | goto out; |
| 1791 | } | 1794 | } |
| 1795 | if (mddev->reshape_position != MaxSector) { | ||
| 1796 | printk("raid1: %s: reshape_position set but not supported\n", | ||
| 1797 | mdname(mddev)); | ||
| 1798 | goto out; | ||
| 1799 | } | ||
| 1792 | /* | 1800 | /* |
| 1793 | * copy the already verified devices into our private RAID1 | 1801 | * copy the already verified devices into our private RAID1 |
| 1794 | * bookkeeping area. [whatever we allocate in run(), | 1802 | * bookkeeping area. [whatever we allocate in run(), |
| @@ -1971,7 +1979,7 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors) | |||
| 1971 | return 0; | 1979 | return 0; |
| 1972 | } | 1980 | } |
| 1973 | 1981 | ||
| 1974 | static int raid1_reshape(mddev_t *mddev, int raid_disks) | 1982 | static int raid1_reshape(mddev_t *mddev) |
| 1975 | { | 1983 | { |
| 1976 | /* We need to: | 1984 | /* We need to: |
| 1977 | * 1/ resize the r1bio_pool | 1985 | * 1/ resize the r1bio_pool |
| @@ -1988,10 +1996,22 @@ static int raid1_reshape(mddev_t *mddev, int raid_disks) | |||
| 1988 | struct pool_info *newpoolinfo; | 1996 | struct pool_info *newpoolinfo; |
| 1989 | mirror_info_t *newmirrors; | 1997 | mirror_info_t *newmirrors; |
| 1990 | conf_t *conf = mddev_to_conf(mddev); | 1998 | conf_t *conf = mddev_to_conf(mddev); |
| 1991 | int cnt; | 1999 | int cnt, raid_disks; |
| 1992 | 2000 | ||
| 1993 | int d, d2; | 2001 | int d, d2; |
| 1994 | 2002 | ||
| 2003 | /* Cannot change chunk_size, layout, or level */ | ||
| 2004 | if (mddev->chunk_size != mddev->new_chunk || | ||
| 2005 | mddev->layout != mddev->new_layout || | ||
| 2006 | mddev->level != mddev->new_level) { | ||
| 2007 | mddev->new_chunk = mddev->chunk_size; | ||
| 2008 | mddev->new_layout = mddev->layout; | ||
| 2009 | mddev->new_level = mddev->level; | ||
| 2010 | return -EINVAL; | ||
| 2011 | } | ||
| 2012 | |||
| 2013 | raid_disks = mddev->raid_disks + mddev->delta_disks; | ||
| 2014 | |||
| 1995 | if (raid_disks < conf->raid_disks) { | 2015 | if (raid_disks < conf->raid_disks) { |
| 1996 | cnt=0; | 2016 | cnt=0; |
| 1997 | for (d= 0; d < conf->raid_disks; d++) | 2017 | for (d= 0; d < conf->raid_disks; d++) |
| @@ -2038,6 +2058,7 @@ static int raid1_reshape(mddev_t *mddev, int raid_disks) | |||
| 2038 | 2058 | ||
| 2039 | mddev->degraded += (raid_disks - conf->raid_disks); | 2059 | mddev->degraded += (raid_disks - conf->raid_disks); |
| 2040 | conf->raid_disks = mddev->raid_disks = raid_disks; | 2060 | conf->raid_disks = mddev->raid_disks = raid_disks; |
| 2061 | mddev->delta_disks = 0; | ||
| 2041 | 2062 | ||
| 2042 | conf->last_used = 0; /* just make sure it is in-range */ | 2063 | conf->last_used = 0; /* just make sure it is in-range */ |
| 2043 | lower_barrier(conf); | 2064 | lower_barrier(conf); |
| @@ -2079,7 +2100,7 @@ static struct mdk_personality raid1_personality = | |||
| 2079 | .spare_active = raid1_spare_active, | 2100 | .spare_active = raid1_spare_active, |
| 2080 | .sync_request = sync_request, | 2101 | .sync_request = sync_request, |
| 2081 | .resize = raid1_resize, | 2102 | .resize = raid1_resize, |
| 2082 | .reshape = raid1_reshape, | 2103 | .check_reshape = raid1_reshape, |
| 2083 | .quiesce = raid1_quiesce, | 2104 | .quiesce = raid1_quiesce, |
| 2084 | }; | 2105 | }; |
| 2085 | 2106 | ||
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 2dba305daf3c..dae740adaf65 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/raid/raid5.h> | 22 | #include <linux/raid/raid5.h> |
| 23 | #include <linux/highmem.h> | 23 | #include <linux/highmem.h> |
| 24 | #include <linux/bitops.h> | 24 | #include <linux/bitops.h> |
| 25 | #include <linux/kthread.h> | ||
| 25 | #include <asm/atomic.h> | 26 | #include <asm/atomic.h> |
| 26 | 27 | ||
| 27 | #include <linux/raid/bitmap.h> | 28 | #include <linux/raid/bitmap.h> |
| @@ -93,11 +94,11 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) | |||
| 93 | if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) | 94 | if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) |
| 94 | md_wakeup_thread(conf->mddev->thread); | 95 | md_wakeup_thread(conf->mddev->thread); |
| 95 | } | 96 | } |
| 96 | list_add_tail(&sh->lru, &conf->inactive_list); | ||
| 97 | atomic_dec(&conf->active_stripes); | 97 | atomic_dec(&conf->active_stripes); |
| 98 | if (!conf->inactive_blocked || | 98 | if (!test_bit(STRIPE_EXPANDING, &sh->state)) { |
| 99 | atomic_read(&conf->active_stripes) < (conf->max_nr_stripes*3/4)) | 99 | list_add_tail(&sh->lru, &conf->inactive_list); |
| 100 | wake_up(&conf->wait_for_stripe); | 100 | wake_up(&conf->wait_for_stripe); |
| 101 | } | ||
| 101 | } | 102 | } |
| 102 | } | 103 | } |
| 103 | } | 104 | } |
| @@ -178,10 +179,10 @@ static int grow_buffers(struct stripe_head *sh, int num) | |||
| 178 | 179 | ||
| 179 | static void raid5_build_block (struct stripe_head *sh, int i); | 180 | static void raid5_build_block (struct stripe_head *sh, int i); |
| 180 | 181 | ||
| 181 | static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx) | 182 | static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, int disks) |
| 182 | { | 183 | { |
| 183 | raid5_conf_t *conf = sh->raid_conf; | 184 | raid5_conf_t *conf = sh->raid_conf; |
| 184 | int disks = conf->raid_disks, i; | 185 | int i; |
| 185 | 186 | ||
| 186 | if (atomic_read(&sh->count) != 0) | 187 | if (atomic_read(&sh->count) != 0) |
| 187 | BUG(); | 188 | BUG(); |
| @@ -198,7 +199,9 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx) | |||
| 198 | sh->pd_idx = pd_idx; | 199 | sh->pd_idx = pd_idx; |
| 199 | sh->state = 0; | 200 | sh->state = 0; |
| 200 | 201 | ||
| 201 | for (i=disks; i--; ) { | 202 | sh->disks = disks; |
| 203 | |||
| 204 | for (i = sh->disks; i--; ) { | ||
| 202 | struct r5dev *dev = &sh->dev[i]; | 205 | struct r5dev *dev = &sh->dev[i]; |
| 203 | 206 | ||
| 204 | if (dev->toread || dev->towrite || dev->written || | 207 | if (dev->toread || dev->towrite || dev->written || |
| @@ -215,7 +218,7 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx) | |||
| 215 | insert_hash(conf, sh); | 218 | insert_hash(conf, sh); |
| 216 | } | 219 | } |
| 217 | 220 | ||
| 218 | static struct stripe_head *__find_stripe(raid5_conf_t *conf, sector_t sector) | 221 | static struct stripe_head *__find_stripe(raid5_conf_t *conf, sector_t sector, int disks) |
| 219 | { | 222 | { |
| 220 | struct stripe_head *sh; | 223 | struct stripe_head *sh; |
| 221 | struct hlist_node *hn; | 224 | struct hlist_node *hn; |
| @@ -223,7 +226,7 @@ static struct stripe_head *__find_stripe(raid5_conf_t *conf, sector_t sector) | |||
| 223 | CHECK_DEVLOCK(); | 226 | CHECK_DEVLOCK(); |
| 224 | PRINTK("__find_stripe, sector %llu\n", (unsigned long long)sector); | 227 | PRINTK("__find_stripe, sector %llu\n", (unsigned long long)sector); |
| 225 | hlist_for_each_entry(sh, hn, stripe_hash(conf, sector), hash) | 228 | hlist_for_each_entry(sh, hn, stripe_hash(conf, sector), hash) |
| 226 | if (sh->sector == sector) | 229 | if (sh->sector == sector && sh->disks == disks) |
| 227 | return sh; | 230 | return sh; |
| 228 | PRINTK("__stripe %llu not in cache\n", (unsigned long long)sector); | 231 | PRINTK("__stripe %llu not in cache\n", (unsigned long long)sector); |
| 229 | return NULL; | 232 | return NULL; |
| @@ -232,8 +235,8 @@ static struct stripe_head *__find_stripe(raid5_conf_t *conf, sector_t sector) | |||
| 232 | static void unplug_slaves(mddev_t *mddev); | 235 | static void unplug_slaves(mddev_t *mddev); |
| 233 | static void raid5_unplug_device(request_queue_t *q); | 236 | static void raid5_unplug_device(request_queue_t *q); |
| 234 | 237 | ||
| 235 | static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector, | 238 | static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector, int disks, |
| 236 | int pd_idx, int noblock) | 239 | int pd_idx, int noblock) |
| 237 | { | 240 | { |
| 238 | struct stripe_head *sh; | 241 | struct stripe_head *sh; |
| 239 | 242 | ||
| @@ -245,7 +248,7 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector | |||
| 245 | wait_event_lock_irq(conf->wait_for_stripe, | 248 | wait_event_lock_irq(conf->wait_for_stripe, |
| 246 | conf->quiesce == 0, | 249 | conf->quiesce == 0, |
| 247 | conf->device_lock, /* nothing */); | 250 | conf->device_lock, /* nothing */); |
| 248 | sh = __find_stripe(conf, sector); | 251 | sh = __find_stripe(conf, sector, disks); |
| 249 | if (!sh) { | 252 | if (!sh) { |
| 250 | if (!conf->inactive_blocked) | 253 | if (!conf->inactive_blocked) |
| 251 | sh = get_free_stripe(conf); | 254 | sh = get_free_stripe(conf); |
| @@ -259,11 +262,11 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector | |||
| 259 | < (conf->max_nr_stripes *3/4) | 262 | < (conf->max_nr_stripes *3/4) |
| 260 | || !conf->inactive_blocked), | 263 | || !conf->inactive_blocked), |
| 261 | conf->device_lock, | 264 | conf->device_lock, |
| 262 | unplug_slaves(conf->mddev); | 265 | unplug_slaves(conf->mddev) |
| 263 | ); | 266 | ); |
| 264 | conf->inactive_blocked = 0; | 267 | conf->inactive_blocked = 0; |
| 265 | } else | 268 | } else |
| 266 | init_stripe(sh, sector, pd_idx); | 269 | init_stripe(sh, sector, pd_idx, disks); |
| 267 | } else { | 270 | } else { |
| 268 | if (atomic_read(&sh->count)) { | 271 | if (atomic_read(&sh->count)) { |
| 269 | if (!list_empty(&sh->lru)) | 272 | if (!list_empty(&sh->lru)) |
| @@ -271,9 +274,8 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector | |||
| 271 | } else { | 274 | } else { |
| 272 | if (!test_bit(STRIPE_HANDLE, &sh->state)) | 275 | if (!test_bit(STRIPE_HANDLE, &sh->state)) |
| 273 | atomic_inc(&conf->active_stripes); | 276 | atomic_inc(&conf->active_stripes); |
| 274 | if (list_empty(&sh->lru)) | 277 | if (!list_empty(&sh->lru)) |
| 275 | BUG(); | 278 | list_del_init(&sh->lru); |
| 276 | list_del_init(&sh->lru); | ||
| 277 | } | 279 | } |
| 278 | } | 280 | } |
| 279 | } while (sh == NULL); | 281 | } while (sh == NULL); |
| @@ -300,6 +302,7 @@ static int grow_one_stripe(raid5_conf_t *conf) | |||
| 300 | kmem_cache_free(conf->slab_cache, sh); | 302 | kmem_cache_free(conf->slab_cache, sh); |
| 301 | return 0; | 303 | return 0; |
| 302 | } | 304 | } |
| 305 | sh->disks = conf->raid_disks; | ||
| 303 | /* we just created an active stripe so... */ | 306 | /* we just created an active stripe so... */ |
| 304 | atomic_set(&sh->count, 1); | 307 | atomic_set(&sh->count, 1); |
| 305 | atomic_inc(&conf->active_stripes); | 308 | atomic_inc(&conf->active_stripes); |
| @@ -313,14 +316,16 @@ static int grow_stripes(raid5_conf_t *conf, int num) | |||
| 313 | kmem_cache_t *sc; | 316 | kmem_cache_t *sc; |
| 314 | int devs = conf->raid_disks; | 317 | int devs = conf->raid_disks; |
| 315 | 318 | ||
| 316 | sprintf(conf->cache_name, "raid5/%s", mdname(conf->mddev)); | 319 | sprintf(conf->cache_name[0], "raid5/%s", mdname(conf->mddev)); |
| 317 | 320 | sprintf(conf->cache_name[1], "raid5/%s-alt", mdname(conf->mddev)); | |
| 318 | sc = kmem_cache_create(conf->cache_name, | 321 | conf->active_name = 0; |
| 322 | sc = kmem_cache_create(conf->cache_name[conf->active_name], | ||
| 319 | sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev), | 323 | sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev), |
| 320 | 0, 0, NULL, NULL); | 324 | 0, 0, NULL, NULL); |
| 321 | if (!sc) | 325 | if (!sc) |
| 322 | return 1; | 326 | return 1; |
| 323 | conf->slab_cache = sc; | 327 | conf->slab_cache = sc; |
| 328 | conf->pool_size = devs; | ||
| 324 | while (num--) { | 329 | while (num--) { |
| 325 | if (!grow_one_stripe(conf)) | 330 | if (!grow_one_stripe(conf)) |
| 326 | return 1; | 331 | return 1; |
| @@ -328,6 +333,129 @@ static int grow_stripes(raid5_conf_t *conf, int num) | |||
| 328 | return 0; | 333 | return 0; |
| 329 | } | 334 | } |
| 330 | 335 | ||
| 336 | #ifdef CONFIG_MD_RAID5_RESHAPE | ||
| 337 | static int resize_stripes(raid5_conf_t *conf, int newsize) | ||
| 338 | { | ||
| 339 | /* Make all the stripes able to hold 'newsize' devices. | ||
| 340 | * New slots in each stripe get 'page' set to a new page. | ||
| 341 | * | ||
| 342 | * This happens in stages: | ||
| 343 | * 1/ create a new kmem_cache and allocate the required number of | ||
| 344 | * stripe_heads. | ||
| 345 | * 2/ gather all the old stripe_heads and tranfer the pages across | ||
| 346 | * to the new stripe_heads. This will have the side effect of | ||
| 347 | * freezing the array as once all stripe_heads have been collected, | ||
| 348 | * no IO will be possible. Old stripe heads are freed once their | ||
| 349 | * pages have been transferred over, and the old kmem_cache is | ||
| 350 | * freed when all stripes are done. | ||
| 351 | * 3/ reallocate conf->disks to be suitable bigger. If this fails, | ||
| 352 | * we simple return a failre status - no need to clean anything up. | ||
| 353 | * 4/ allocate new pages for the new slots in the new stripe_heads. | ||
| 354 | * If this fails, we don't bother trying the shrink the | ||
| 355 | * stripe_heads down again, we just leave them as they are. | ||
| 356 | * As each stripe_head is processed the new one is released into | ||
| 357 | * active service. | ||
| 358 | * | ||
| 359 | * Once step2 is started, we cannot afford to wait for a write, | ||
| 360 | * so we use GFP_NOIO allocations. | ||
| 361 | */ | ||
| 362 | struct stripe_head *osh, *nsh; | ||
| 363 | LIST_HEAD(newstripes); | ||
| 364 | struct disk_info *ndisks; | ||
| 365 | int err = 0; | ||
| 366 | kmem_cache_t *sc; | ||
| 367 | int i; | ||
| 368 | |||
| 369 | if (newsize <= conf->pool_size) | ||
| 370 | return 0; /* never bother to shrink */ | ||
| 371 | |||
| 372 | /* Step 1 */ | ||
| 373 | sc = kmem_cache_create(conf->cache_name[1-conf->active_name], | ||
| 374 | sizeof(struct stripe_head)+(newsize-1)*sizeof(struct r5dev), | ||
| 375 | 0, 0, NULL, NULL); | ||
| 376 | if (!sc) | ||
| 377 | return -ENOMEM; | ||
| 378 | |||
| 379 | for (i = conf->max_nr_stripes; i; i--) { | ||
| 380 | nsh = kmem_cache_alloc(sc, GFP_KERNEL); | ||
| 381 | if (!nsh) | ||
| 382 | break; | ||
| 383 | |||
| 384 | memset(nsh, 0, sizeof(*nsh) + (newsize-1)*sizeof(struct r5dev)); | ||
| 385 | |||
| 386 | nsh->raid_conf = conf; | ||
| 387 | spin_lock_init(&nsh->lock); | ||
| 388 | |||
| 389 | list_add(&nsh->lru, &newstripes); | ||
| 390 | } | ||
| 391 | if (i) { | ||
| 392 | /* didn't get enough, give up */ | ||
| 393 | while (!list_empty(&newstripes)) { | ||
| 394 | nsh = list_entry(newstripes.next, struct stripe_head, lru); | ||
| 395 | list_del(&nsh->lru); | ||
| 396 | kmem_cache_free(sc, nsh); | ||
| 397 | } | ||
| 398 | kmem_cache_destroy(sc); | ||
| 399 | return -ENOMEM; | ||
| 400 | } | ||
| 401 | /* Step 2 - Must use GFP_NOIO now. | ||
| 402 | * OK, we have enough stripes, start collecting inactive | ||
| 403 | * stripes and copying them over | ||
| 404 | */ | ||
| 405 | list_for_each_entry(nsh, &newstripes, lru) { | ||
| 406 | spin_lock_irq(&conf->device_lock); | ||
| 407 | wait_event_lock_irq(conf->wait_for_stripe, | ||
| 408 | !list_empty(&conf->inactive_list), | ||
| 409 | conf->device_lock, | ||
| 410 | unplug_slaves(conf->mddev) | ||
| 411 | ); | ||
| 412 | osh = get_free_stripe(conf); | ||
| 413 | spin_unlock_irq(&conf->device_lock); | ||
| 414 | atomic_set(&nsh->count, 1); | ||
| 415 | for(i=0; i<conf->pool_size; i++) | ||
| 416 | nsh->dev[i].page = osh->dev[i].page; | ||
| 417 | for( ; i<newsize; i++) | ||
| 418 | nsh->dev[i].page = NULL; | ||
| 419 | kmem_cache_free(conf->slab_cache, osh); | ||
| 420 | } | ||
| 421 | kmem_cache_destroy(conf->slab_cache); | ||
| 422 | |||
| 423 | /* Step 3. | ||
| 424 | * At this point, we are holding all the stripes so the array | ||
| 425 | * is completely stalled, so now is a good time to resize | ||
| 426 | * conf->disks. | ||
| 427 | */ | ||
| 428 | ndisks = kzalloc(newsize * sizeof(struct disk_info), GFP_NOIO); | ||
| 429 | if (ndisks) { | ||
| 430 | for (i=0; i<conf->raid_disks; i++) | ||
| 431 | ndisks[i] = conf->disks[i]; | ||
| 432 | kfree(conf->disks); | ||
| 433 | conf->disks = ndisks; | ||
| 434 | } else | ||
| 435 | err = -ENOMEM; | ||
| 436 | |||
| 437 | /* Step 4, return new stripes to service */ | ||
| 438 | while(!list_empty(&newstripes)) { | ||
| 439 | nsh = list_entry(newstripes.next, struct stripe_head, lru); | ||
| 440 | list_del_init(&nsh->lru); | ||
| 441 | for (i=conf->raid_disks; i < newsize; i++) | ||
| 442 | if (nsh->dev[i].page == NULL) { | ||
| 443 | struct page *p = alloc_page(GFP_NOIO); | ||
| 444 | nsh->dev[i].page = p; | ||
| 445 | if (!p) | ||
| 446 | err = -ENOMEM; | ||
| 447 | } | ||
| 448 | release_stripe(nsh); | ||
| 449 | } | ||
| 450 | /* critical section pass, GFP_NOIO no longer needed */ | ||
| 451 | |||
| 452 | conf->slab_cache = sc; | ||
| 453 | conf->active_name = 1-conf->active_name; | ||
| 454 | conf->pool_size = newsize; | ||
| 455 | return err; | ||
| 456 | } | ||
| 457 | #endif | ||
| 458 | |||
| 331 | static int drop_one_stripe(raid5_conf_t *conf) | 459 | static int drop_one_stripe(raid5_conf_t *conf) |
| 332 | { | 460 | { |
| 333 | struct stripe_head *sh; | 461 | struct stripe_head *sh; |
| @@ -339,7 +467,7 @@ static int drop_one_stripe(raid5_conf_t *conf) | |||
| 339 | return 0; | 467 | return 0; |
| 340 | if (atomic_read(&sh->count)) | 468 | if (atomic_read(&sh->count)) |
| 341 | BUG(); | 469 | BUG(); |
| 342 | shrink_buffers(sh, conf->raid_disks); | 470 | shrink_buffers(sh, conf->pool_size); |
| 343 | kmem_cache_free(conf->slab_cache, sh); | 471 | kmem_cache_free(conf->slab_cache, sh); |
| 344 | atomic_dec(&conf->active_stripes); | 472 | atomic_dec(&conf->active_stripes); |
| 345 | return 1; | 473 | return 1; |
| @@ -360,7 +488,7 @@ static int raid5_end_read_request(struct bio * bi, unsigned int bytes_done, | |||
| 360 | { | 488 | { |
| 361 | struct stripe_head *sh = bi->bi_private; | 489 | struct stripe_head *sh = bi->bi_private; |
| 362 | raid5_conf_t *conf = sh->raid_conf; | 490 | raid5_conf_t *conf = sh->raid_conf; |
| 363 | int disks = conf->raid_disks, i; | 491 | int disks = sh->disks, i; |
| 364 | int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags); | 492 | int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags); |
| 365 | 493 | ||
| 366 | if (bi->bi_size) | 494 | if (bi->bi_size) |
| @@ -458,7 +586,7 @@ static int raid5_end_write_request (struct bio *bi, unsigned int bytes_done, | |||
| 458 | { | 586 | { |
| 459 | struct stripe_head *sh = bi->bi_private; | 587 | struct stripe_head *sh = bi->bi_private; |
| 460 | raid5_conf_t *conf = sh->raid_conf; | 588 | raid5_conf_t *conf = sh->raid_conf; |
| 461 | int disks = conf->raid_disks, i; | 589 | int disks = sh->disks, i; |
| 462 | unsigned long flags; | 590 | unsigned long flags; |
| 463 | int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags); | 591 | int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags); |
| 464 | 592 | ||
| @@ -612,7 +740,7 @@ static sector_t raid5_compute_sector(sector_t r_sector, unsigned int raid_disks, | |||
| 612 | static sector_t compute_blocknr(struct stripe_head *sh, int i) | 740 | static sector_t compute_blocknr(struct stripe_head *sh, int i) |
| 613 | { | 741 | { |
| 614 | raid5_conf_t *conf = sh->raid_conf; | 742 | raid5_conf_t *conf = sh->raid_conf; |
| 615 | int raid_disks = conf->raid_disks, data_disks = raid_disks - 1; | 743 | int raid_disks = sh->disks, data_disks = raid_disks - 1; |
| 616 | sector_t new_sector = sh->sector, check; | 744 | sector_t new_sector = sh->sector, check; |
| 617 | int sectors_per_chunk = conf->chunk_size >> 9; | 745 | int sectors_per_chunk = conf->chunk_size >> 9; |
| 618 | sector_t stripe; | 746 | sector_t stripe; |
| @@ -713,8 +841,7 @@ static void copy_data(int frombio, struct bio *bio, | |||
| 713 | 841 | ||
| 714 | static void compute_block(struct stripe_head *sh, int dd_idx) | 842 | static void compute_block(struct stripe_head *sh, int dd_idx) |
| 715 | { | 843 | { |
| 716 | raid5_conf_t *conf = sh->raid_conf; | 844 | int i, count, disks = sh->disks; |
| 717 | int i, count, disks = conf->raid_disks; | ||
| 718 | void *ptr[MAX_XOR_BLOCKS], *p; | 845 | void *ptr[MAX_XOR_BLOCKS], *p; |
| 719 | 846 | ||
| 720 | PRINTK("compute_block, stripe %llu, idx %d\n", | 847 | PRINTK("compute_block, stripe %llu, idx %d\n", |
| @@ -744,7 +871,7 @@ static void compute_block(struct stripe_head *sh, int dd_idx) | |||
| 744 | static void compute_parity(struct stripe_head *sh, int method) | 871 | static void compute_parity(struct stripe_head *sh, int method) |
| 745 | { | 872 | { |
| 746 | raid5_conf_t *conf = sh->raid_conf; | 873 | raid5_conf_t *conf = sh->raid_conf; |
| 747 | int i, pd_idx = sh->pd_idx, disks = conf->raid_disks, count; | 874 | int i, pd_idx = sh->pd_idx, disks = sh->disks, count; |
| 748 | void *ptr[MAX_XOR_BLOCKS]; | 875 | void *ptr[MAX_XOR_BLOCKS]; |
| 749 | struct bio *chosen; | 876 | struct bio *chosen; |
| 750 | 877 | ||
| @@ -910,6 +1037,20 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in | |||
| 910 | return 0; | 1037 | return 0; |
| 911 | } | 1038 | } |
| 912 | 1039 | ||
| 1040 | static void end_reshape(raid5_conf_t *conf); | ||
| 1041 | |||
| 1042 | static int stripe_to_pdidx(sector_t stripe, raid5_conf_t *conf, int disks) | ||
| 1043 | { | ||
| 1044 | int sectors_per_chunk = conf->chunk_size >> 9; | ||
| 1045 | sector_t x = stripe; | ||
| 1046 | int pd_idx, dd_idx; | ||
| 1047 | int chunk_offset = sector_div(x, sectors_per_chunk); | ||
| 1048 | stripe = x; | ||
| 1049 | raid5_compute_sector(stripe*(disks-1)*sectors_per_chunk | ||
| 1050 | + chunk_offset, disks, disks-1, &dd_idx, &pd_idx, conf); | ||
| 1051 | return pd_idx; | ||
| 1052 | } | ||
| 1053 | |||
| 913 | 1054 | ||
| 914 | /* | 1055 | /* |
| 915 | * handle_stripe - do things to a stripe. | 1056 | * handle_stripe - do things to a stripe. |
| @@ -932,11 +1073,11 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in | |||
| 932 | static void handle_stripe(struct stripe_head *sh) | 1073 | static void handle_stripe(struct stripe_head *sh) |
| 933 | { | 1074 | { |
| 934 | raid5_conf_t *conf = sh->raid_conf; | 1075 | raid5_conf_t *conf = sh->raid_conf; |
| 935 | int disks = conf->raid_disks; | 1076 | int disks = sh->disks; |
| 936 | struct bio *return_bi= NULL; | 1077 | struct bio *return_bi= NULL; |
| 937 | struct bio *bi; | 1078 | struct bio *bi; |
| 938 | int i; | 1079 | int i; |
| 939 | int syncing; | 1080 | int syncing, expanding, expanded; |
| 940 | int locked=0, uptodate=0, to_read=0, to_write=0, failed=0, written=0; | 1081 | int locked=0, uptodate=0, to_read=0, to_write=0, failed=0, written=0; |
| 941 | int non_overwrite = 0; | 1082 | int non_overwrite = 0; |
| 942 | int failed_num=0; | 1083 | int failed_num=0; |
| @@ -951,6 +1092,8 @@ static void handle_stripe(struct stripe_head *sh) | |||
| 951 | clear_bit(STRIPE_DELAYED, &sh->state); | 1092 | clear_bit(STRIPE_DELAYED, &sh->state); |
| 952 | 1093 | ||
| 953 | syncing = test_bit(STRIPE_SYNCING, &sh->state); | 1094 | syncing = test_bit(STRIPE_SYNCING, &sh->state); |
| 1095 | expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state); | ||
| 1096 | expanded = test_bit(STRIPE_EXPAND_READY, &sh->state); | ||
| 954 | /* Now to look around and see what can be done */ | 1097 | /* Now to look around and see what can be done */ |
| 955 | 1098 | ||
| 956 | rcu_read_lock(); | 1099 | rcu_read_lock(); |
| @@ -1143,13 +1286,14 @@ static void handle_stripe(struct stripe_head *sh) | |||
| 1143 | * parity, or to satisfy requests | 1286 | * parity, or to satisfy requests |
| 1144 | * or to load a block that is being partially written. | 1287 | * or to load a block that is being partially written. |
| 1145 | */ | 1288 | */ |
| 1146 | if (to_read || non_overwrite || (syncing && (uptodate < disks))) { | 1289 | if (to_read || non_overwrite || (syncing && (uptodate < disks)) || expanding) { |
| 1147 | for (i=disks; i--;) { | 1290 | for (i=disks; i--;) { |
| 1148 | dev = &sh->dev[i]; | 1291 | dev = &sh->dev[i]; |
| 1149 | if (!test_bit(R5_LOCKED, &dev->flags) && !test_bit(R5_UPTODATE, &dev->flags) && | 1292 | if (!test_bit(R5_LOCKED, &dev->flags) && !test_bit(R5_UPTODATE, &dev->flags) && |
| 1150 | (dev->toread || | 1293 | (dev->toread || |
| 1151 | (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) || | 1294 | (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) || |
| 1152 | syncing || | 1295 | syncing || |
| 1296 | expanding || | ||
| 1153 | (failed && (sh->dev[failed_num].toread || | 1297 | (failed && (sh->dev[failed_num].toread || |
| 1154 | (sh->dev[failed_num].towrite && !test_bit(R5_OVERWRITE, &sh->dev[failed_num].flags)))) | 1298 | (sh->dev[failed_num].towrite && !test_bit(R5_OVERWRITE, &sh->dev[failed_num].flags)))) |
| 1155 | ) | 1299 | ) |
| @@ -1339,13 +1483,77 @@ static void handle_stripe(struct stripe_head *sh) | |||
| 1339 | set_bit(R5_Wantwrite, &dev->flags); | 1483 | set_bit(R5_Wantwrite, &dev->flags); |
| 1340 | set_bit(R5_ReWrite, &dev->flags); | 1484 | set_bit(R5_ReWrite, &dev->flags); |
| 1341 | set_bit(R5_LOCKED, &dev->flags); | 1485 | set_bit(R5_LOCKED, &dev->flags); |
| 1486 | locked++; | ||
| 1342 | } else { | 1487 | } else { |
| 1343 | /* let's read it back */ | 1488 | /* let's read it back */ |
| 1344 | set_bit(R5_Wantread, &dev->flags); | 1489 | set_bit(R5_Wantread, &dev->flags); |
| 1345 | set_bit(R5_LOCKED, &dev->flags); | 1490 | set_bit(R5_LOCKED, &dev->flags); |
| 1491 | locked++; | ||
| 1346 | } | 1492 | } |
| 1347 | } | 1493 | } |
| 1348 | 1494 | ||
| 1495 | if (expanded && test_bit(STRIPE_EXPANDING, &sh->state)) { | ||
| 1496 | /* Need to write out all blocks after computing parity */ | ||
| 1497 | sh->disks = conf->raid_disks; | ||
| 1498 | sh->pd_idx = stripe_to_pdidx(sh->sector, conf, conf->raid_disks); | ||
| 1499 | compute_parity(sh, RECONSTRUCT_WRITE); | ||
| 1500 | for (i= conf->raid_disks; i--;) { | ||
| 1501 | set_bit(R5_LOCKED, &sh->dev[i].flags); | ||
| 1502 | locked++; | ||
| 1503 | set_bit(R5_Wantwrite, &sh->dev[i].flags); | ||
| 1504 | } | ||
| 1505 | clear_bit(STRIPE_EXPANDING, &sh->state); | ||
| 1506 | } else if (expanded) { | ||
| 1507 | clear_bit(STRIPE_EXPAND_READY, &sh->state); | ||
| 1508 | atomic_dec(&conf->reshape_stripes); | ||
| 1509 | wake_up(&conf->wait_for_overlap); | ||
| 1510 | md_done_sync(conf->mddev, STRIPE_SECTORS, 1); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | if (expanding && locked == 0) { | ||
| 1514 | /* We have read all the blocks in this stripe and now we need to | ||
| 1515 | * copy some of them into a target stripe for expand. | ||
| 1516 | */ | ||
| 1517 | clear_bit(STRIPE_EXPAND_SOURCE, &sh->state); | ||
| 1518 | for (i=0; i< sh->disks; i++) | ||
| 1519 | if (i != sh->pd_idx) { | ||
| 1520 | int dd_idx, pd_idx, j; | ||
| 1521 | struct stripe_head *sh2; | ||
| 1522 | |||
| 1523 | sector_t bn = compute_blocknr(sh, i); | ||
| 1524 | sector_t s = raid5_compute_sector(bn, conf->raid_disks, | ||
| 1525 | conf->raid_disks-1, | ||
| 1526 | &dd_idx, &pd_idx, conf); | ||
| 1527 | sh2 = get_active_stripe(conf, s, conf->raid_disks, pd_idx, 1); | ||
| 1528 | if (sh2 == NULL) | ||
| 1529 | /* so far only the early blocks of this stripe | ||
| 1530 | * have been requested. When later blocks | ||
| 1531 | * get requested, we will try again | ||
| 1532 | */ | ||
| 1533 | continue; | ||
| 1534 | if(!test_bit(STRIPE_EXPANDING, &sh2->state) || | ||
| 1535 | test_bit(R5_Expanded, &sh2->dev[dd_idx].flags)) { | ||
| 1536 | /* must have already done this block */ | ||
| 1537 | release_stripe(sh2); | ||
| 1538 | continue; | ||
| 1539 | } | ||
| 1540 | memcpy(page_address(sh2->dev[dd_idx].page), | ||
| 1541 | page_address(sh->dev[i].page), | ||
| 1542 | STRIPE_SIZE); | ||
| 1543 | set_bit(R5_Expanded, &sh2->dev[dd_idx].flags); | ||
| 1544 | set_bit(R5_UPTODATE, &sh2->dev[dd_idx].flags); | ||
| 1545 | for (j=0; j<conf->raid_disks; j++) | ||
| 1546 | if (j != sh2->pd_idx && | ||
| 1547 | !test_bit(R5_Expanded, &sh2->dev[j].flags)) | ||
| 1548 | break; | ||
| 1549 | if (j == conf->raid_disks) { | ||
| 1550 | set_bit(STRIPE_EXPAND_READY, &sh2->state); | ||
| 1551 | set_bit(STRIPE_HANDLE, &sh2->state); | ||
| 1552 | } | ||
| 1553 | release_stripe(sh2); | ||
| 1554 | } | ||
| 1555 | } | ||
| 1556 | |||
| 1349 | spin_unlock(&sh->lock); | 1557 | spin_unlock(&sh->lock); |
| 1350 | 1558 | ||
| 1351 | while ((bi=return_bi)) { | 1559 | while ((bi=return_bi)) { |
| @@ -1384,7 +1592,7 @@ static void handle_stripe(struct stripe_head *sh) | |||
| 1384 | rcu_read_unlock(); | 1592 | rcu_read_unlock(); |
| 1385 | 1593 | ||
| 1386 | if (rdev) { | 1594 | if (rdev) { |
| 1387 | if (syncing) | 1595 | if (syncing || expanding || expanded) |
| 1388 | md_sync_acct(rdev->bdev, STRIPE_SECTORS); | 1596 | md_sync_acct(rdev->bdev, STRIPE_SECTORS); |
| 1389 | 1597 | ||
| 1390 | bi->bi_bdev = rdev->bdev; | 1598 | bi->bi_bdev = rdev->bdev; |
| @@ -1526,17 +1734,16 @@ static inline void raid5_plug_device(raid5_conf_t *conf) | |||
| 1526 | spin_unlock_irq(&conf->device_lock); | 1734 | spin_unlock_irq(&conf->device_lock); |
| 1527 | } | 1735 | } |
| 1528 | 1736 | ||
| 1529 | static int make_request (request_queue_t *q, struct bio * bi) | 1737 | static int make_request(request_queue_t *q, struct bio * bi) |
| 1530 | { | 1738 | { |
| 1531 | mddev_t *mddev = q->queuedata; | 1739 | mddev_t *mddev = q->queuedata; |
| 1532 | raid5_conf_t *conf = mddev_to_conf(mddev); | 1740 | raid5_conf_t *conf = mddev_to_conf(mddev); |
| 1533 | const unsigned int raid_disks = conf->raid_disks; | ||
| 1534 | const unsigned int data_disks = raid_disks - 1; | ||
| 1535 | unsigned int dd_idx, pd_idx; | 1741 | unsigned int dd_idx, pd_idx; |
| 1536 | sector_t new_sector; | 1742 | sector_t new_sector; |
| 1537 | sector_t logical_sector, last_sector; | 1743 | sector_t logical_sector, last_sector; |
| 1538 | struct stripe_head *sh; | 1744 | struct stripe_head *sh; |
| 1539 | const int rw = bio_data_dir(bi); | 1745 | const int rw = bio_data_dir(bi); |
| 1746 | int remaining; | ||
| 1540 | 1747 | ||
| 1541 | if (unlikely(bio_barrier(bi))) { | 1748 | if (unlikely(bio_barrier(bi))) { |
| 1542 | bio_endio(bi, bi->bi_size, -EOPNOTSUPP); | 1749 | bio_endio(bi, bi->bi_size, -EOPNOTSUPP); |
| @@ -1555,20 +1762,77 @@ static int make_request (request_queue_t *q, struct bio * bi) | |||
| 1555 | 1762 | ||
| 1556 | for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) { | 1763 | for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) { |
| 1557 | DEFINE_WAIT(w); | 1764 | DEFINE_WAIT(w); |
| 1558 | 1765 | int disks; | |
| 1559 | new_sector = raid5_compute_sector(logical_sector, | ||
| 1560 | raid_disks, data_disks, &dd_idx, &pd_idx, conf); | ||
| 1561 | 1766 | ||
| 1767 | retry: | ||
| 1768 | prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE); | ||
| 1769 | if (likely(conf->expand_progress == MaxSector)) | ||
| 1770 | disks = conf->raid_disks; | ||
| 1771 | else { | ||
| 1772 | /* spinlock is needed as expand_progress may be | ||
| 1773 | * 64bit on a 32bit platform, and so it might be | ||
| 1774 | * possible to see a half-updated value | ||
| 1775 | * Ofcourse expand_progress could change after | ||
| 1776 | * the lock is dropped, so once we get a reference | ||
| 1777 | * to the stripe that we think it is, we will have | ||
| 1778 | * to check again. | ||
| 1779 | */ | ||
| 1780 | spin_lock_irq(&conf->device_lock); | ||
| 1781 | disks = conf->raid_disks; | ||
| 1782 | if (logical_sector >= conf->expand_progress) | ||
| 1783 | disks = conf->previous_raid_disks; | ||
| 1784 | else { | ||
| 1785 | if (logical_sector >= conf->expand_lo) { | ||
| 1786 | spin_unlock_irq(&conf->device_lock); | ||
| 1787 | schedule(); | ||
| 1788 | goto retry; | ||
| 1789 | } | ||
| 1790 | } | ||
| 1791 | spin_unlock_irq(&conf->device_lock); | ||
| 1792 | } | ||
| 1793 | new_sector = raid5_compute_sector(logical_sector, disks, disks - 1, | ||
| 1794 | &dd_idx, &pd_idx, conf); | ||
| 1562 | PRINTK("raid5: make_request, sector %llu logical %llu\n", | 1795 | PRINTK("raid5: make_request, sector %llu logical %llu\n", |
| 1563 | (unsigned long long)new_sector, | 1796 | (unsigned long long)new_sector, |
| 1564 | (unsigned long long)logical_sector); | 1797 | (unsigned long long)logical_sector); |
| 1565 | 1798 | ||
| 1566 | retry: | 1799 | sh = get_active_stripe(conf, new_sector, disks, pd_idx, (bi->bi_rw&RWA_MASK)); |
| 1567 | prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE); | ||
| 1568 | sh = get_active_stripe(conf, new_sector, pd_idx, (bi->bi_rw&RWA_MASK)); | ||
| 1569 | if (sh) { | 1800 | if (sh) { |
| 1570 | if (!add_stripe_bio(sh, bi, dd_idx, (bi->bi_rw&RW_MASK))) { | 1801 | if (unlikely(conf->expand_progress != MaxSector)) { |
| 1571 | /* Add failed due to overlap. Flush everything | 1802 | /* expansion might have moved on while waiting for a |
| 1803 | * stripe, so we must do the range check again. | ||
| 1804 | * Expansion could still move past after this | ||
| 1805 | * test, but as we are holding a reference to | ||
| 1806 | * 'sh', we know that if that happens, | ||
| 1807 | * STRIPE_EXPANDING will get set and the expansion | ||
| 1808 | * won't proceed until we finish with the stripe. | ||
| 1809 | */ | ||
| 1810 | int must_retry = 0; | ||
| 1811 | spin_lock_irq(&conf->device_lock); | ||
| 1812 | if (logical_sector < conf->expand_progress && | ||
| 1813 | disks == conf->previous_raid_disks) | ||
| 1814 | /* mismatch, need to try again */ | ||
| 1815 | must_retry = 1; | ||
| 1816 | spin_unlock_irq(&conf->device_lock); | ||
| 1817 | if (must_retry) { | ||
| 1818 | release_stripe(sh); | ||
| 1819 | goto retry; | ||
| 1820 | } | ||
| 1821 | } | ||
| 1822 | /* FIXME what if we get a false positive because these | ||
| 1823 | * are being updated. | ||
| 1824 | */ | ||
| 1825 | if (logical_sector >= mddev->suspend_lo && | ||
| 1826 | logical_sector < mddev->suspend_hi) { | ||
| 1827 | release_stripe(sh); | ||
| 1828 | schedule(); | ||
| 1829 | goto retry; | ||
| 1830 | } | ||
| 1831 | |||
| 1832 | if (test_bit(STRIPE_EXPANDING, &sh->state) || | ||
| 1833 | !add_stripe_bio(sh, bi, dd_idx, (bi->bi_rw&RW_MASK))) { | ||
| 1834 | /* Stripe is busy expanding or | ||
| 1835 | * add failed due to overlap. Flush everything | ||
| 1572 | * and wait a while | 1836 | * and wait a while |
| 1573 | */ | 1837 | */ |
| 1574 | raid5_unplug_device(mddev->queue); | 1838 | raid5_unplug_device(mddev->queue); |
| @@ -1580,7 +1844,6 @@ static int make_request (request_queue_t *q, struct bio * bi) | |||
| 1580 | raid5_plug_device(conf); | 1844 | raid5_plug_device(conf); |
| 1581 | handle_stripe(sh); | 1845 | handle_stripe(sh); |
| 1582 | release_stripe(sh); | 1846 | release_stripe(sh); |
| 1583 | |||
| 1584 | } else { | 1847 | } else { |
| 1585 | /* cannot get stripe for read-ahead, just give-up */ | 1848 | /* cannot get stripe for read-ahead, just give-up */ |
| 1586 | clear_bit(BIO_UPTODATE, &bi->bi_flags); | 1849 | clear_bit(BIO_UPTODATE, &bi->bi_flags); |
| @@ -1590,7 +1853,9 @@ static int make_request (request_queue_t *q, struct bio * bi) | |||
| 1590 | 1853 | ||
| 1591 | } | 1854 | } |
| 1592 | spin_lock_irq(&conf->device_lock); | 1855 | spin_lock_irq(&conf->device_lock); |
| 1593 | if (--bi->bi_phys_segments == 0) { | 1856 | remaining = --bi->bi_phys_segments; |
| 1857 | spin_unlock_irq(&conf->device_lock); | ||
| 1858 | if (remaining == 0) { | ||
| 1594 | int bytes = bi->bi_size; | 1859 | int bytes = bi->bi_size; |
| 1595 | 1860 | ||
| 1596 | if ( bio_data_dir(bi) == WRITE ) | 1861 | if ( bio_data_dir(bi) == WRITE ) |
| @@ -1598,7 +1863,6 @@ static int make_request (request_queue_t *q, struct bio * bi) | |||
| 1598 | bi->bi_size = 0; | 1863 | bi->bi_size = 0; |
| 1599 | bi->bi_end_io(bi, bytes, 0); | 1864 | bi->bi_end_io(bi, bytes, 0); |
| 1600 | } | 1865 | } |
| 1601 | spin_unlock_irq(&conf->device_lock); | ||
| 1602 | return 0; | 1866 | return 0; |
| 1603 | } | 1867 | } |
| 1604 | 1868 | ||
| @@ -1607,12 +1871,8 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i | |||
| 1607 | { | 1871 | { |
| 1608 | raid5_conf_t *conf = (raid5_conf_t *) mddev->private; | 1872 | raid5_conf_t *conf = (raid5_conf_t *) mddev->private; |
| 1609 | struct stripe_head *sh; | 1873 | struct stripe_head *sh; |
| 1610 | int sectors_per_chunk = conf->chunk_size >> 9; | 1874 | int pd_idx; |
| 1611 | sector_t x; | 1875 | sector_t first_sector, last_sector; |
| 1612 | unsigned long stripe; | ||
| 1613 | int chunk_offset; | ||
| 1614 | int dd_idx, pd_idx; | ||
| 1615 | sector_t first_sector; | ||
| 1616 | int raid_disks = conf->raid_disks; | 1876 | int raid_disks = conf->raid_disks; |
| 1617 | int data_disks = raid_disks-1; | 1877 | int data_disks = raid_disks-1; |
| 1618 | sector_t max_sector = mddev->size << 1; | 1878 | sector_t max_sector = mddev->size << 1; |
| @@ -1621,6 +1881,10 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i | |||
| 1621 | if (sector_nr >= max_sector) { | 1881 | if (sector_nr >= max_sector) { |
| 1622 | /* just being told to finish up .. nothing much to do */ | 1882 | /* just being told to finish up .. nothing much to do */ |
| 1623 | unplug_slaves(mddev); | 1883 | unplug_slaves(mddev); |
| 1884 | if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) { | ||
| 1885 | end_reshape(conf); | ||
| 1886 | return 0; | ||
| 1887 | } | ||
| 1624 | 1888 | ||
| 1625 | if (mddev->curr_resync < max_sector) /* aborted */ | 1889 | if (mddev->curr_resync < max_sector) /* aborted */ |
| 1626 | bitmap_end_sync(mddev->bitmap, mddev->curr_resync, | 1890 | bitmap_end_sync(mddev->bitmap, mddev->curr_resync, |
| @@ -1631,6 +1895,123 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i | |||
| 1631 | 1895 | ||
| 1632 | return 0; | 1896 | return 0; |
| 1633 | } | 1897 | } |
| 1898 | |||
| 1899 | if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) { | ||
| 1900 | /* reshaping is quite different to recovery/resync so it is | ||
| 1901 | * handled quite separately ... here. | ||
| 1902 | * | ||
| 1903 | * On each call to sync_request, we gather one chunk worth of | ||
| 1904 | * destination stripes and flag them as expanding. | ||
| 1905 | * Then we find all the source stripes and request reads. | ||
| 1906 | * As the reads complete, handle_stripe will copy the data | ||
| 1907 | * into the destination stripe and release that stripe. | ||
| 1908 | */ | ||
| 1909 | int i; | ||
| 1910 | int dd_idx; | ||
| 1911 | sector_t writepos, safepos, gap; | ||
| 1912 | |||
| 1913 | if (sector_nr == 0 && | ||
| 1914 | conf->expand_progress != 0) { | ||
| 1915 | /* restarting in the middle, skip the initial sectors */ | ||
| 1916 | sector_nr = conf->expand_progress; | ||
| 1917 | sector_div(sector_nr, conf->raid_disks-1); | ||
| 1918 | *skipped = 1; | ||
| 1919 | return sector_nr; | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | /* we update the metadata when there is more than 3Meg | ||
| 1923 | * in the block range (that is rather arbitrary, should | ||
| 1924 | * probably be time based) or when the data about to be | ||
| 1925 | * copied would over-write the source of the data at | ||
| 1926 | * the front of the range. | ||
| 1927 | * i.e. one new_stripe forward from expand_progress new_maps | ||
| 1928 | * to after where expand_lo old_maps to | ||
| 1929 | */ | ||
| 1930 | writepos = conf->expand_progress + | ||
| 1931 | conf->chunk_size/512*(conf->raid_disks-1); | ||
| 1932 | sector_div(writepos, conf->raid_disks-1); | ||
| 1933 | safepos = conf->expand_lo; | ||
| 1934 | sector_div(safepos, conf->previous_raid_disks-1); | ||
| 1935 | gap = conf->expand_progress - conf->expand_lo; | ||
| 1936 | |||
| 1937 | if (writepos >= safepos || | ||
| 1938 | gap > (conf->raid_disks-1)*3000*2 /*3Meg*/) { | ||
| 1939 | /* Cannot proceed until we've updated the superblock... */ | ||
| 1940 | wait_event(conf->wait_for_overlap, | ||
| 1941 | atomic_read(&conf->reshape_stripes)==0); | ||
| 1942 | mddev->reshape_position = conf->expand_progress; | ||
| 1943 | mddev->sb_dirty = 1; | ||
| 1944 | md_wakeup_thread(mddev->thread); | ||
| 1945 | wait_event(mddev->sb_wait, mddev->sb_dirty == 0 || | ||
| 1946 | kthread_should_stop()); | ||
| 1947 | spin_lock_irq(&conf->device_lock); | ||
| 1948 | conf->expand_lo = mddev->reshape_position; | ||
| 1949 | spin_unlock_irq(&conf->device_lock); | ||
| 1950 | wake_up(&conf->wait_for_overlap); | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | for (i=0; i < conf->chunk_size/512; i+= STRIPE_SECTORS) { | ||
| 1954 | int j; | ||
| 1955 | int skipped = 0; | ||
| 1956 | pd_idx = stripe_to_pdidx(sector_nr+i, conf, conf->raid_disks); | ||
| 1957 | sh = get_active_stripe(conf, sector_nr+i, | ||
| 1958 | conf->raid_disks, pd_idx, 0); | ||
| 1959 | set_bit(STRIPE_EXPANDING, &sh->state); | ||
| 1960 | atomic_inc(&conf->reshape_stripes); | ||
| 1961 | /* If any of this stripe is beyond the end of the old | ||
| 1962 | * array, then we need to zero those blocks | ||
| 1963 | */ | ||
| 1964 | for (j=sh->disks; j--;) { | ||
| 1965 | sector_t s; | ||
| 1966 | if (j == sh->pd_idx) | ||
| 1967 | continue; | ||
| 1968 | s = compute_blocknr(sh, j); | ||
| 1969 | if (s < (mddev->array_size<<1)) { | ||
| 1970 | skipped = 1; | ||
| 1971 | continue; | ||
| 1972 | } | ||
| 1973 | memset(page_address(sh->dev[j].page), 0, STRIPE_SIZE); | ||
| 1974 | set_bit(R5_Expanded, &sh->dev[j].flags); | ||
| 1975 | set_bit(R5_UPTODATE, &sh->dev[j].flags); | ||
| 1976 | } | ||
| 1977 | if (!skipped) { | ||
| 1978 | set_bit(STRIPE_EXPAND_READY, &sh->state); | ||
| 1979 | set_bit(STRIPE_HANDLE, &sh->state); | ||
| 1980 | } | ||
| 1981 | release_stripe(sh); | ||
| 1982 | } | ||
| 1983 | spin_lock_irq(&conf->device_lock); | ||
| 1984 | conf->expand_progress = (sector_nr + i)*(conf->raid_disks-1); | ||
| 1985 | spin_unlock_irq(&conf->device_lock); | ||
| 1986 | /* Ok, those stripe are ready. We can start scheduling | ||
| 1987 | * reads on the source stripes. | ||
| 1988 | * The source stripes are determined by mapping the first and last | ||
| 1989 | * block on the destination stripes. | ||
| 1990 | */ | ||
| 1991 | raid_disks = conf->previous_raid_disks; | ||
| 1992 | data_disks = raid_disks - 1; | ||
| 1993 | first_sector = | ||
| 1994 | raid5_compute_sector(sector_nr*(conf->raid_disks-1), | ||
| 1995 | raid_disks, data_disks, | ||
| 1996 | &dd_idx, &pd_idx, conf); | ||
| 1997 | last_sector = | ||
| 1998 | raid5_compute_sector((sector_nr+conf->chunk_size/512) | ||
| 1999 | *(conf->raid_disks-1) -1, | ||
| 2000 | raid_disks, data_disks, | ||
| 2001 | &dd_idx, &pd_idx, conf); | ||
| 2002 | if (last_sector >= (mddev->size<<1)) | ||
| 2003 | last_sector = (mddev->size<<1)-1; | ||
| 2004 | while (first_sector <= last_sector) { | ||
| 2005 | pd_idx = stripe_to_pdidx(first_sector, conf, conf->previous_raid_disks); | ||
| 2006 | sh = get_active_stripe(conf, first_sector, | ||
| 2007 | conf->previous_raid_disks, pd_idx, 0); | ||
| 2008 | set_bit(STRIPE_EXPAND_SOURCE, &sh->state); | ||
| 2009 | set_bit(STRIPE_HANDLE, &sh->state); | ||
| 2010 | release_stripe(sh); | ||
| 2011 | first_sector += STRIPE_SECTORS; | ||
| 2012 | } | ||
| 2013 | return conf->chunk_size>>9; | ||
| 2014 | } | ||
| 1634 | /* if there is 1 or more failed drives and we are trying | 2015 | /* if there is 1 or more failed drives and we are trying |
| 1635 | * to resync, then assert that we are finished, because there is | 2016 | * to resync, then assert that we are finished, because there is |
| 1636 | * nothing we can do. | 2017 | * nothing we can do. |
| @@ -1649,16 +2030,10 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i | |||
| 1649 | return sync_blocks * STRIPE_SECTORS; /* keep things rounded to whole stripes */ | 2030 | return sync_blocks * STRIPE_SECTORS; /* keep things rounded to whole stripes */ |
| 1650 | } | 2031 | } |
| 1651 | 2032 | ||
| 1652 | x = sector_nr; | 2033 | pd_idx = stripe_to_pdidx(sector_nr, conf, raid_disks); |
| 1653 | chunk_offset = sector_div(x, sectors_per_chunk); | 2034 | sh = get_active_stripe(conf, sector_nr, raid_disks, pd_idx, 1); |
| 1654 | stripe = x; | ||
| 1655 | BUG_ON(x != stripe); | ||
| 1656 | |||
| 1657 | first_sector = raid5_compute_sector((sector_t)stripe*data_disks*sectors_per_chunk | ||
| 1658 | + chunk_offset, raid_disks, data_disks, &dd_idx, &pd_idx, conf); | ||
| 1659 | sh = get_active_stripe(conf, sector_nr, pd_idx, 1); | ||
| 1660 | if (sh == NULL) { | 2035 | if (sh == NULL) { |
| 1661 | sh = get_active_stripe(conf, sector_nr, pd_idx, 0); | 2036 | sh = get_active_stripe(conf, sector_nr, raid_disks, pd_idx, 0); |
| 1662 | /* make sure we don't swamp the stripe cache if someone else | 2037 | /* make sure we don't swamp the stripe cache if someone else |
| 1663 | * is trying to get access | 2038 | * is trying to get access |
| 1664 | */ | 2039 | */ |
| @@ -1822,11 +2197,64 @@ static int run(mddev_t *mddev) | |||
| 1822 | return -EIO; | 2197 | return -EIO; |
| 1823 | } | 2198 | } |
| 1824 | 2199 | ||
| 1825 | mddev->private = kzalloc(sizeof (raid5_conf_t) | 2200 | if (mddev->reshape_position != MaxSector) { |
| 1826 | + mddev->raid_disks * sizeof(struct disk_info), | 2201 | /* Check that we can continue the reshape. |
| 1827 | GFP_KERNEL); | 2202 | * Currently only disks can change, it must |
| 2203 | * increase, and we must be past the point where | ||
| 2204 | * a stripe over-writes itself | ||
| 2205 | */ | ||
| 2206 | sector_t here_new, here_old; | ||
| 2207 | int old_disks; | ||
| 2208 | |||
| 2209 | if (mddev->new_level != mddev->level || | ||
| 2210 | mddev->new_layout != mddev->layout || | ||
| 2211 | mddev->new_chunk != mddev->chunk_size) { | ||
| 2212 | printk(KERN_ERR "raid5: %s: unsupported reshape required - aborting.\n", | ||
| 2213 | mdname(mddev)); | ||
| 2214 | return -EINVAL; | ||
| 2215 | } | ||
| 2216 | if (mddev->delta_disks <= 0) { | ||
| 2217 | printk(KERN_ERR "raid5: %s: unsupported reshape (reduce disks) required - aborting.\n", | ||
| 2218 | mdname(mddev)); | ||
| 2219 | return -EINVAL; | ||
| 2220 | } | ||
| 2221 | old_disks = mddev->raid_disks - mddev->delta_disks; | ||
| 2222 | /* reshape_position must be on a new-stripe boundary, and one | ||
| 2223 | * further up in new geometry must map after here in old geometry. | ||
| 2224 | */ | ||
| 2225 | here_new = mddev->reshape_position; | ||
| 2226 | if (sector_div(here_new, (mddev->chunk_size>>9)*(mddev->raid_disks-1))) { | ||
| 2227 | printk(KERN_ERR "raid5: reshape_position not on a stripe boundary\n"); | ||
| 2228 | return -EINVAL; | ||
| 2229 | } | ||
| 2230 | /* here_new is the stripe we will write to */ | ||
| 2231 | here_old = mddev->reshape_position; | ||
| 2232 | sector_div(here_old, (mddev->chunk_size>>9)*(old_disks-1)); | ||
| 2233 | /* here_old is the first stripe that we might need to read from */ | ||
| 2234 | if (here_new >= here_old) { | ||
| 2235 | /* Reading from the same stripe as writing to - bad */ | ||
| 2236 | printk(KERN_ERR "raid5: reshape_position too early for auto-recovery - aborting.\n"); | ||
| 2237 | return -EINVAL; | ||
| 2238 | } | ||
| 2239 | printk(KERN_INFO "raid5: reshape will continue\n"); | ||
| 2240 | /* OK, we should be able to continue; */ | ||
| 2241 | } | ||
| 2242 | |||
| 2243 | |||
| 2244 | mddev->private = kzalloc(sizeof (raid5_conf_t), GFP_KERNEL); | ||
| 1828 | if ((conf = mddev->private) == NULL) | 2245 | if ((conf = mddev->private) == NULL) |
| 1829 | goto abort; | 2246 | goto abort; |
| 2247 | if (mddev->reshape_position == MaxSector) { | ||
| 2248 | conf->previous_raid_disks = conf->raid_disks = mddev->raid_disks; | ||
| 2249 | } else { | ||
| 2250 | conf->raid_disks = mddev->raid_disks; | ||
| 2251 | conf->previous_raid_disks = mddev->raid_disks - mddev->delta_disks; | ||
| 2252 | } | ||
| 2253 | |||
| 2254 | conf->disks = kzalloc(conf->raid_disks * sizeof(struct disk_info), | ||
| 2255 | GFP_KERNEL); | ||
| 2256 | if (!conf->disks) | ||
| 2257 | goto abort; | ||
| 1830 | 2258 | ||
| 1831 | conf->mddev = mddev; | 2259 | conf->mddev = mddev; |
| 1832 | 2260 | ||
| @@ -1847,7 +2275,7 @@ static int run(mddev_t *mddev) | |||
| 1847 | 2275 | ||
| 1848 | ITERATE_RDEV(mddev,rdev,tmp) { | 2276 | ITERATE_RDEV(mddev,rdev,tmp) { |
| 1849 | raid_disk = rdev->raid_disk; | 2277 | raid_disk = rdev->raid_disk; |
| 1850 | if (raid_disk >= mddev->raid_disks | 2278 | if (raid_disk >= conf->raid_disks |
| 1851 | || raid_disk < 0) | 2279 | || raid_disk < 0) |
| 1852 | continue; | 2280 | continue; |
| 1853 | disk = conf->disks + raid_disk; | 2281 | disk = conf->disks + raid_disk; |
| @@ -1863,7 +2291,6 @@ static int run(mddev_t *mddev) | |||
| 1863 | } | 2291 | } |
| 1864 | } | 2292 | } |
| 1865 | 2293 | ||
| 1866 | conf->raid_disks = mddev->raid_disks; | ||
| 1867 | /* | 2294 | /* |
| 1868 | * 0 for a fully functional array, 1 for a degraded array. | 2295 | * 0 for a fully functional array, 1 for a degraded array. |
| 1869 | */ | 2296 | */ |
| @@ -1873,6 +2300,7 @@ static int run(mddev_t *mddev) | |||
| 1873 | conf->level = mddev->level; | 2300 | conf->level = mddev->level; |
| 1874 | conf->algorithm = mddev->layout; | 2301 | conf->algorithm = mddev->layout; |
| 1875 | conf->max_nr_stripes = NR_STRIPES; | 2302 | conf->max_nr_stripes = NR_STRIPES; |
| 2303 | conf->expand_progress = mddev->reshape_position; | ||
| 1876 | 2304 | ||
| 1877 | /* device size must be a multiple of chunk size */ | 2305 | /* device size must be a multiple of chunk size */ |
| 1878 | mddev->size &= ~(mddev->chunk_size/1024 -1); | 2306 | mddev->size &= ~(mddev->chunk_size/1024 -1); |
| @@ -1945,6 +2373,21 @@ static int run(mddev_t *mddev) | |||
| 1945 | 2373 | ||
| 1946 | print_raid5_conf(conf); | 2374 | print_raid5_conf(conf); |
| 1947 | 2375 | ||
| 2376 | if (conf->expand_progress != MaxSector) { | ||
| 2377 | printk("...ok start reshape thread\n"); | ||
| 2378 | conf->expand_lo = conf->expand_progress; | ||
| 2379 | atomic_set(&conf->reshape_stripes, 0); | ||
| 2380 | clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); | ||
| 2381 | clear_bit(MD_RECOVERY_CHECK, &mddev->recovery); | ||
| 2382 | set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); | ||
| 2383 | set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); | ||
| 2384 | mddev->sync_thread = md_register_thread(md_do_sync, mddev, | ||
| 2385 | "%s_reshape"); | ||
| 2386 | /* FIXME if md_register_thread fails?? */ | ||
| 2387 | md_wakeup_thread(mddev->sync_thread); | ||
| 2388 | |||
| 2389 | } | ||
| 2390 | |||
| 1948 | /* read-ahead size must cover two whole stripes, which is | 2391 | /* read-ahead size must cover two whole stripes, which is |
| 1949 | * 2 * (n-1) * chunksize where 'n' is the number of raid devices | 2392 | * 2 * (n-1) * chunksize where 'n' is the number of raid devices |
| 1950 | */ | 2393 | */ |
| @@ -1960,12 +2403,13 @@ static int run(mddev_t *mddev) | |||
| 1960 | 2403 | ||
| 1961 | mddev->queue->unplug_fn = raid5_unplug_device; | 2404 | mddev->queue->unplug_fn = raid5_unplug_device; |
| 1962 | mddev->queue->issue_flush_fn = raid5_issue_flush; | 2405 | mddev->queue->issue_flush_fn = raid5_issue_flush; |
| 2406 | mddev->array_size = mddev->size * (conf->previous_raid_disks - 1); | ||
| 1963 | 2407 | ||
| 1964 | mddev->array_size = mddev->size * (mddev->raid_disks - 1); | ||
| 1965 | return 0; | 2408 | return 0; |
| 1966 | abort: | 2409 | abort: |
| 1967 | if (conf) { | 2410 | if (conf) { |
| 1968 | print_raid5_conf(conf); | 2411 | print_raid5_conf(conf); |
| 2412 | kfree(conf->disks); | ||
| 1969 | kfree(conf->stripe_hashtbl); | 2413 | kfree(conf->stripe_hashtbl); |
| 1970 | kfree(conf); | 2414 | kfree(conf); |
| 1971 | } | 2415 | } |
| @@ -1986,6 +2430,7 @@ static int stop(mddev_t *mddev) | |||
| 1986 | kfree(conf->stripe_hashtbl); | 2430 | kfree(conf->stripe_hashtbl); |
| 1987 | blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ | 2431 | blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ |
| 1988 | sysfs_remove_group(&mddev->kobj, &raid5_attrs_group); | 2432 | sysfs_remove_group(&mddev->kobj, &raid5_attrs_group); |
| 2433 | kfree(conf->disks); | ||
| 1989 | kfree(conf); | 2434 | kfree(conf); |
| 1990 | mddev->private = NULL; | 2435 | mddev->private = NULL; |
| 1991 | return 0; | 2436 | return 0; |
| @@ -2001,7 +2446,7 @@ static void print_sh (struct stripe_head *sh) | |||
| 2001 | printk("sh %llu, count %d.\n", | 2446 | printk("sh %llu, count %d.\n", |
| 2002 | (unsigned long long)sh->sector, atomic_read(&sh->count)); | 2447 | (unsigned long long)sh->sector, atomic_read(&sh->count)); |
| 2003 | printk("sh %llu, ", (unsigned long long)sh->sector); | 2448 | printk("sh %llu, ", (unsigned long long)sh->sector); |
| 2004 | for (i = 0; i < sh->raid_conf->raid_disks; i++) { | 2449 | for (i = 0; i < sh->disks; i++) { |
| 2005 | printk("(cache%d: %p %ld) ", | 2450 | printk("(cache%d: %p %ld) ", |
| 2006 | i, sh->dev[i].page, sh->dev[i].flags); | 2451 | i, sh->dev[i].page, sh->dev[i].flags); |
| 2007 | } | 2452 | } |
| @@ -2132,7 +2577,7 @@ static int raid5_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 2132 | /* | 2577 | /* |
| 2133 | * find the disk ... | 2578 | * find the disk ... |
| 2134 | */ | 2579 | */ |
| 2135 | for (disk=0; disk < mddev->raid_disks; disk++) | 2580 | for (disk=0; disk < conf->raid_disks; disk++) |
| 2136 | if ((p=conf->disks + disk)->rdev == NULL) { | 2581 | if ((p=conf->disks + disk)->rdev == NULL) { |
| 2137 | clear_bit(In_sync, &rdev->flags); | 2582 | clear_bit(In_sync, &rdev->flags); |
| 2138 | rdev->raid_disk = disk; | 2583 | rdev->raid_disk = disk; |
| @@ -2168,11 +2613,146 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors) | |||
| 2168 | return 0; | 2613 | return 0; |
| 2169 | } | 2614 | } |
| 2170 | 2615 | ||
| 2616 | #ifdef CONFIG_MD_RAID5_RESHAPE | ||
| 2617 | static int raid5_check_reshape(mddev_t *mddev) | ||
| 2618 | { | ||
| 2619 | raid5_conf_t *conf = mddev_to_conf(mddev); | ||
| 2620 | int err; | ||
| 2621 | |||
| 2622 | if (mddev->delta_disks < 0 || | ||
| 2623 | mddev->new_level != mddev->level) | ||
| 2624 | return -EINVAL; /* Cannot shrink array or change level yet */ | ||
| 2625 | if (mddev->delta_disks == 0) | ||
| 2626 | return 0; /* nothing to do */ | ||
| 2627 | |||
| 2628 | /* Can only proceed if there are plenty of stripe_heads. | ||
| 2629 | * We need a minimum of one full stripe,, and for sensible progress | ||
| 2630 | * it is best to have about 4 times that. | ||
| 2631 | * If we require 4 times, then the default 256 4K stripe_heads will | ||
| 2632 | * allow for chunk sizes up to 256K, which is probably OK. | ||
| 2633 | * If the chunk size is greater, user-space should request more | ||
| 2634 | * stripe_heads first. | ||
| 2635 | */ | ||
| 2636 | if ((mddev->chunk_size / STRIPE_SIZE) * 4 > conf->max_nr_stripes || | ||
| 2637 | (mddev->new_chunk / STRIPE_SIZE) * 4 > conf->max_nr_stripes) { | ||
| 2638 | printk(KERN_WARNING "raid5: reshape: not enough stripes. Needed %lu\n", | ||
| 2639 | (mddev->chunk_size / STRIPE_SIZE)*4); | ||
| 2640 | return -ENOSPC; | ||
| 2641 | } | ||
| 2642 | |||
| 2643 | err = resize_stripes(conf, conf->raid_disks + mddev->delta_disks); | ||
| 2644 | if (err) | ||
| 2645 | return err; | ||
| 2646 | |||
| 2647 | /* looks like we might be able to manage this */ | ||
| 2648 | return 0; | ||
| 2649 | } | ||
| 2650 | |||
| 2651 | static int raid5_start_reshape(mddev_t *mddev) | ||
| 2652 | { | ||
| 2653 | raid5_conf_t *conf = mddev_to_conf(mddev); | ||
| 2654 | mdk_rdev_t *rdev; | ||
| 2655 | struct list_head *rtmp; | ||
| 2656 | int spares = 0; | ||
| 2657 | int added_devices = 0; | ||
| 2658 | |||
| 2659 | if (mddev->degraded || | ||
| 2660 | test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) | ||
| 2661 | return -EBUSY; | ||
| 2662 | |||
| 2663 | ITERATE_RDEV(mddev, rdev, rtmp) | ||
| 2664 | if (rdev->raid_disk < 0 && | ||
| 2665 | !test_bit(Faulty, &rdev->flags)) | ||
| 2666 | spares++; | ||
| 2667 | |||
| 2668 | if (spares < mddev->delta_disks-1) | ||
| 2669 | /* Not enough devices even to make a degraded array | ||
| 2670 | * of that size | ||
| 2671 | */ | ||
| 2672 | return -EINVAL; | ||
| 2673 | |||
| 2674 | atomic_set(&conf->reshape_stripes, 0); | ||
| 2675 | spin_lock_irq(&conf->device_lock); | ||
| 2676 | conf->previous_raid_disks = conf->raid_disks; | ||
| 2677 | conf->raid_disks += mddev->delta_disks; | ||
| 2678 | conf->expand_progress = 0; | ||
| 2679 | conf->expand_lo = 0; | ||
| 2680 | spin_unlock_irq(&conf->device_lock); | ||
| 2681 | |||
| 2682 | /* Add some new drives, as many as will fit. | ||
| 2683 | * We know there are enough to make the newly sized array work. | ||
| 2684 | */ | ||
| 2685 | ITERATE_RDEV(mddev, rdev, rtmp) | ||
| 2686 | if (rdev->raid_disk < 0 && | ||
| 2687 | !test_bit(Faulty, &rdev->flags)) { | ||
| 2688 | if (raid5_add_disk(mddev, rdev)) { | ||
| 2689 | char nm[20]; | ||
| 2690 | set_bit(In_sync, &rdev->flags); | ||
| 2691 | conf->working_disks++; | ||
| 2692 | added_devices++; | ||
| 2693 | sprintf(nm, "rd%d", rdev->raid_disk); | ||
| 2694 | sysfs_create_link(&mddev->kobj, &rdev->kobj, nm); | ||
| 2695 | } else | ||
| 2696 | break; | ||
| 2697 | } | ||
| 2698 | |||
| 2699 | mddev->degraded = (conf->raid_disks - conf->previous_raid_disks) - added_devices; | ||
| 2700 | mddev->raid_disks = conf->raid_disks; | ||
| 2701 | mddev->reshape_position = 0; | ||
| 2702 | mddev->sb_dirty = 1; | ||
| 2703 | |||
| 2704 | clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); | ||
| 2705 | clear_bit(MD_RECOVERY_CHECK, &mddev->recovery); | ||
| 2706 | set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); | ||
| 2707 | set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); | ||
| 2708 | mddev->sync_thread = md_register_thread(md_do_sync, mddev, | ||
| 2709 | "%s_reshape"); | ||
| 2710 | if (!mddev->sync_thread) { | ||
| 2711 | mddev->recovery = 0; | ||
| 2712 | spin_lock_irq(&conf->device_lock); | ||
| 2713 | mddev->raid_disks = conf->raid_disks = conf->previous_raid_disks; | ||
| 2714 | conf->expand_progress = MaxSector; | ||
| 2715 | spin_unlock_irq(&conf->device_lock); | ||
| 2716 | return -EAGAIN; | ||
| 2717 | } | ||
| 2718 | md_wakeup_thread(mddev->sync_thread); | ||
| 2719 | md_new_event(mddev); | ||
| 2720 | return 0; | ||
| 2721 | } | ||
| 2722 | #endif | ||
| 2723 | |||
| 2724 | static void end_reshape(raid5_conf_t *conf) | ||
| 2725 | { | ||
| 2726 | struct block_device *bdev; | ||
| 2727 | |||
| 2728 | if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) { | ||
| 2729 | conf->mddev->array_size = conf->mddev->size * (conf->raid_disks-1); | ||
| 2730 | set_capacity(conf->mddev->gendisk, conf->mddev->array_size << 1); | ||
| 2731 | conf->mddev->changed = 1; | ||
| 2732 | |||
| 2733 | bdev = bdget_disk(conf->mddev->gendisk, 0); | ||
| 2734 | if (bdev) { | ||
| 2735 | mutex_lock(&bdev->bd_inode->i_mutex); | ||
| 2736 | i_size_write(bdev->bd_inode, conf->mddev->array_size << 10); | ||
| 2737 | mutex_unlock(&bdev->bd_inode->i_mutex); | ||
| 2738 | bdput(bdev); | ||
| 2739 | } | ||
| 2740 | spin_lock_irq(&conf->device_lock); | ||
| 2741 | conf->expand_progress = MaxSector; | ||
| 2742 | spin_unlock_irq(&conf->device_lock); | ||
| 2743 | conf->mddev->reshape_position = MaxSector; | ||
| 2744 | } | ||
| 2745 | } | ||
| 2746 | |||
| 2171 | static void raid5_quiesce(mddev_t *mddev, int state) | 2747 | static void raid5_quiesce(mddev_t *mddev, int state) |
| 2172 | { | 2748 | { |
| 2173 | raid5_conf_t *conf = mddev_to_conf(mddev); | 2749 | raid5_conf_t *conf = mddev_to_conf(mddev); |
| 2174 | 2750 | ||
| 2175 | switch(state) { | 2751 | switch(state) { |
| 2752 | case 2: /* resume for a suspend */ | ||
| 2753 | wake_up(&conf->wait_for_overlap); | ||
| 2754 | break; | ||
| 2755 | |||
| 2176 | case 1: /* stop all writes */ | 2756 | case 1: /* stop all writes */ |
| 2177 | spin_lock_irq(&conf->device_lock); | 2757 | spin_lock_irq(&conf->device_lock); |
| 2178 | conf->quiesce = 1; | 2758 | conf->quiesce = 1; |
| @@ -2186,6 +2766,7 @@ static void raid5_quiesce(mddev_t *mddev, int state) | |||
| 2186 | spin_lock_irq(&conf->device_lock); | 2766 | spin_lock_irq(&conf->device_lock); |
| 2187 | conf->quiesce = 0; | 2767 | conf->quiesce = 0; |
| 2188 | wake_up(&conf->wait_for_stripe); | 2768 | wake_up(&conf->wait_for_stripe); |
| 2769 | wake_up(&conf->wait_for_overlap); | ||
| 2189 | spin_unlock_irq(&conf->device_lock); | 2770 | spin_unlock_irq(&conf->device_lock); |
| 2190 | break; | 2771 | break; |
| 2191 | } | 2772 | } |
| @@ -2206,6 +2787,10 @@ static struct mdk_personality raid5_personality = | |||
| 2206 | .spare_active = raid5_spare_active, | 2787 | .spare_active = raid5_spare_active, |
| 2207 | .sync_request = sync_request, | 2788 | .sync_request = sync_request, |
| 2208 | .resize = raid5_resize, | 2789 | .resize = raid5_resize, |
| 2790 | #ifdef CONFIG_MD_RAID5_RESHAPE | ||
| 2791 | .check_reshape = raid5_check_reshape, | ||
| 2792 | .start_reshape = raid5_start_reshape, | ||
| 2793 | #endif | ||
| 2209 | .quiesce = raid5_quiesce, | 2794 | .quiesce = raid5_quiesce, |
| 2210 | }; | 2795 | }; |
| 2211 | 2796 | ||
diff --git a/drivers/md/raid6main.c b/drivers/md/raid6main.c index cd477ebf2ee4..6df4930fddec 100644 --- a/drivers/md/raid6main.c +++ b/drivers/md/raid6main.c | |||
| @@ -331,9 +331,9 @@ static int grow_stripes(raid6_conf_t *conf, int num) | |||
| 331 | kmem_cache_t *sc; | 331 | kmem_cache_t *sc; |
| 332 | int devs = conf->raid_disks; | 332 | int devs = conf->raid_disks; |
| 333 | 333 | ||
| 334 | sprintf(conf->cache_name, "raid6/%s", mdname(conf->mddev)); | 334 | sprintf(conf->cache_name[0], "raid6/%s", mdname(conf->mddev)); |
| 335 | 335 | ||
| 336 | sc = kmem_cache_create(conf->cache_name, | 336 | sc = kmem_cache_create(conf->cache_name[0], |
| 337 | sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev), | 337 | sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev), |
| 338 | 0, 0, NULL, NULL); | 338 | 0, 0, NULL, NULL); |
| 339 | if (!sc) | 339 | if (!sc) |
| @@ -2006,11 +2006,14 @@ static int run(mddev_t *mddev) | |||
| 2006 | return -EIO; | 2006 | return -EIO; |
| 2007 | } | 2007 | } |
| 2008 | 2008 | ||
| 2009 | mddev->private = kzalloc(sizeof (raid6_conf_t) | 2009 | mddev->private = kzalloc(sizeof (raid6_conf_t), GFP_KERNEL); |
| 2010 | + mddev->raid_disks * sizeof(struct disk_info), | ||
| 2011 | GFP_KERNEL); | ||
| 2012 | if ((conf = mddev->private) == NULL) | 2010 | if ((conf = mddev->private) == NULL) |
| 2013 | goto abort; | 2011 | goto abort; |
| 2012 | conf->disks = kzalloc(mddev->raid_disks * sizeof(struct disk_info), | ||
| 2013 | GFP_KERNEL); | ||
| 2014 | if (!conf->disks) | ||
| 2015 | goto abort; | ||
| 2016 | |||
| 2014 | conf->mddev = mddev; | 2017 | conf->mddev = mddev; |
| 2015 | 2018 | ||
| 2016 | if ((conf->stripe_hashtbl = kzalloc(PAGE_SIZE, GFP_KERNEL)) == NULL) | 2019 | if ((conf->stripe_hashtbl = kzalloc(PAGE_SIZE, GFP_KERNEL)) == NULL) |
| @@ -2158,6 +2161,7 @@ abort: | |||
| 2158 | print_raid6_conf(conf); | 2161 | print_raid6_conf(conf); |
| 2159 | safe_put_page(conf->spare_page); | 2162 | safe_put_page(conf->spare_page); |
| 2160 | kfree(conf->stripe_hashtbl); | 2163 | kfree(conf->stripe_hashtbl); |
| 2164 | kfree(conf->disks); | ||
| 2161 | kfree(conf); | 2165 | kfree(conf); |
| 2162 | } | 2166 | } |
| 2163 | mddev->private = NULL; | 2167 | mddev->private = NULL; |
diff --git a/drivers/misc/ibmasm/heartbeat.c b/drivers/misc/ibmasm/heartbeat.c index f295401fac21..7fd7a43e38de 100644 --- a/drivers/misc/ibmasm/heartbeat.c +++ b/drivers/misc/ibmasm/heartbeat.c | |||
| @@ -52,12 +52,13 @@ static struct notifier_block panic_notifier = { panic_happened, NULL, 1 }; | |||
| 52 | 52 | ||
| 53 | void ibmasm_register_panic_notifier(void) | 53 | void ibmasm_register_panic_notifier(void) |
| 54 | { | 54 | { |
| 55 | notifier_chain_register(&panic_notifier_list, &panic_notifier); | 55 | atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | void ibmasm_unregister_panic_notifier(void) | 58 | void ibmasm_unregister_panic_notifier(void) |
| 59 | { | 59 | { |
| 60 | notifier_chain_unregister(&panic_notifier_list, &panic_notifier); | 60 | atomic_notifier_chain_unregister(&panic_notifier_list, |
| 61 | &panic_notifier); | ||
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | 64 | ||
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 2d0ac169a86c..f13a539dc169 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c | |||
| @@ -3159,7 +3159,7 @@ static int bond_slave_netdev_event(unsigned long event, struct net_device *slave | |||
| 3159 | * bond_netdev_event: handle netdev notifier chain events. | 3159 | * bond_netdev_event: handle netdev notifier chain events. |
| 3160 | * | 3160 | * |
| 3161 | * This function receives events for the netdev chain. The caller (an | 3161 | * This function receives events for the netdev chain. The caller (an |
| 3162 | * ioctl handler calling notifier_call_chain) holds the necessary | 3162 | * ioctl handler calling blocking_notifier_call_chain) holds the necessary |
| 3163 | * locks for us to safely manipulate the slave devices (RTNL lock, | 3163 | * locks for us to safely manipulate the slave devices (RTNL lock, |
| 3164 | * dev_probe_lock). | 3164 | * dev_probe_lock). |
| 3165 | */ | 3165 | */ |
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index 83141a3ff546..9aa074b44dd3 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c | |||
| @@ -207,7 +207,7 @@ static int __init nsc_ircc_init(void) | |||
| 207 | /* Register with PnP subsystem to detect disable ports */ | 207 | /* Register with PnP subsystem to detect disable ports */ |
| 208 | ret = pnp_register_driver(&nsc_ircc_pnp_driver); | 208 | ret = pnp_register_driver(&nsc_ircc_pnp_driver); |
| 209 | 209 | ||
| 210 | if (ret >= 0) | 210 | if (!ret) |
| 211 | pnp_registered = 1; | 211 | pnp_registered = 1; |
| 212 | 212 | ||
| 213 | ret = -ENODEV; | 213 | ret = -ENODEV; |
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c index 3627a2d7f79f..298f2ddb2c17 100644 --- a/drivers/parisc/led.c +++ b/drivers/parisc/led.c | |||
| @@ -499,11 +499,16 @@ static int led_halt(struct notifier_block *, unsigned long, void *); | |||
| 499 | static struct notifier_block led_notifier = { | 499 | static struct notifier_block led_notifier = { |
| 500 | .notifier_call = led_halt, | 500 | .notifier_call = led_halt, |
| 501 | }; | 501 | }; |
| 502 | static int notifier_disabled = 0; | ||
| 502 | 503 | ||
| 503 | static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) | 504 | static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) |
| 504 | { | 505 | { |
| 505 | char *txt; | 506 | char *txt; |
| 506 | 507 | ||
| 508 | if (notifier_disabled) | ||
| 509 | return NOTIFY_OK; | ||
| 510 | |||
| 511 | notifier_disabled = 1; | ||
| 507 | switch (event) { | 512 | switch (event) { |
| 508 | case SYS_RESTART: txt = "SYSTEM RESTART"; | 513 | case SYS_RESTART: txt = "SYSTEM RESTART"; |
| 509 | break; | 514 | break; |
| @@ -527,7 +532,6 @@ static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) | |||
| 527 | if (led_func_ptr) | 532 | if (led_func_ptr) |
| 528 | led_func_ptr(0xff); /* turn all LEDs ON */ | 533 | led_func_ptr(0xff); /* turn all LEDs ON */ |
| 529 | 534 | ||
| 530 | unregister_reboot_notifier(&led_notifier); | ||
| 531 | return NOTIFY_OK; | 535 | return NOTIFY_OK; |
| 532 | } | 536 | } |
| 533 | 537 | ||
| @@ -758,6 +762,12 @@ not_found: | |||
| 758 | return 1; | 762 | return 1; |
| 759 | } | 763 | } |
| 760 | 764 | ||
| 765 | static void __exit led_exit(void) | ||
| 766 | { | ||
| 767 | unregister_reboot_notifier(&led_notifier); | ||
| 768 | return; | ||
| 769 | } | ||
| 770 | |||
| 761 | #ifdef CONFIG_PROC_FS | 771 | #ifdef CONFIG_PROC_FS |
| 762 | module_init(led_create_procfs) | 772 | module_init(led_create_procfs) |
| 763 | #endif | 773 | #endif |
diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c index 54b2b7f20b96..0bcab83b4080 100644 --- a/drivers/parisc/power.c +++ b/drivers/parisc/power.c | |||
| @@ -251,7 +251,8 @@ static int __init power_init(void) | |||
| 251 | } | 251 | } |
| 252 | 252 | ||
| 253 | /* Register a call for panic conditions. */ | 253 | /* Register a call for panic conditions. */ |
| 254 | notifier_chain_register(&panic_notifier_list, &parisc_panic_block); | 254 | atomic_notifier_chain_register(&panic_notifier_list, |
| 255 | &parisc_panic_block); | ||
| 255 | 256 | ||
| 256 | tasklet_enable(&power_tasklet); | 257 | tasklet_enable(&power_tasklet); |
| 257 | 258 | ||
| @@ -264,7 +265,8 @@ static void __exit power_exit(void) | |||
| 264 | return; | 265 | return; |
| 265 | 266 | ||
| 266 | tasklet_disable(&power_tasklet); | 267 | tasklet_disable(&power_tasklet); |
| 267 | notifier_chain_unregister(&panic_notifier_list, &parisc_panic_block); | 268 | atomic_notifier_chain_unregister(&panic_notifier_list, |
| 269 | &parisc_panic_block); | ||
| 268 | power_tasklet.func = NULL; | 270 | power_tasklet.func = NULL; |
| 269 | pdc_soft_power_button(0); | 271 | pdc_soft_power_button(0); |
| 270 | } | 272 | } |
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 9302b8fd7461..d5890027f8af 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c | |||
| @@ -3126,9 +3126,9 @@ parport_pc_find_isa_ports (int autoirq, int autodma) | |||
| 3126 | * autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY | 3126 | * autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY |
| 3127 | * autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO | 3127 | * autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO |
| 3128 | */ | 3128 | */ |
| 3129 | static int __init parport_pc_find_ports (int autoirq, int autodma) | 3129 | static void __init parport_pc_find_ports (int autoirq, int autodma) |
| 3130 | { | 3130 | { |
| 3131 | int count = 0, r; | 3131 | int count = 0, err; |
| 3132 | 3132 | ||
| 3133 | #ifdef CONFIG_PARPORT_PC_SUPERIO | 3133 | #ifdef CONFIG_PARPORT_PC_SUPERIO |
| 3134 | detect_and_report_winbond (); | 3134 | detect_and_report_winbond (); |
| @@ -3140,23 +3140,17 @@ static int __init parport_pc_find_ports (int autoirq, int autodma) | |||
| 3140 | 3140 | ||
| 3141 | /* PnP ports, skip detection if SuperIO already found them */ | 3141 | /* PnP ports, skip detection if SuperIO already found them */ |
| 3142 | if (!count) { | 3142 | if (!count) { |
| 3143 | r = pnp_register_driver (&parport_pc_pnp_driver); | 3143 | err = pnp_register_driver (&parport_pc_pnp_driver); |
| 3144 | if (r >= 0) { | 3144 | if (!err) |
| 3145 | pnp_registered_parport = 1; | 3145 | pnp_registered_parport = 1; |
| 3146 | count += r; | ||
| 3147 | } | ||
| 3148 | } | 3146 | } |
| 3149 | 3147 | ||
| 3150 | /* ISA ports and whatever (see asm/parport.h). */ | 3148 | /* ISA ports and whatever (see asm/parport.h). */ |
| 3151 | count += parport_pc_find_nonpci_ports (autoirq, autodma); | 3149 | parport_pc_find_nonpci_ports (autoirq, autodma); |
| 3152 | |||
| 3153 | r = pci_register_driver (&parport_pc_pci_driver); | ||
| 3154 | if (r) | ||
| 3155 | return r; | ||
| 3156 | pci_registered_parport = 1; | ||
| 3157 | count += 1; | ||
| 3158 | 3150 | ||
| 3159 | return count; | 3151 | err = pci_register_driver (&parport_pc_pci_driver); |
| 3152 | if (!err) | ||
| 3153 | pci_registered_parport = 1; | ||
| 3160 | } | 3154 | } |
| 3161 | 3155 | ||
| 3162 | /* | 3156 | /* |
| @@ -3381,8 +3375,6 @@ __setup("parport_init_mode=",parport_init_mode_setup); | |||
| 3381 | 3375 | ||
| 3382 | static int __init parport_pc_init(void) | 3376 | static int __init parport_pc_init(void) |
| 3383 | { | 3377 | { |
| 3384 | int count = 0; | ||
| 3385 | |||
| 3386 | if (parse_parport_params()) | 3378 | if (parse_parport_params()) |
| 3387 | return -EINVAL; | 3379 | return -EINVAL; |
| 3388 | 3380 | ||
| @@ -3395,12 +3387,11 @@ static int __init parport_pc_init(void) | |||
| 3395 | break; | 3387 | break; |
| 3396 | if ((io_hi[i]) == PARPORT_IOHI_AUTO) | 3388 | if ((io_hi[i]) == PARPORT_IOHI_AUTO) |
| 3397 | io_hi[i] = 0x400 + io[i]; | 3389 | io_hi[i] = 0x400 + io[i]; |
| 3398 | if (parport_pc_probe_port(io[i], io_hi[i], | 3390 | parport_pc_probe_port(io[i], io_hi[i], |
| 3399 | irqval[i], dmaval[i], NULL)) | 3391 | irqval[i], dmaval[i], NULL); |
| 3400 | count++; | ||
| 3401 | } | 3392 | } |
| 3402 | } else | 3393 | } else |
| 3403 | count += parport_pc_find_ports (irqval[0], dmaval[0]); | 3394 | parport_pc_find_ports (irqval[0], dmaval[0]); |
| 3404 | 3395 | ||
| 3405 | return 0; | 3396 | return 0; |
| 3406 | } | 3397 | } |
diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index b68eef251614..bb19c64073c6 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c | |||
| @@ -47,7 +47,7 @@ static void card_remove(struct pnp_dev * dev) | |||
| 47 | { | 47 | { |
| 48 | dev->card_link = NULL; | 48 | dev->card_link = NULL; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | static void card_remove_first(struct pnp_dev * dev) | 51 | static void card_remove_first(struct pnp_dev * dev) |
| 52 | { | 52 | { |
| 53 | struct pnp_card_driver * drv = to_pnp_card_driver(dev->driver); | 53 | struct pnp_card_driver * drv = to_pnp_card_driver(dev->driver); |
| @@ -361,7 +361,7 @@ static int card_resume(struct pnp_dev *dev) | |||
| 361 | 361 | ||
| 362 | int pnp_register_card_driver(struct pnp_card_driver * drv) | 362 | int pnp_register_card_driver(struct pnp_card_driver * drv) |
| 363 | { | 363 | { |
| 364 | int count; | 364 | int error; |
| 365 | struct list_head *pos, *temp; | 365 | struct list_head *pos, *temp; |
| 366 | 366 | ||
| 367 | drv->link.name = drv->name; | 367 | drv->link.name = drv->name; |
| @@ -372,21 +372,19 @@ int pnp_register_card_driver(struct pnp_card_driver * drv) | |||
| 372 | drv->link.suspend = drv->suspend ? card_suspend : NULL; | 372 | drv->link.suspend = drv->suspend ? card_suspend : NULL; |
| 373 | drv->link.resume = drv->resume ? card_resume : NULL; | 373 | drv->link.resume = drv->resume ? card_resume : NULL; |
| 374 | 374 | ||
| 375 | count = pnp_register_driver(&drv->link); | 375 | error = pnp_register_driver(&drv->link); |
| 376 | if (count < 0) | 376 | if (error < 0) |
| 377 | return count; | 377 | return error; |
| 378 | 378 | ||
| 379 | spin_lock(&pnp_lock); | 379 | spin_lock(&pnp_lock); |
| 380 | list_add_tail(&drv->global_list, &pnp_card_drivers); | 380 | list_add_tail(&drv->global_list, &pnp_card_drivers); |
| 381 | spin_unlock(&pnp_lock); | 381 | spin_unlock(&pnp_lock); |
| 382 | 382 | ||
| 383 | count = 0; | ||
| 384 | |||
| 385 | list_for_each_safe(pos,temp,&pnp_cards){ | 383 | list_for_each_safe(pos,temp,&pnp_cards){ |
| 386 | struct pnp_card *card = list_entry(pos, struct pnp_card, global_list); | 384 | struct pnp_card *card = list_entry(pos, struct pnp_card, global_list); |
| 387 | count += card_probe(card,drv); | 385 | card_probe(card,drv); |
| 388 | } | 386 | } |
| 389 | return count; | 387 | return 0; |
| 390 | } | 388 | } |
| 391 | 389 | ||
| 392 | /** | 390 | /** |
diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 7cafacdd12b0..e54c15383193 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c | |||
| @@ -201,31 +201,14 @@ struct bus_type pnp_bus_type = { | |||
| 201 | .resume = pnp_bus_resume, | 201 | .resume = pnp_bus_resume, |
| 202 | }; | 202 | }; |
| 203 | 203 | ||
| 204 | |||
| 205 | static int count_devices(struct device * dev, void * c) | ||
| 206 | { | ||
| 207 | int * count = c; | ||
| 208 | (*count)++; | ||
| 209 | return 0; | ||
| 210 | } | ||
| 211 | |||
| 212 | int pnp_register_driver(struct pnp_driver *drv) | 204 | int pnp_register_driver(struct pnp_driver *drv) |
| 213 | { | 205 | { |
| 214 | int count; | ||
| 215 | |||
| 216 | pnp_dbg("the driver '%s' has been registered", drv->name); | 206 | pnp_dbg("the driver '%s' has been registered", drv->name); |
| 217 | 207 | ||
| 218 | drv->driver.name = drv->name; | 208 | drv->driver.name = drv->name; |
| 219 | drv->driver.bus = &pnp_bus_type; | 209 | drv->driver.bus = &pnp_bus_type; |
| 220 | 210 | ||
| 221 | count = driver_register(&drv->driver); | 211 | return driver_register(&drv->driver); |
| 222 | |||
| 223 | /* get the number of initial matches */ | ||
| 224 | if (count >= 0){ | ||
| 225 | count = 0; | ||
| 226 | driver_for_each_device(&drv->driver, NULL, &count, count_devices); | ||
| 227 | } | ||
| 228 | return count; | ||
| 229 | } | 212 | } |
| 230 | 213 | ||
| 231 | void pnp_unregister_driver(struct pnp_driver *drv) | 214 | void pnp_unregister_driver(struct pnp_driver *drv) |
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig new file mode 100644 index 000000000000..929dd8090578 --- /dev/null +++ b/drivers/rtc/Kconfig | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | \# | ||
| 2 | # RTC class/drivers configuration | ||
| 3 | # | ||
| 4 | |||
| 5 | menu "Real Time Clock" | ||
| 6 | |||
| 7 | config RTC_LIB | ||
| 8 | tristate | ||
| 9 | |||
| 10 | config RTC_CLASS | ||
| 11 | tristate "RTC class" | ||
| 12 | depends on EXPERIMENTAL | ||
| 13 | default n | ||
| 14 | select RTC_LIB | ||
| 15 | help | ||
| 16 | Generic RTC class support. If you say yes here, you will | ||
| 17 | be allowed to plug one or more RTCs to your system. You will | ||
| 18 | probably want to enable one of more of the interfaces below. | ||
| 19 | |||
| 20 | This driver can also be built as a module. If so, the module | ||
| 21 | will be called rtc-class. | ||
| 22 | |||
| 23 | config RTC_HCTOSYS | ||
| 24 | bool "Set system time from RTC on startup" | ||
| 25 | depends on RTC_CLASS = y | ||
| 26 | default y | ||
| 27 | help | ||
| 28 | If you say yes here, the system time will be set using | ||
| 29 | the value read from the specified RTC device. This is useful | ||
| 30 | in order to avoid unnecessary fschk runs. | ||
| 31 | |||
| 32 | config RTC_HCTOSYS_DEVICE | ||
| 33 | string "The RTC to read the time from" | ||
| 34 | depends on RTC_HCTOSYS = y | ||
| 35 | default "rtc0" | ||
| 36 | help | ||
| 37 | The RTC device that will be used as the source for | ||
| 38 | the system time, usually rtc0. | ||
| 39 | |||
| 40 | comment "RTC interfaces" | ||
| 41 | depends on RTC_CLASS | ||
| 42 | |||
| 43 | config RTC_INTF_SYSFS | ||
| 44 | tristate "sysfs" | ||
| 45 | depends on RTC_CLASS && SYSFS | ||
| 46 | default RTC_CLASS | ||
| 47 | help | ||
| 48 | Say yes here if you want to use your RTC using the sysfs | ||
| 49 | interface, /sys/class/rtc/rtcX . | ||
| 50 | |||
| 51 | This driver can also be built as a module. If so, the module | ||
| 52 | will be called rtc-sysfs. | ||
| 53 | |||
| 54 | config RTC_INTF_PROC | ||
| 55 | tristate "proc" | ||
| 56 | depends on RTC_CLASS && PROC_FS | ||
| 57 | default RTC_CLASS | ||
| 58 | help | ||
| 59 | Say yes here if you want to use your RTC using the proc | ||
| 60 | interface, /proc/driver/rtc . | ||
| 61 | |||
| 62 | This driver can also be built as a module. If so, the module | ||
| 63 | will be called rtc-proc. | ||
| 64 | |||
| 65 | config RTC_INTF_DEV | ||
| 66 | tristate "dev" | ||
| 67 | depends on RTC_CLASS | ||
| 68 | default RTC_CLASS | ||
| 69 | help | ||
| 70 | Say yes here if you want to use your RTC using the dev | ||
| 71 | interface, /dev/rtc . | ||
| 72 | |||
| 73 | This driver can also be built as a module. If so, the module | ||
| 74 | will be called rtc-dev. | ||
| 75 | |||
| 76 | comment "RTC drivers" | ||
| 77 | depends on RTC_CLASS | ||
| 78 | |||
| 79 | config RTC_DRV_X1205 | ||
| 80 | tristate "Xicor/Intersil X1205" | ||
| 81 | depends on RTC_CLASS && I2C | ||
| 82 | help | ||
| 83 | If you say yes here you get support for the | ||
| 84 | Xicor/Intersil X1205 RTC chip. | ||
| 85 | |||
| 86 | This driver can also be built as a module. If so, the module | ||
| 87 | will be called rtc-x1205. | ||
| 88 | |||
| 89 | config RTC_DRV_DS1672 | ||
| 90 | tristate "Dallas/Maxim DS1672" | ||
| 91 | depends on RTC_CLASS && I2C | ||
| 92 | help | ||
| 93 | If you say yes here you get support for the | ||
| 94 | Dallas/Maxim DS1672 timekeeping chip. | ||
| 95 | |||
| 96 | This driver can also be built as a module. If so, the module | ||
| 97 | will be called rtc-ds1672. | ||
| 98 | |||
| 99 | config RTC_DRV_PCF8563 | ||
| 100 | tristate "Philips PCF8563/Epson RTC8564" | ||
| 101 | depends on RTC_CLASS && I2C | ||
| 102 | help | ||
| 103 | If you say yes here you get support for the | ||
| 104 | Philips PCF8563 RTC chip. The Epson RTC8564 | ||
| 105 | should work as well. | ||
| 106 | |||
| 107 | This driver can also be built as a module. If so, the module | ||
| 108 | will be called rtc-pcf8563. | ||
| 109 | |||
| 110 | config RTC_DRV_RS5C372 | ||
| 111 | tristate "Ricoh RS5C372A/B" | ||
| 112 | depends on RTC_CLASS && I2C | ||
| 113 | help | ||
| 114 | If you say yes here you get support for the | ||
| 115 | Ricoh RS5C372A and RS5C372B RTC chips. | ||
| 116 | |||
| 117 | This driver can also be built as a module. If so, the module | ||
| 118 | will be called rtc-rs5c372. | ||
| 119 | |||
| 120 | config RTC_DRV_M48T86 | ||
| 121 | tristate "ST M48T86/Dallas DS12887" | ||
| 122 | depends on RTC_CLASS | ||
| 123 | help | ||
| 124 | If you say Y here you will get support for the | ||
| 125 | ST M48T86 and Dallas DS12887 RTC chips. | ||
| 126 | |||
| 127 | This driver can also be built as a module. If so, the module | ||
| 128 | will be called rtc-m48t86. | ||
| 129 | |||
| 130 | config RTC_DRV_EP93XX | ||
| 131 | tristate "Cirrus Logic EP93XX" | ||
| 132 | depends on RTC_CLASS && ARCH_EP93XX | ||
| 133 | help | ||
| 134 | If you say yes here you get support for the | ||
| 135 | RTC embedded in the Cirrus Logic EP93XX processors. | ||
| 136 | |||
| 137 | This driver can also be built as a module. If so, the module | ||
| 138 | will be called rtc-ep93xx. | ||
| 139 | |||
| 140 | config RTC_DRV_SA1100 | ||
| 141 | tristate "SA11x0/PXA2xx" | ||
| 142 | depends on RTC_CLASS && (ARCH_SA1100 || ARCH_PXA) | ||
| 143 | help | ||
| 144 | If you say Y here you will get access to the real time clock | ||
| 145 | built into your SA11x0 or PXA2xx CPU. | ||
| 146 | |||
| 147 | To compile this driver as a module, choose M here: the | ||
| 148 | module will be called rtc-sa1100. | ||
| 149 | |||
| 150 | config RTC_DRV_TEST | ||
| 151 | tristate "Test driver/device" | ||
| 152 | depends on RTC_CLASS | ||
| 153 | help | ||
| 154 | If you say yes here you get support for the | ||
| 155 | RTC test driver. It's a software RTC which can be | ||
| 156 | used to test the RTC subsystem APIs. It gets | ||
| 157 | the time from the system clock. | ||
| 158 | You want this driver only if you are doing development | ||
| 159 | on the RTC subsystem. Please read the source code | ||
| 160 | for further details. | ||
| 161 | |||
| 162 | This driver can also be built as a module. If so, the module | ||
| 163 | will be called rtc-test. | ||
| 164 | |||
| 165 | endmenu | ||
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile new file mode 100644 index 000000000000..8d4c7fe88d58 --- /dev/null +++ b/drivers/rtc/Makefile | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | # | ||
| 2 | # Makefile for RTC class/drivers. | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_RTC_LIB) += rtc-lib.o | ||
| 6 | obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o | ||
| 7 | obj-$(CONFIG_RTC_CLASS) += rtc-core.o | ||
| 8 | rtc-core-y := class.o interface.o | ||
| 9 | |||
| 10 | obj-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o | ||
| 11 | obj-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o | ||
| 12 | obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o | ||
| 13 | |||
| 14 | obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o | ||
| 15 | obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o | ||
| 16 | obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o | ||
| 17 | obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o | ||
| 18 | obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o | ||
| 19 | obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o | ||
| 20 | obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o | ||
| 21 | obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o | ||
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c new file mode 100644 index 000000000000..8533936d50d8 --- /dev/null +++ b/drivers/rtc/class.c | |||
| @@ -0,0 +1,145 @@ | |||
| 1 | /* | ||
| 2 | * RTC subsystem, base class | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Tower Technologies | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * class skeleton from drivers/hwmon/hwmon.c | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/rtc.h> | ||
| 16 | #include <linux/kdev_t.h> | ||
| 17 | #include <linux/idr.h> | ||
| 18 | |||
| 19 | static DEFINE_IDR(rtc_idr); | ||
| 20 | static DEFINE_MUTEX(idr_lock); | ||
| 21 | struct class *rtc_class; | ||
| 22 | |||
| 23 | static void rtc_device_release(struct class_device *class_dev) | ||
| 24 | { | ||
| 25 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 26 | mutex_lock(&idr_lock); | ||
| 27 | idr_remove(&rtc_idr, rtc->id); | ||
| 28 | mutex_unlock(&idr_lock); | ||
| 29 | kfree(rtc); | ||
| 30 | } | ||
| 31 | |||
| 32 | /** | ||
| 33 | * rtc_device_register - register w/ RTC class | ||
| 34 | * @dev: the device to register | ||
| 35 | * | ||
| 36 | * rtc_device_unregister() must be called when the class device is no | ||
| 37 | * longer needed. | ||
| 38 | * | ||
| 39 | * Returns the pointer to the new struct class device. | ||
| 40 | */ | ||
| 41 | struct rtc_device *rtc_device_register(const char *name, struct device *dev, | ||
| 42 | struct rtc_class_ops *ops, | ||
| 43 | struct module *owner) | ||
| 44 | { | ||
| 45 | struct rtc_device *rtc; | ||
| 46 | int id, err; | ||
| 47 | |||
| 48 | if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { | ||
| 49 | err = -ENOMEM; | ||
| 50 | goto exit; | ||
| 51 | } | ||
| 52 | |||
| 53 | |||
| 54 | mutex_lock(&idr_lock); | ||
| 55 | err = idr_get_new(&rtc_idr, NULL, &id); | ||
| 56 | mutex_unlock(&idr_lock); | ||
| 57 | |||
| 58 | if (err < 0) | ||
| 59 | goto exit; | ||
| 60 | |||
| 61 | id = id & MAX_ID_MASK; | ||
| 62 | |||
| 63 | rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); | ||
| 64 | if (rtc == NULL) { | ||
| 65 | err = -ENOMEM; | ||
| 66 | goto exit_idr; | ||
| 67 | } | ||
| 68 | |||
| 69 | rtc->id = id; | ||
| 70 | rtc->ops = ops; | ||
| 71 | rtc->owner = owner; | ||
| 72 | rtc->class_dev.dev = dev; | ||
| 73 | rtc->class_dev.class = rtc_class; | ||
| 74 | rtc->class_dev.release = rtc_device_release; | ||
| 75 | |||
| 76 | mutex_init(&rtc->ops_lock); | ||
| 77 | spin_lock_init(&rtc->irq_lock); | ||
| 78 | spin_lock_init(&rtc->irq_task_lock); | ||
| 79 | |||
| 80 | strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); | ||
| 81 | snprintf(rtc->class_dev.class_id, BUS_ID_SIZE, "rtc%d", id); | ||
| 82 | |||
| 83 | err = class_device_register(&rtc->class_dev); | ||
| 84 | if (err) | ||
| 85 | goto exit_kfree; | ||
| 86 | |||
| 87 | dev_info(dev, "rtc core: registered %s as %s\n", | ||
| 88 | rtc->name, rtc->class_dev.class_id); | ||
| 89 | |||
| 90 | return rtc; | ||
| 91 | |||
| 92 | exit_kfree: | ||
| 93 | kfree(rtc); | ||
| 94 | |||
| 95 | exit_idr: | ||
| 96 | idr_remove(&rtc_idr, id); | ||
| 97 | |||
| 98 | exit: | ||
| 99 | return ERR_PTR(err); | ||
| 100 | } | ||
| 101 | EXPORT_SYMBOL_GPL(rtc_device_register); | ||
| 102 | |||
| 103 | |||
| 104 | /** | ||
| 105 | * rtc_device_unregister - removes the previously registered RTC class device | ||
| 106 | * | ||
| 107 | * @rtc: the RTC class device to destroy | ||
| 108 | */ | ||
| 109 | void rtc_device_unregister(struct rtc_device *rtc) | ||
| 110 | { | ||
| 111 | mutex_lock(&rtc->ops_lock); | ||
| 112 | rtc->ops = NULL; | ||
| 113 | mutex_unlock(&rtc->ops_lock); | ||
| 114 | class_device_unregister(&rtc->class_dev); | ||
| 115 | } | ||
| 116 | EXPORT_SYMBOL_GPL(rtc_device_unregister); | ||
| 117 | |||
| 118 | int rtc_interface_register(struct class_interface *intf) | ||
| 119 | { | ||
| 120 | intf->class = rtc_class; | ||
| 121 | return class_interface_register(intf); | ||
| 122 | } | ||
| 123 | EXPORT_SYMBOL_GPL(rtc_interface_register); | ||
| 124 | |||
| 125 | static int __init rtc_init(void) | ||
| 126 | { | ||
| 127 | rtc_class = class_create(THIS_MODULE, "rtc"); | ||
| 128 | if (IS_ERR(rtc_class)) { | ||
| 129 | printk(KERN_ERR "%s: couldn't create class\n", __FILE__); | ||
| 130 | return PTR_ERR(rtc_class); | ||
| 131 | } | ||
| 132 | return 0; | ||
| 133 | } | ||
| 134 | |||
| 135 | static void __exit rtc_exit(void) | ||
| 136 | { | ||
| 137 | class_destroy(rtc_class); | ||
| 138 | } | ||
| 139 | |||
| 140 | module_init(rtc_init); | ||
| 141 | module_exit(rtc_exit); | ||
| 142 | |||
| 143 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towerteh.it>"); | ||
| 144 | MODULE_DESCRIPTION("RTC class support"); | ||
| 145 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c new file mode 100644 index 000000000000..d02fe9a0001f --- /dev/null +++ b/drivers/rtc/hctosys.c | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | /* | ||
| 2 | * RTC subsystem, initialize system time on startup | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Tower Technologies | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/rtc.h> | ||
| 13 | |||
| 14 | /* IMPORTANT: the RTC only stores whole seconds. It is arbitrary | ||
| 15 | * whether it stores the most close value or the value with partial | ||
| 16 | * seconds truncated. However, it is important that we use it to store | ||
| 17 | * the truncated value. This is because otherwise it is necessary, | ||
| 18 | * in an rtc sync function, to read both xtime.tv_sec and | ||
| 19 | * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read | ||
| 20 | * of >32bits is not possible. So storing the most close value would | ||
| 21 | * slow down the sync API. So here we have the truncated value and | ||
| 22 | * the best guess is to add 0.5s. | ||
| 23 | */ | ||
| 24 | |||
| 25 | static int __init rtc_hctosys(void) | ||
| 26 | { | ||
| 27 | int err; | ||
| 28 | struct rtc_time tm; | ||
| 29 | struct class_device *class_dev = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); | ||
| 30 | |||
| 31 | if (class_dev == NULL) { | ||
| 32 | printk("%s: unable to open rtc device (%s)\n", | ||
| 33 | __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); | ||
| 34 | return -ENODEV; | ||
| 35 | } | ||
| 36 | |||
| 37 | err = rtc_read_time(class_dev, &tm); | ||
| 38 | if (err == 0) { | ||
| 39 | err = rtc_valid_tm(&tm); | ||
| 40 | if (err == 0) { | ||
| 41 | struct timespec tv; | ||
| 42 | |||
| 43 | tv.tv_nsec = NSEC_PER_SEC >> 1; | ||
| 44 | |||
| 45 | rtc_tm_to_time(&tm, &tv.tv_sec); | ||
| 46 | |||
| 47 | do_settimeofday(&tv); | ||
| 48 | |||
| 49 | dev_info(class_dev->dev, | ||
| 50 | "setting the system clock to " | ||
| 51 | "%d-%02d-%02d %02d:%02d:%02d (%u)\n", | ||
| 52 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, | ||
| 53 | tm.tm_hour, tm.tm_min, tm.tm_sec, | ||
| 54 | (unsigned int) tv.tv_sec); | ||
| 55 | } | ||
| 56 | else | ||
| 57 | dev_err(class_dev->dev, | ||
| 58 | "hctosys: invalid date/time\n"); | ||
| 59 | } | ||
| 60 | else | ||
| 61 | dev_err(class_dev->dev, | ||
| 62 | "hctosys: unable to read the hardware clock\n"); | ||
| 63 | |||
| 64 | rtc_class_close(class_dev); | ||
| 65 | |||
| 66 | return 0; | ||
| 67 | } | ||
| 68 | |||
| 69 | late_initcall(rtc_hctosys); | ||
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c new file mode 100644 index 000000000000..56e490709b87 --- /dev/null +++ b/drivers/rtc/interface.c | |||
| @@ -0,0 +1,277 @@ | |||
| 1 | /* | ||
| 2 | * RTC subsystem, interface functions | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Tower Technologies | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * based on arch/arm/common/rtctime.c | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/rtc.h> | ||
| 15 | |||
| 16 | int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm) | ||
| 17 | { | ||
| 18 | int err; | ||
| 19 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 20 | |||
| 21 | err = mutex_lock_interruptible(&rtc->ops_lock); | ||
| 22 | if (err) | ||
| 23 | return -EBUSY; | ||
| 24 | |||
| 25 | if (!rtc->ops) | ||
| 26 | err = -ENODEV; | ||
| 27 | else if (!rtc->ops->read_time) | ||
| 28 | err = -EINVAL; | ||
| 29 | else { | ||
| 30 | memset(tm, 0, sizeof(struct rtc_time)); | ||
| 31 | err = rtc->ops->read_time(class_dev->dev, tm); | ||
| 32 | } | ||
| 33 | |||
| 34 | mutex_unlock(&rtc->ops_lock); | ||
| 35 | return err; | ||
| 36 | } | ||
| 37 | EXPORT_SYMBOL_GPL(rtc_read_time); | ||
| 38 | |||
| 39 | int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm) | ||
| 40 | { | ||
| 41 | int err; | ||
| 42 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 43 | |||
| 44 | err = rtc_valid_tm(tm); | ||
| 45 | if (err != 0) | ||
| 46 | return err; | ||
| 47 | |||
| 48 | err = mutex_lock_interruptible(&rtc->ops_lock); | ||
| 49 | if (err) | ||
| 50 | return -EBUSY; | ||
| 51 | |||
| 52 | if (!rtc->ops) | ||
| 53 | err = -ENODEV; | ||
| 54 | else if (!rtc->ops->set_time) | ||
| 55 | err = -EINVAL; | ||
| 56 | else | ||
| 57 | err = rtc->ops->set_time(class_dev->dev, tm); | ||
| 58 | |||
| 59 | mutex_unlock(&rtc->ops_lock); | ||
| 60 | return err; | ||
| 61 | } | ||
| 62 | EXPORT_SYMBOL_GPL(rtc_set_time); | ||
| 63 | |||
| 64 | int rtc_set_mmss(struct class_device *class_dev, unsigned long secs) | ||
| 65 | { | ||
| 66 | int err; | ||
| 67 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 68 | |||
| 69 | err = mutex_lock_interruptible(&rtc->ops_lock); | ||
| 70 | if (err) | ||
| 71 | return -EBUSY; | ||
| 72 | |||
| 73 | if (!rtc->ops) | ||
| 74 | err = -ENODEV; | ||
| 75 | else if (rtc->ops->set_mmss) | ||
| 76 | err = rtc->ops->set_mmss(class_dev->dev, secs); | ||
| 77 | else if (rtc->ops->read_time && rtc->ops->set_time) { | ||
| 78 | struct rtc_time new, old; | ||
| 79 | |||
| 80 | err = rtc->ops->read_time(class_dev->dev, &old); | ||
| 81 | if (err == 0) { | ||
| 82 | rtc_time_to_tm(secs, &new); | ||
| 83 | |||
| 84 | /* | ||
| 85 | * avoid writing when we're going to change the day of | ||
| 86 | * the month. We will retry in the next minute. This | ||
| 87 | * basically means that if the RTC must not drift | ||
| 88 | * by more than 1 minute in 11 minutes. | ||
| 89 | */ | ||
| 90 | if (!((old.tm_hour == 23 && old.tm_min == 59) || | ||
| 91 | (new.tm_hour == 23 && new.tm_min == 59))) | ||
| 92 | err = rtc->ops->set_time(class_dev->dev, &new); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | else | ||
| 96 | err = -EINVAL; | ||
| 97 | |||
| 98 | mutex_unlock(&rtc->ops_lock); | ||
| 99 | |||
| 100 | return err; | ||
| 101 | } | ||
| 102 | EXPORT_SYMBOL_GPL(rtc_set_mmss); | ||
| 103 | |||
| 104 | int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm) | ||
| 105 | { | ||
| 106 | int err; | ||
| 107 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 108 | |||
| 109 | err = mutex_lock_interruptible(&rtc->ops_lock); | ||
| 110 | if (err) | ||
| 111 | return -EBUSY; | ||
| 112 | |||
| 113 | if (rtc->ops == NULL) | ||
| 114 | err = -ENODEV; | ||
| 115 | else if (!rtc->ops->read_alarm) | ||
| 116 | err = -EINVAL; | ||
| 117 | else { | ||
| 118 | memset(alarm, 0, sizeof(struct rtc_wkalrm)); | ||
| 119 | err = rtc->ops->read_alarm(class_dev->dev, alarm); | ||
| 120 | } | ||
| 121 | |||
| 122 | mutex_unlock(&rtc->ops_lock); | ||
| 123 | return err; | ||
| 124 | } | ||
| 125 | EXPORT_SYMBOL_GPL(rtc_read_alarm); | ||
| 126 | |||
| 127 | int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm) | ||
| 128 | { | ||
| 129 | int err; | ||
| 130 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 131 | |||
| 132 | err = mutex_lock_interruptible(&rtc->ops_lock); | ||
| 133 | if (err) | ||
| 134 | return -EBUSY; | ||
| 135 | |||
| 136 | if (!rtc->ops) | ||
| 137 | err = -ENODEV; | ||
| 138 | else if (!rtc->ops->set_alarm) | ||
| 139 | err = -EINVAL; | ||
| 140 | else | ||
| 141 | err = rtc->ops->set_alarm(class_dev->dev, alarm); | ||
| 142 | |||
| 143 | mutex_unlock(&rtc->ops_lock); | ||
| 144 | return err; | ||
| 145 | } | ||
| 146 | EXPORT_SYMBOL_GPL(rtc_set_alarm); | ||
| 147 | |||
| 148 | void rtc_update_irq(struct class_device *class_dev, | ||
| 149 | unsigned long num, unsigned long events) | ||
| 150 | { | ||
| 151 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 152 | |||
| 153 | spin_lock(&rtc->irq_lock); | ||
| 154 | rtc->irq_data = (rtc->irq_data + (num << 8)) | events; | ||
| 155 | spin_unlock(&rtc->irq_lock); | ||
| 156 | |||
| 157 | spin_lock(&rtc->irq_task_lock); | ||
| 158 | if (rtc->irq_task) | ||
| 159 | rtc->irq_task->func(rtc->irq_task->private_data); | ||
| 160 | spin_unlock(&rtc->irq_task_lock); | ||
| 161 | |||
| 162 | wake_up_interruptible(&rtc->irq_queue); | ||
| 163 | kill_fasync(&rtc->async_queue, SIGIO, POLL_IN); | ||
| 164 | } | ||
| 165 | EXPORT_SYMBOL_GPL(rtc_update_irq); | ||
| 166 | |||
| 167 | struct class_device *rtc_class_open(char *name) | ||
| 168 | { | ||
| 169 | struct class_device *class_dev = NULL, | ||
| 170 | *class_dev_tmp; | ||
| 171 | |||
| 172 | down(&rtc_class->sem); | ||
| 173 | list_for_each_entry(class_dev_tmp, &rtc_class->children, node) { | ||
| 174 | if (strncmp(class_dev_tmp->class_id, name, BUS_ID_SIZE) == 0) { | ||
| 175 | class_dev = class_dev_tmp; | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | if (class_dev) { | ||
| 181 | if (!try_module_get(to_rtc_device(class_dev)->owner)) | ||
| 182 | class_dev = NULL; | ||
| 183 | } | ||
| 184 | up(&rtc_class->sem); | ||
| 185 | |||
| 186 | return class_dev; | ||
| 187 | } | ||
| 188 | EXPORT_SYMBOL_GPL(rtc_class_open); | ||
| 189 | |||
| 190 | void rtc_class_close(struct class_device *class_dev) | ||
| 191 | { | ||
| 192 | module_put(to_rtc_device(class_dev)->owner); | ||
| 193 | } | ||
| 194 | EXPORT_SYMBOL_GPL(rtc_class_close); | ||
| 195 | |||
| 196 | int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task) | ||
| 197 | { | ||
| 198 | int retval = -EBUSY; | ||
| 199 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 200 | |||
| 201 | if (task == NULL || task->func == NULL) | ||
| 202 | return -EINVAL; | ||
| 203 | |||
| 204 | spin_lock(&rtc->irq_task_lock); | ||
| 205 | if (rtc->irq_task == NULL) { | ||
| 206 | rtc->irq_task = task; | ||
| 207 | retval = 0; | ||
| 208 | } | ||
| 209 | spin_unlock(&rtc->irq_task_lock); | ||
| 210 | |||
| 211 | return retval; | ||
| 212 | } | ||
| 213 | EXPORT_SYMBOL_GPL(rtc_irq_register); | ||
| 214 | |||
| 215 | void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task) | ||
| 216 | { | ||
| 217 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 218 | |||
| 219 | spin_lock(&rtc->irq_task_lock); | ||
| 220 | if (rtc->irq_task == task) | ||
| 221 | rtc->irq_task = NULL; | ||
| 222 | spin_unlock(&rtc->irq_task_lock); | ||
| 223 | } | ||
| 224 | EXPORT_SYMBOL_GPL(rtc_irq_unregister); | ||
| 225 | |||
| 226 | int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int enabled) | ||
| 227 | { | ||
| 228 | int err = 0; | ||
| 229 | unsigned long flags; | ||
| 230 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 231 | |||
| 232 | spin_lock_irqsave(&rtc->irq_task_lock, flags); | ||
| 233 | if (rtc->irq_task != task) | ||
| 234 | err = -ENXIO; | ||
| 235 | spin_unlock_irqrestore(&rtc->irq_task_lock, flags); | ||
| 236 | |||
| 237 | if (err == 0) | ||
| 238 | err = rtc->ops->irq_set_state(class_dev->dev, enabled); | ||
| 239 | |||
| 240 | return err; | ||
| 241 | } | ||
| 242 | EXPORT_SYMBOL_GPL(rtc_irq_set_state); | ||
| 243 | |||
| 244 | int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq) | ||
| 245 | { | ||
| 246 | int err = 0, tmp = 0; | ||
| 247 | unsigned long flags; | ||
| 248 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 249 | |||
| 250 | /* allowed range is 2-8192 */ | ||
| 251 | if (freq < 2 || freq > 8192) | ||
| 252 | return -EINVAL; | ||
| 253 | /* | ||
| 254 | FIXME: this does not belong here, will move where appropriate | ||
| 255 | at a later stage. It cannot hurt right now, trust me :) | ||
| 256 | if ((freq > rtc_max_user_freq) && (!capable(CAP_SYS_RESOURCE))) | ||
| 257 | return -EACCES; | ||
| 258 | */ | ||
| 259 | /* check if freq is a power of 2 */ | ||
| 260 | while (freq > (1 << tmp)) | ||
| 261 | tmp++; | ||
| 262 | |||
| 263 | if (freq != (1 << tmp)) | ||
| 264 | return -EINVAL; | ||
| 265 | |||
| 266 | spin_lock_irqsave(&rtc->irq_task_lock, flags); | ||
| 267 | if (rtc->irq_task != task) | ||
| 268 | err = -ENXIO; | ||
| 269 | spin_unlock_irqrestore(&rtc->irq_task_lock, flags); | ||
| 270 | |||
| 271 | if (err == 0) { | ||
| 272 | err = rtc->ops->irq_set_freq(class_dev->dev, freq); | ||
| 273 | if (err == 0) | ||
| 274 | rtc->irq_freq = freq; | ||
| 275 | } | ||
| 276 | return err; | ||
| 277 | } | ||
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c new file mode 100644 index 000000000000..b1e3e6179e56 --- /dev/null +++ b/drivers/rtc/rtc-dev.c | |||
| @@ -0,0 +1,382 @@ | |||
| 1 | /* | ||
| 2 | * RTC subsystem, dev interface | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Tower Technologies | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * based on arch/arm/common/rtctime.c | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/rtc.h> | ||
| 16 | |||
| 17 | static struct class *rtc_dev_class; | ||
| 18 | static dev_t rtc_devt; | ||
| 19 | |||
| 20 | #define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ | ||
| 21 | |||
| 22 | static int rtc_dev_open(struct inode *inode, struct file *file) | ||
| 23 | { | ||
| 24 | int err; | ||
| 25 | struct rtc_device *rtc = container_of(inode->i_cdev, | ||
| 26 | struct rtc_device, char_dev); | ||
| 27 | struct rtc_class_ops *ops = rtc->ops; | ||
| 28 | |||
| 29 | /* We keep the lock as long as the device is in use | ||
| 30 | * and return immediately if busy | ||
| 31 | */ | ||
| 32 | if (!(mutex_trylock(&rtc->char_lock))) | ||
| 33 | return -EBUSY; | ||
| 34 | |||
| 35 | file->private_data = &rtc->class_dev; | ||
| 36 | |||
| 37 | err = ops->open ? ops->open(rtc->class_dev.dev) : 0; | ||
| 38 | if (err == 0) { | ||
| 39 | spin_lock_irq(&rtc->irq_lock); | ||
| 40 | rtc->irq_data = 0; | ||
| 41 | spin_unlock_irq(&rtc->irq_lock); | ||
| 42 | |||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 46 | /* something has gone wrong, release the lock */ | ||
| 47 | mutex_unlock(&rtc->char_lock); | ||
| 48 | return err; | ||
| 49 | } | ||
| 50 | |||
| 51 | |||
| 52 | static ssize_t | ||
| 53 | rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
| 54 | { | ||
| 55 | struct rtc_device *rtc = to_rtc_device(file->private_data); | ||
| 56 | |||
| 57 | DECLARE_WAITQUEUE(wait, current); | ||
| 58 | unsigned long data; | ||
| 59 | ssize_t ret; | ||
| 60 | |||
| 61 | if (count < sizeof(unsigned long)) | ||
| 62 | return -EINVAL; | ||
| 63 | |||
| 64 | add_wait_queue(&rtc->irq_queue, &wait); | ||
| 65 | do { | ||
| 66 | __set_current_state(TASK_INTERRUPTIBLE); | ||
| 67 | |||
| 68 | spin_lock_irq(&rtc->irq_lock); | ||
| 69 | data = rtc->irq_data; | ||
| 70 | rtc->irq_data = 0; | ||
| 71 | spin_unlock_irq(&rtc->irq_lock); | ||
| 72 | |||
| 73 | if (data != 0) { | ||
| 74 | ret = 0; | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | if (file->f_flags & O_NONBLOCK) { | ||
| 78 | ret = -EAGAIN; | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | if (signal_pending(current)) { | ||
| 82 | ret = -ERESTARTSYS; | ||
| 83 | break; | ||
| 84 | } | ||
| 85 | schedule(); | ||
| 86 | } while (1); | ||
| 87 | set_current_state(TASK_RUNNING); | ||
| 88 | remove_wait_queue(&rtc->irq_queue, &wait); | ||
| 89 | |||
| 90 | if (ret == 0) { | ||
| 91 | /* Check for any data updates */ | ||
| 92 | if (rtc->ops->read_callback) | ||
| 93 | data = rtc->ops->read_callback(rtc->class_dev.dev, data); | ||
| 94 | |||
| 95 | ret = put_user(data, (unsigned long __user *)buf); | ||
| 96 | if (ret == 0) | ||
| 97 | ret = sizeof(unsigned long); | ||
| 98 | } | ||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | |||
| 102 | static unsigned int rtc_dev_poll(struct file *file, poll_table *wait) | ||
| 103 | { | ||
| 104 | struct rtc_device *rtc = to_rtc_device(file->private_data); | ||
| 105 | unsigned long data; | ||
| 106 | |||
| 107 | poll_wait(file, &rtc->irq_queue, wait); | ||
| 108 | |||
| 109 | data = rtc->irq_data; | ||
| 110 | |||
| 111 | return (data != 0) ? (POLLIN | POLLRDNORM) : 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | static int rtc_dev_ioctl(struct inode *inode, struct file *file, | ||
| 115 | unsigned int cmd, unsigned long arg) | ||
| 116 | { | ||
| 117 | int err = 0; | ||
| 118 | struct class_device *class_dev = file->private_data; | ||
| 119 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 120 | struct rtc_class_ops *ops = rtc->ops; | ||
| 121 | struct rtc_time tm; | ||
| 122 | struct rtc_wkalrm alarm; | ||
| 123 | void __user *uarg = (void __user *) arg; | ||
| 124 | |||
| 125 | /* avoid conflicting IRQ users */ | ||
| 126 | if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) { | ||
| 127 | spin_lock(&rtc->irq_task_lock); | ||
| 128 | if (rtc->irq_task) | ||
| 129 | err = -EBUSY; | ||
| 130 | spin_unlock(&rtc->irq_task_lock); | ||
| 131 | |||
| 132 | if (err < 0) | ||
| 133 | return err; | ||
| 134 | } | ||
| 135 | |||
| 136 | /* try the driver's ioctl interface */ | ||
| 137 | if (ops->ioctl) { | ||
| 138 | err = ops->ioctl(class_dev->dev, cmd, arg); | ||
| 139 | if (err != -EINVAL) | ||
| 140 | return err; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* if the driver does not provide the ioctl interface | ||
| 144 | * or if that particular ioctl was not implemented | ||
| 145 | * (-EINVAL), we will try to emulate here. | ||
| 146 | */ | ||
| 147 | |||
| 148 | switch (cmd) { | ||
| 149 | case RTC_ALM_READ: | ||
| 150 | err = rtc_read_alarm(class_dev, &alarm); | ||
| 151 | if (err < 0) | ||
| 152 | return err; | ||
| 153 | |||
| 154 | if (copy_to_user(uarg, &alarm.time, sizeof(tm))) | ||
| 155 | return -EFAULT; | ||
| 156 | break; | ||
| 157 | |||
| 158 | case RTC_ALM_SET: | ||
| 159 | if (copy_from_user(&alarm.time, uarg, sizeof(tm))) | ||
| 160 | return -EFAULT; | ||
| 161 | |||
| 162 | alarm.enabled = 0; | ||
| 163 | alarm.pending = 0; | ||
| 164 | alarm.time.tm_mday = -1; | ||
| 165 | alarm.time.tm_mon = -1; | ||
| 166 | alarm.time.tm_year = -1; | ||
| 167 | alarm.time.tm_wday = -1; | ||
| 168 | alarm.time.tm_yday = -1; | ||
| 169 | alarm.time.tm_isdst = -1; | ||
| 170 | err = rtc_set_alarm(class_dev, &alarm); | ||
| 171 | break; | ||
| 172 | |||
| 173 | case RTC_RD_TIME: | ||
| 174 | err = rtc_read_time(class_dev, &tm); | ||
| 175 | if (err < 0) | ||
| 176 | return err; | ||
| 177 | |||
| 178 | if (copy_to_user(uarg, &tm, sizeof(tm))) | ||
| 179 | return -EFAULT; | ||
| 180 | break; | ||
| 181 | |||
| 182 | case RTC_SET_TIME: | ||
| 183 | if (!capable(CAP_SYS_TIME)) | ||
| 184 | return -EACCES; | ||
| 185 | |||
| 186 | if (copy_from_user(&tm, uarg, sizeof(tm))) | ||
| 187 | return -EFAULT; | ||
| 188 | |||
| 189 | err = rtc_set_time(class_dev, &tm); | ||
| 190 | break; | ||
| 191 | #if 0 | ||
| 192 | case RTC_EPOCH_SET: | ||
| 193 | #ifndef rtc_epoch | ||
| 194 | /* | ||
| 195 | * There were no RTC clocks before 1900. | ||
| 196 | */ | ||
| 197 | if (arg < 1900) { | ||
| 198 | err = -EINVAL; | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | if (!capable(CAP_SYS_TIME)) { | ||
| 202 | err = -EACCES; | ||
| 203 | break; | ||
| 204 | } | ||
| 205 | rtc_epoch = arg; | ||
| 206 | err = 0; | ||
| 207 | #endif | ||
| 208 | break; | ||
| 209 | |||
| 210 | case RTC_EPOCH_READ: | ||
| 211 | err = put_user(rtc_epoch, (unsigned long __user *)uarg); | ||
| 212 | break; | ||
| 213 | #endif | ||
| 214 | case RTC_WKALM_SET: | ||
| 215 | if (copy_from_user(&alarm, uarg, sizeof(alarm))) | ||
| 216 | return -EFAULT; | ||
| 217 | |||
| 218 | err = rtc_set_alarm(class_dev, &alarm); | ||
| 219 | break; | ||
| 220 | |||
| 221 | case RTC_WKALM_RD: | ||
| 222 | err = rtc_read_alarm(class_dev, &alarm); | ||
| 223 | if (err < 0) | ||
| 224 | return err; | ||
| 225 | |||
| 226 | if (copy_to_user(uarg, &alarm, sizeof(alarm))) | ||
| 227 | return -EFAULT; | ||
| 228 | break; | ||
| 229 | |||
| 230 | default: | ||
| 231 | err = -EINVAL; | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | |||
| 235 | return err; | ||
| 236 | } | ||
| 237 | |||
| 238 | static int rtc_dev_release(struct inode *inode, struct file *file) | ||
| 239 | { | ||
| 240 | struct rtc_device *rtc = to_rtc_device(file->private_data); | ||
| 241 | |||
| 242 | if (rtc->ops->release) | ||
| 243 | rtc->ops->release(rtc->class_dev.dev); | ||
| 244 | |||
| 245 | mutex_unlock(&rtc->char_lock); | ||
| 246 | return 0; | ||
| 247 | } | ||
| 248 | |||
| 249 | static int rtc_dev_fasync(int fd, struct file *file, int on) | ||
| 250 | { | ||
| 251 | struct rtc_device *rtc = to_rtc_device(file->private_data); | ||
| 252 | return fasync_helper(fd, file, on, &rtc->async_queue); | ||
| 253 | } | ||
| 254 | |||
| 255 | static struct file_operations rtc_dev_fops = { | ||
| 256 | .owner = THIS_MODULE, | ||
| 257 | .llseek = no_llseek, | ||
| 258 | .read = rtc_dev_read, | ||
| 259 | .poll = rtc_dev_poll, | ||
| 260 | .ioctl = rtc_dev_ioctl, | ||
| 261 | .open = rtc_dev_open, | ||
| 262 | .release = rtc_dev_release, | ||
| 263 | .fasync = rtc_dev_fasync, | ||
| 264 | }; | ||
| 265 | |||
| 266 | /* insertion/removal hooks */ | ||
| 267 | |||
| 268 | static int rtc_dev_add_device(struct class_device *class_dev, | ||
| 269 | struct class_interface *class_intf) | ||
| 270 | { | ||
| 271 | int err = 0; | ||
| 272 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 273 | |||
| 274 | if (rtc->id >= RTC_DEV_MAX) { | ||
| 275 | dev_err(class_dev->dev, "too many RTCs\n"); | ||
| 276 | return -EINVAL; | ||
| 277 | } | ||
| 278 | |||
| 279 | mutex_init(&rtc->char_lock); | ||
| 280 | spin_lock_init(&rtc->irq_lock); | ||
| 281 | init_waitqueue_head(&rtc->irq_queue); | ||
| 282 | |||
| 283 | cdev_init(&rtc->char_dev, &rtc_dev_fops); | ||
| 284 | rtc->char_dev.owner = rtc->owner; | ||
| 285 | |||
| 286 | if (cdev_add(&rtc->char_dev, MKDEV(MAJOR(rtc_devt), rtc->id), 1)) { | ||
| 287 | cdev_del(&rtc->char_dev); | ||
| 288 | dev_err(class_dev->dev, | ||
| 289 | "failed to add char device %d:%d\n", | ||
| 290 | MAJOR(rtc_devt), rtc->id); | ||
| 291 | return -ENODEV; | ||
| 292 | } | ||
| 293 | |||
| 294 | rtc->rtc_dev = class_device_create(rtc_dev_class, NULL, | ||
| 295 | MKDEV(MAJOR(rtc_devt), rtc->id), | ||
| 296 | class_dev->dev, "rtc%d", rtc->id); | ||
| 297 | if (IS_ERR(rtc->rtc_dev)) { | ||
| 298 | dev_err(class_dev->dev, "cannot create rtc_dev device\n"); | ||
| 299 | err = PTR_ERR(rtc->rtc_dev); | ||
| 300 | goto err_cdev_del; | ||
| 301 | } | ||
| 302 | |||
| 303 | dev_info(class_dev->dev, "rtc intf: dev (%d:%d)\n", | ||
| 304 | MAJOR(rtc->rtc_dev->devt), | ||
| 305 | MINOR(rtc->rtc_dev->devt)); | ||
| 306 | |||
| 307 | return 0; | ||
| 308 | |||
| 309 | err_cdev_del: | ||
| 310 | |||
| 311 | cdev_del(&rtc->char_dev); | ||
| 312 | return err; | ||
| 313 | } | ||
| 314 | |||
| 315 | static void rtc_dev_remove_device(struct class_device *class_dev, | ||
| 316 | struct class_interface *class_intf) | ||
| 317 | { | ||
| 318 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 319 | |||
| 320 | if (rtc->rtc_dev) { | ||
| 321 | dev_dbg(class_dev->dev, "removing char %d:%d\n", | ||
| 322 | MAJOR(rtc->rtc_dev->devt), | ||
| 323 | MINOR(rtc->rtc_dev->devt)); | ||
| 324 | |||
| 325 | class_device_unregister(rtc->rtc_dev); | ||
| 326 | cdev_del(&rtc->char_dev); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | /* interface registration */ | ||
| 331 | |||
| 332 | static struct class_interface rtc_dev_interface = { | ||
| 333 | .add = &rtc_dev_add_device, | ||
| 334 | .remove = &rtc_dev_remove_device, | ||
| 335 | }; | ||
| 336 | |||
| 337 | static int __init rtc_dev_init(void) | ||
| 338 | { | ||
| 339 | int err; | ||
| 340 | |||
| 341 | rtc_dev_class = class_create(THIS_MODULE, "rtc-dev"); | ||
| 342 | if (IS_ERR(rtc_dev_class)) | ||
| 343 | return PTR_ERR(rtc_dev_class); | ||
| 344 | |||
| 345 | err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); | ||
| 346 | if (err < 0) { | ||
| 347 | printk(KERN_ERR "%s: failed to allocate char dev region\n", | ||
| 348 | __FILE__); | ||
| 349 | goto err_destroy_class; | ||
| 350 | } | ||
| 351 | |||
| 352 | err = rtc_interface_register(&rtc_dev_interface); | ||
| 353 | if (err < 0) { | ||
| 354 | printk(KERN_ERR "%s: failed to register the interface\n", | ||
| 355 | __FILE__); | ||
| 356 | goto err_unregister_chrdev; | ||
| 357 | } | ||
| 358 | |||
| 359 | return 0; | ||
| 360 | |||
| 361 | err_unregister_chrdev: | ||
| 362 | unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); | ||
| 363 | |||
| 364 | err_destroy_class: | ||
| 365 | class_destroy(rtc_dev_class); | ||
| 366 | |||
| 367 | return err; | ||
| 368 | } | ||
| 369 | |||
| 370 | static void __exit rtc_dev_exit(void) | ||
| 371 | { | ||
| 372 | class_interface_unregister(&rtc_dev_interface); | ||
| 373 | class_destroy(rtc_dev_class); | ||
| 374 | unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); | ||
| 375 | } | ||
| 376 | |||
| 377 | module_init(rtc_dev_init); | ||
| 378 | module_exit(rtc_dev_exit); | ||
| 379 | |||
| 380 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 381 | MODULE_DESCRIPTION("RTC class dev interface"); | ||
| 382 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c new file mode 100644 index 000000000000..358695a416f3 --- /dev/null +++ b/drivers/rtc/rtc-ds1672.c | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | /* | ||
| 2 | * An rtc/i2c driver for the Dallas DS1672 | ||
| 3 | * Copyright 2005 Alessandro Zummo | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/module.h> | ||
| 11 | #include <linux/i2c.h> | ||
| 12 | #include <linux/rtc.h> | ||
| 13 | |||
| 14 | #define DRV_VERSION "0.2" | ||
| 15 | |||
| 16 | /* Addresses to scan: none. This chip cannot be detected. */ | ||
| 17 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 18 | |||
| 19 | /* Insmod parameters */ | ||
| 20 | I2C_CLIENT_INSMOD; | ||
| 21 | |||
| 22 | /* Registers */ | ||
| 23 | |||
| 24 | #define DS1672_REG_CNT_BASE 0 | ||
| 25 | #define DS1672_REG_CONTROL 4 | ||
| 26 | #define DS1672_REG_TRICKLE 5 | ||
| 27 | |||
| 28 | |||
| 29 | /* Prototypes */ | ||
| 30 | static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind); | ||
| 31 | |||
| 32 | /* | ||
| 33 | * In the routines that deal directly with the ds1672 hardware, we use | ||
| 34 | * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch | ||
| 35 | * Epoch is initialized as 2000. Time is set to UTC. | ||
| 36 | */ | ||
| 37 | static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) | ||
| 38 | { | ||
| 39 | unsigned long time; | ||
| 40 | unsigned char addr = DS1672_REG_CNT_BASE; | ||
| 41 | unsigned char buf[4]; | ||
| 42 | |||
| 43 | struct i2c_msg msgs[] = { | ||
| 44 | { client->addr, 0, 1, &addr }, /* setup read ptr */ | ||
| 45 | { client->addr, I2C_M_RD, 4, buf }, /* read date */ | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* read date registers */ | ||
| 49 | if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { | ||
| 50 | dev_err(&client->dev, "%s: read error\n", __FUNCTION__); | ||
| 51 | return -EIO; | ||
| 52 | } | ||
| 53 | |||
| 54 | dev_dbg(&client->dev, | ||
| 55 | "%s: raw read data - counters=%02x,%02x,%02x,%02x\n" | ||
| 56 | __FUNCTION__, | ||
| 57 | buf[0], buf[1], buf[2], buf[3]); | ||
| 58 | |||
| 59 | time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
| 60 | |||
| 61 | rtc_time_to_tm(time, tm); | ||
| 62 | |||
| 63 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " | ||
| 64 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 65 | __FUNCTION__, | ||
| 66 | tm->tm_sec, tm->tm_min, tm->tm_hour, | ||
| 67 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs) | ||
| 73 | { | ||
| 74 | int xfer; | ||
| 75 | unsigned char buf[5]; | ||
| 76 | |||
| 77 | buf[0] = DS1672_REG_CNT_BASE; | ||
| 78 | buf[1] = secs & 0x000000FF; | ||
| 79 | buf[2] = (secs & 0x0000FF00) >> 8; | ||
| 80 | buf[3] = (secs & 0x00FF0000) >> 16; | ||
| 81 | buf[4] = (secs & 0xFF000000) >> 24; | ||
| 82 | |||
| 83 | xfer = i2c_master_send(client, buf, 5); | ||
| 84 | if (xfer != 5) { | ||
| 85 | dev_err(&client->dev, "%s: send: %d\n", __FUNCTION__, xfer); | ||
| 86 | return -EIO; | ||
| 87 | } | ||
| 88 | |||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int ds1672_set_datetime(struct i2c_client *client, struct rtc_time *tm) | ||
| 93 | { | ||
| 94 | unsigned long secs; | ||
| 95 | |||
| 96 | dev_dbg(&client->dev, | ||
| 97 | "%s: secs=%d, mins=%d, hours=%d, ", | ||
| 98 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 99 | __FUNCTION__, | ||
| 100 | tm->tm_sec, tm->tm_min, tm->tm_hour, | ||
| 101 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 102 | |||
| 103 | rtc_tm_to_time(tm, &secs); | ||
| 104 | |||
| 105 | return ds1672_set_mmss(client, secs); | ||
| 106 | } | ||
| 107 | |||
| 108 | static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 109 | { | ||
| 110 | return ds1672_get_datetime(to_i2c_client(dev), tm); | ||
| 111 | } | ||
| 112 | |||
| 113 | static int ds1672_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 114 | { | ||
| 115 | return ds1672_set_datetime(to_i2c_client(dev), tm); | ||
| 116 | } | ||
| 117 | |||
| 118 | static int ds1672_rtc_set_mmss(struct device *dev, unsigned long secs) | ||
| 119 | { | ||
| 120 | return ds1672_set_mmss(to_i2c_client(dev), secs); | ||
| 121 | } | ||
| 122 | |||
| 123 | static struct rtc_class_ops ds1672_rtc_ops = { | ||
| 124 | .read_time = ds1672_rtc_read_time, | ||
| 125 | .set_time = ds1672_rtc_set_time, | ||
| 126 | .set_mmss = ds1672_rtc_set_mmss, | ||
| 127 | }; | ||
| 128 | |||
| 129 | static int ds1672_attach(struct i2c_adapter *adapter) | ||
| 130 | { | ||
| 131 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); | ||
| 132 | return i2c_probe(adapter, &addr_data, ds1672_probe); | ||
| 133 | } | ||
| 134 | |||
| 135 | static int ds1672_detach(struct i2c_client *client) | ||
| 136 | { | ||
| 137 | int err; | ||
| 138 | struct rtc_device *rtc = i2c_get_clientdata(client); | ||
| 139 | |||
| 140 | dev_dbg(&client->dev, "%s\n", __FUNCTION__); | ||
| 141 | |||
| 142 | if (rtc) | ||
| 143 | rtc_device_unregister(rtc); | ||
| 144 | |||
| 145 | if ((err = i2c_detach_client(client))) | ||
| 146 | return err; | ||
| 147 | |||
| 148 | kfree(client); | ||
| 149 | |||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | static struct i2c_driver ds1672_driver = { | ||
| 154 | .driver = { | ||
| 155 | .name = "ds1672", | ||
| 156 | }, | ||
| 157 | .id = I2C_DRIVERID_DS1672, | ||
| 158 | .attach_adapter = &ds1672_attach, | ||
| 159 | .detach_client = &ds1672_detach, | ||
| 160 | }; | ||
| 161 | |||
| 162 | static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind) | ||
| 163 | { | ||
| 164 | int err = 0; | ||
| 165 | struct i2c_client *client; | ||
| 166 | struct rtc_device *rtc; | ||
| 167 | |||
| 168 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); | ||
| 169 | |||
| 170 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { | ||
| 171 | err = -ENODEV; | ||
| 172 | goto exit; | ||
| 173 | } | ||
| 174 | |||
| 175 | if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { | ||
| 176 | err = -ENOMEM; | ||
| 177 | goto exit; | ||
| 178 | } | ||
| 179 | |||
| 180 | /* I2C client */ | ||
| 181 | client->addr = address; | ||
| 182 | client->driver = &ds1672_driver; | ||
| 183 | client->adapter = adapter; | ||
| 184 | |||
| 185 | strlcpy(client->name, ds1672_driver.driver.name, I2C_NAME_SIZE); | ||
| 186 | |||
| 187 | /* Inform the i2c layer */ | ||
| 188 | if ((err = i2c_attach_client(client))) | ||
| 189 | goto exit_kfree; | ||
| 190 | |||
| 191 | dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); | ||
| 192 | |||
| 193 | rtc = rtc_device_register(ds1672_driver.driver.name, &client->dev, | ||
| 194 | &ds1672_rtc_ops, THIS_MODULE); | ||
| 195 | |||
| 196 | if (IS_ERR(rtc)) { | ||
| 197 | err = PTR_ERR(rtc); | ||
| 198 | dev_err(&client->dev, | ||
| 199 | "unable to register the class device\n"); | ||
| 200 | goto exit_detach; | ||
| 201 | } | ||
| 202 | |||
| 203 | i2c_set_clientdata(client, rtc); | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | |||
| 207 | exit_detach: | ||
| 208 | i2c_detach_client(client); | ||
| 209 | |||
| 210 | exit_kfree: | ||
| 211 | kfree(client); | ||
| 212 | |||
| 213 | exit: | ||
| 214 | return err; | ||
| 215 | } | ||
| 216 | |||
| 217 | static int __init ds1672_init(void) | ||
| 218 | { | ||
| 219 | return i2c_add_driver(&ds1672_driver); | ||
| 220 | } | ||
| 221 | |||
| 222 | static void __exit ds1672_exit(void) | ||
| 223 | { | ||
| 224 | i2c_del_driver(&ds1672_driver); | ||
| 225 | } | ||
| 226 | |||
| 227 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 228 | MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver"); | ||
| 229 | MODULE_LICENSE("GPL"); | ||
| 230 | MODULE_VERSION(DRV_VERSION); | ||
| 231 | |||
| 232 | module_init(ds1672_init); | ||
| 233 | module_exit(ds1672_exit); | ||
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c new file mode 100644 index 000000000000..0dd80ea686a9 --- /dev/null +++ b/drivers/rtc/rtc-ep93xx.c | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | /* | ||
| 2 | * A driver for the RTC embedded in the Cirrus Logic EP93XX processors | ||
| 3 | * Copyright (c) 2006 Tower Technologies | ||
| 4 | * | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/rtc.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <asm/hardware.h> | ||
| 16 | |||
| 17 | #define EP93XX_RTC_REG(x) (EP93XX_RTC_BASE + (x)) | ||
| 18 | #define EP93XX_RTC_DATA EP93XX_RTC_REG(0x0000) | ||
| 19 | #define EP93XX_RTC_LOAD EP93XX_RTC_REG(0x000C) | ||
| 20 | #define EP93XX_RTC_SWCOMP EP93XX_RTC_REG(0x0108) | ||
| 21 | |||
| 22 | #define DRV_VERSION "0.2" | ||
| 23 | |||
| 24 | static int ep93xx_get_swcomp(struct device *dev, unsigned short *preload, | ||
| 25 | unsigned short *delete) | ||
| 26 | { | ||
| 27 | unsigned short comp = __raw_readl(EP93XX_RTC_SWCOMP); | ||
| 28 | |||
| 29 | if (preload) | ||
| 30 | *preload = comp & 0xffff; | ||
| 31 | |||
| 32 | if (delete) | ||
| 33 | *delete = (comp >> 16) & 0x1f; | ||
| 34 | |||
| 35 | return 0; | ||
| 36 | } | ||
| 37 | |||
| 38 | static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 39 | { | ||
| 40 | unsigned long time = __raw_readl(EP93XX_RTC_DATA); | ||
| 41 | |||
| 42 | rtc_time_to_tm(time, tm); | ||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 46 | static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs) | ||
| 47 | { | ||
| 48 | __raw_writel(secs + 1, EP93XX_RTC_LOAD); | ||
| 49 | return 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | static int ep93xx_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 53 | { | ||
| 54 | int err; | ||
| 55 | unsigned long secs; | ||
| 56 | |||
| 57 | err = rtc_tm_to_time(tm, &secs); | ||
| 58 | if (err != 0) | ||
| 59 | return err; | ||
| 60 | |||
| 61 | return ep93xx_rtc_set_mmss(dev, secs); | ||
| 62 | } | ||
| 63 | |||
| 64 | static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq) | ||
| 65 | { | ||
| 66 | unsigned short preload, delete; | ||
| 67 | |||
| 68 | ep93xx_get_swcomp(dev, &preload, &delete); | ||
| 69 | |||
| 70 | seq_printf(seq, "24hr\t\t: yes\n"); | ||
| 71 | seq_printf(seq, "preload\t\t: %d\n", preload); | ||
| 72 | seq_printf(seq, "delete\t\t: %d\n", delete); | ||
| 73 | |||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | static struct rtc_class_ops ep93xx_rtc_ops = { | ||
| 78 | .read_time = ep93xx_rtc_read_time, | ||
| 79 | .set_time = ep93xx_rtc_set_time, | ||
| 80 | .set_mmss = ep93xx_rtc_set_mmss, | ||
| 81 | .proc = ep93xx_rtc_proc, | ||
| 82 | }; | ||
| 83 | |||
| 84 | static ssize_t ep93xx_sysfs_show_comp_preload(struct device *dev, | ||
| 85 | struct device_attribute *attr, char *buf) | ||
| 86 | { | ||
| 87 | unsigned short preload; | ||
| 88 | |||
| 89 | ep93xx_get_swcomp(dev, &preload, NULL); | ||
| 90 | |||
| 91 | return sprintf(buf, "%d\n", preload); | ||
| 92 | } | ||
| 93 | static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_sysfs_show_comp_preload, NULL); | ||
| 94 | |||
| 95 | static ssize_t ep93xx_sysfs_show_comp_delete(struct device *dev, | ||
| 96 | struct device_attribute *attr, char *buf) | ||
| 97 | { | ||
| 98 | unsigned short delete; | ||
| 99 | |||
| 100 | ep93xx_get_swcomp(dev, NULL, &delete); | ||
| 101 | |||
| 102 | return sprintf(buf, "%d\n", delete); | ||
| 103 | } | ||
| 104 | static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_sysfs_show_comp_delete, NULL); | ||
| 105 | |||
| 106 | |||
| 107 | static int __devinit ep93xx_rtc_probe(struct platform_device *dev) | ||
| 108 | { | ||
| 109 | struct rtc_device *rtc = rtc_device_register("ep93xx", | ||
| 110 | &dev->dev, &ep93xx_rtc_ops, THIS_MODULE); | ||
| 111 | |||
| 112 | if (IS_ERR(rtc)) { | ||
| 113 | dev_err(&dev->dev, "unable to register\n"); | ||
| 114 | return PTR_ERR(rtc); | ||
| 115 | } | ||
| 116 | |||
| 117 | platform_set_drvdata(dev, rtc); | ||
| 118 | |||
| 119 | device_create_file(&dev->dev, &dev_attr_comp_preload); | ||
| 120 | device_create_file(&dev->dev, &dev_attr_comp_delete); | ||
| 121 | |||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | |||
| 125 | static int __devexit ep93xx_rtc_remove(struct platform_device *dev) | ||
| 126 | { | ||
| 127 | struct rtc_device *rtc = platform_get_drvdata(dev); | ||
| 128 | |||
| 129 | if (rtc) | ||
| 130 | rtc_device_unregister(rtc); | ||
| 131 | |||
| 132 | platform_set_drvdata(dev, NULL); | ||
| 133 | |||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | static struct platform_driver ep93xx_rtc_platform_driver = { | ||
| 138 | .driver = { | ||
| 139 | .name = "ep93xx-rtc", | ||
| 140 | .owner = THIS_MODULE, | ||
| 141 | }, | ||
| 142 | .probe = ep93xx_rtc_probe, | ||
| 143 | .remove = __devexit_p(ep93xx_rtc_remove), | ||
| 144 | }; | ||
| 145 | |||
| 146 | static int __init ep93xx_rtc_init(void) | ||
| 147 | { | ||
| 148 | return platform_driver_register(&ep93xx_rtc_platform_driver); | ||
| 149 | } | ||
| 150 | |||
| 151 | static void __exit ep93xx_rtc_exit(void) | ||
| 152 | { | ||
| 153 | platform_driver_unregister(&ep93xx_rtc_platform_driver); | ||
| 154 | } | ||
| 155 | |||
| 156 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 157 | MODULE_DESCRIPTION("EP93XX RTC driver"); | ||
| 158 | MODULE_LICENSE("GPL"); | ||
| 159 | MODULE_VERSION(DRV_VERSION); | ||
| 160 | |||
| 161 | module_init(ep93xx_rtc_init); | ||
| 162 | module_exit(ep93xx_rtc_exit); | ||
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c new file mode 100644 index 000000000000..cfedc1d28ee1 --- /dev/null +++ b/drivers/rtc/rtc-lib.c | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | /* | ||
| 2 | * rtc and date/time utility functions | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-06 Tower Technologies | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * based on arch/arm/common/rtctime.c and other bits | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/rtc.h> | ||
| 16 | |||
| 17 | static const unsigned char rtc_days_in_month[] = { | ||
| 18 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
| 19 | }; | ||
| 20 | |||
| 21 | #define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) | ||
| 22 | #define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) | ||
| 23 | |||
| 24 | int rtc_month_days(unsigned int month, unsigned int year) | ||
| 25 | { | ||
| 26 | return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1); | ||
| 27 | } | ||
| 28 | EXPORT_SYMBOL(rtc_month_days); | ||
| 29 | |||
| 30 | /* | ||
| 31 | * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. | ||
| 32 | */ | ||
| 33 | void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) | ||
| 34 | { | ||
| 35 | register int days, month, year; | ||
| 36 | |||
| 37 | days = time / 86400; | ||
| 38 | time -= days * 86400; | ||
| 39 | |||
| 40 | /* day of the week, 1970-01-01 was a Thursday */ | ||
| 41 | tm->tm_wday = (days + 4) % 7; | ||
| 42 | |||
| 43 | year = 1970 + days / 365; | ||
| 44 | days -= (year - 1970) * 365 | ||
| 45 | + LEAPS_THRU_END_OF(year - 1) | ||
| 46 | - LEAPS_THRU_END_OF(1970 - 1); | ||
| 47 | if (days < 0) { | ||
| 48 | year -= 1; | ||
| 49 | days += 365 + LEAP_YEAR(year); | ||
| 50 | } | ||
| 51 | tm->tm_year = year - 1900; | ||
| 52 | tm->tm_yday = days + 1; | ||
| 53 | |||
| 54 | for (month = 0; month < 11; month++) { | ||
| 55 | int newdays; | ||
| 56 | |||
| 57 | newdays = days - rtc_month_days(month, year); | ||
| 58 | if (newdays < 0) | ||
| 59 | break; | ||
| 60 | days = newdays; | ||
| 61 | } | ||
| 62 | tm->tm_mon = month; | ||
| 63 | tm->tm_mday = days + 1; | ||
| 64 | |||
| 65 | tm->tm_hour = time / 3600; | ||
| 66 | time -= tm->tm_hour * 3600; | ||
| 67 | tm->tm_min = time / 60; | ||
| 68 | tm->tm_sec = time - tm->tm_min * 60; | ||
| 69 | } | ||
| 70 | EXPORT_SYMBOL(rtc_time_to_tm); | ||
| 71 | |||
| 72 | /* | ||
| 73 | * Does the rtc_time represent a valid date/time? | ||
| 74 | */ | ||
| 75 | int rtc_valid_tm(struct rtc_time *tm) | ||
| 76 | { | ||
| 77 | if (tm->tm_year < 70 | ||
| 78 | || tm->tm_mon >= 12 | ||
| 79 | || tm->tm_mday < 1 | ||
| 80 | || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) | ||
| 81 | || tm->tm_hour >= 24 | ||
| 82 | || tm->tm_min >= 60 | ||
| 83 | || tm->tm_sec >= 60) | ||
| 84 | return -EINVAL; | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | EXPORT_SYMBOL(rtc_valid_tm); | ||
| 89 | |||
| 90 | /* | ||
| 91 | * Convert Gregorian date to seconds since 01-01-1970 00:00:00. | ||
| 92 | */ | ||
| 93 | int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) | ||
| 94 | { | ||
| 95 | *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, | ||
| 96 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | EXPORT_SYMBOL(rtc_tm_to_time); | ||
| 100 | |||
| 101 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c new file mode 100644 index 000000000000..db445c872b1b --- /dev/null +++ b/drivers/rtc/rtc-m48t86.c | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | /* | ||
| 2 | * ST M48T86 / Dallas DS12887 RTC driver | ||
| 3 | * Copyright (c) 2006 Tower Technologies | ||
| 4 | * | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This drivers only supports the clock running in BCD and 24H mode. | ||
| 12 | * If it will be ever adapted to binary and 12H mode, care must be taken | ||
| 13 | * to not introduce bugs. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/rtc.h> | ||
| 18 | #include <linux/platform_device.h> | ||
| 19 | #include <linux/m48t86.h> | ||
| 20 | #include <linux/bcd.h> | ||
| 21 | |||
| 22 | #define M48T86_REG_SEC 0x00 | ||
| 23 | #define M48T86_REG_SECALRM 0x01 | ||
| 24 | #define M48T86_REG_MIN 0x02 | ||
| 25 | #define M48T86_REG_MINALRM 0x03 | ||
| 26 | #define M48T86_REG_HOUR 0x04 | ||
| 27 | #define M48T86_REG_HOURALRM 0x05 | ||
| 28 | #define M48T86_REG_DOW 0x06 /* 1 = sunday */ | ||
| 29 | #define M48T86_REG_DOM 0x07 | ||
| 30 | #define M48T86_REG_MONTH 0x08 /* 1 - 12 */ | ||
| 31 | #define M48T86_REG_YEAR 0x09 /* 0 - 99 */ | ||
| 32 | #define M48T86_REG_A 0x0A | ||
| 33 | #define M48T86_REG_B 0x0B | ||
| 34 | #define M48T86_REG_C 0x0C | ||
| 35 | #define M48T86_REG_D 0x0D | ||
| 36 | |||
| 37 | #define M48T86_REG_B_H24 (1 << 1) | ||
| 38 | #define M48T86_REG_B_DM (1 << 2) | ||
| 39 | #define M48T86_REG_B_SET (1 << 7) | ||
| 40 | #define M48T86_REG_D_VRT (1 << 7) | ||
| 41 | |||
| 42 | #define DRV_VERSION "0.1" | ||
| 43 | |||
| 44 | |||
| 45 | static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 46 | { | ||
| 47 | unsigned char reg; | ||
| 48 | struct platform_device *pdev = to_platform_device(dev); | ||
| 49 | struct m48t86_ops *ops = pdev->dev.platform_data; | ||
| 50 | |||
| 51 | reg = ops->readb(M48T86_REG_B); | ||
| 52 | |||
| 53 | if (reg & M48T86_REG_B_DM) { | ||
| 54 | /* data (binary) mode */ | ||
| 55 | tm->tm_sec = ops->readb(M48T86_REG_SEC); | ||
| 56 | tm->tm_min = ops->readb(M48T86_REG_MIN); | ||
| 57 | tm->tm_hour = ops->readb(M48T86_REG_HOUR) & 0x3F; | ||
| 58 | tm->tm_mday = ops->readb(M48T86_REG_DOM); | ||
| 59 | /* tm_mon is 0-11 */ | ||
| 60 | tm->tm_mon = ops->readb(M48T86_REG_MONTH) - 1; | ||
| 61 | tm->tm_year = ops->readb(M48T86_REG_YEAR) + 100; | ||
| 62 | tm->tm_wday = ops->readb(M48T86_REG_DOW); | ||
| 63 | } else { | ||
| 64 | /* bcd mode */ | ||
| 65 | tm->tm_sec = BCD2BIN(ops->readb(M48T86_REG_SEC)); | ||
| 66 | tm->tm_min = BCD2BIN(ops->readb(M48T86_REG_MIN)); | ||
| 67 | tm->tm_hour = BCD2BIN(ops->readb(M48T86_REG_HOUR) & 0x3F); | ||
| 68 | tm->tm_mday = BCD2BIN(ops->readb(M48T86_REG_DOM)); | ||
| 69 | /* tm_mon is 0-11 */ | ||
| 70 | tm->tm_mon = BCD2BIN(ops->readb(M48T86_REG_MONTH)) - 1; | ||
| 71 | tm->tm_year = BCD2BIN(ops->readb(M48T86_REG_YEAR)) + 100; | ||
| 72 | tm->tm_wday = BCD2BIN(ops->readb(M48T86_REG_DOW)); | ||
| 73 | } | ||
| 74 | |||
| 75 | /* correct the hour if the clock is in 12h mode */ | ||
| 76 | if (!(reg & M48T86_REG_B_H24)) | ||
| 77 | if (ops->readb(M48T86_REG_HOUR) & 0x80) | ||
| 78 | tm->tm_hour += 12; | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 84 | { | ||
| 85 | unsigned char reg; | ||
| 86 | struct platform_device *pdev = to_platform_device(dev); | ||
| 87 | struct m48t86_ops *ops = pdev->dev.platform_data; | ||
| 88 | |||
| 89 | reg = ops->readb(M48T86_REG_B); | ||
| 90 | |||
| 91 | /* update flag and 24h mode */ | ||
| 92 | reg |= M48T86_REG_B_SET | M48T86_REG_B_H24; | ||
| 93 | ops->writeb(reg, M48T86_REG_B); | ||
| 94 | |||
| 95 | if (reg & M48T86_REG_B_DM) { | ||
| 96 | /* data (binary) mode */ | ||
| 97 | ops->writeb(tm->tm_sec, M48T86_REG_SEC); | ||
| 98 | ops->writeb(tm->tm_min, M48T86_REG_MIN); | ||
| 99 | ops->writeb(tm->tm_hour, M48T86_REG_HOUR); | ||
| 100 | ops->writeb(tm->tm_mday, M48T86_REG_DOM); | ||
| 101 | ops->writeb(tm->tm_mon + 1, M48T86_REG_MONTH); | ||
| 102 | ops->writeb(tm->tm_year % 100, M48T86_REG_YEAR); | ||
| 103 | ops->writeb(tm->tm_wday, M48T86_REG_DOW); | ||
| 104 | } else { | ||
| 105 | /* bcd mode */ | ||
| 106 | ops->writeb(BIN2BCD(tm->tm_sec), M48T86_REG_SEC); | ||
| 107 | ops->writeb(BIN2BCD(tm->tm_min), M48T86_REG_MIN); | ||
| 108 | ops->writeb(BIN2BCD(tm->tm_hour), M48T86_REG_HOUR); | ||
| 109 | ops->writeb(BIN2BCD(tm->tm_mday), M48T86_REG_DOM); | ||
| 110 | ops->writeb(BIN2BCD(tm->tm_mon + 1), M48T86_REG_MONTH); | ||
| 111 | ops->writeb(BIN2BCD(tm->tm_year % 100), M48T86_REG_YEAR); | ||
| 112 | ops->writeb(BIN2BCD(tm->tm_wday), M48T86_REG_DOW); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* update ended */ | ||
| 116 | reg &= ~M48T86_REG_B_SET; | ||
| 117 | ops->writeb(reg, M48T86_REG_B); | ||
| 118 | |||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) | ||
| 123 | { | ||
| 124 | unsigned char reg; | ||
| 125 | struct platform_device *pdev = to_platform_device(dev); | ||
| 126 | struct m48t86_ops *ops = pdev->dev.platform_data; | ||
| 127 | |||
| 128 | reg = ops->readb(M48T86_REG_B); | ||
| 129 | |||
| 130 | seq_printf(seq, "24hr\t\t: %s\n", | ||
| 131 | (reg & M48T86_REG_B_H24) ? "yes" : "no"); | ||
| 132 | |||
| 133 | seq_printf(seq, "mode\t\t: %s\n", | ||
| 134 | (reg & M48T86_REG_B_DM) ? "binary" : "bcd"); | ||
| 135 | |||
| 136 | reg = ops->readb(M48T86_REG_D); | ||
| 137 | |||
| 138 | seq_printf(seq, "battery\t\t: %s\n", | ||
| 139 | (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | static struct rtc_class_ops m48t86_rtc_ops = { | ||
| 145 | .read_time = m48t86_rtc_read_time, | ||
| 146 | .set_time = m48t86_rtc_set_time, | ||
| 147 | .proc = m48t86_rtc_proc, | ||
| 148 | }; | ||
| 149 | |||
| 150 | static int __devinit m48t86_rtc_probe(struct platform_device *dev) | ||
| 151 | { | ||
| 152 | unsigned char reg; | ||
| 153 | struct m48t86_ops *ops = dev->dev.platform_data; | ||
| 154 | struct rtc_device *rtc = rtc_device_register("m48t86", | ||
| 155 | &dev->dev, &m48t86_rtc_ops, THIS_MODULE); | ||
| 156 | |||
| 157 | if (IS_ERR(rtc)) { | ||
| 158 | dev_err(&dev->dev, "unable to register\n"); | ||
| 159 | return PTR_ERR(rtc); | ||
| 160 | } | ||
| 161 | |||
| 162 | platform_set_drvdata(dev, rtc); | ||
| 163 | |||
| 164 | /* read battery status */ | ||
| 165 | reg = ops->readb(M48T86_REG_D); | ||
| 166 | dev_info(&dev->dev, "battery %s\n", | ||
| 167 | (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); | ||
| 168 | |||
| 169 | return 0; | ||
| 170 | } | ||
| 171 | |||
| 172 | static int __devexit m48t86_rtc_remove(struct platform_device *dev) | ||
| 173 | { | ||
| 174 | struct rtc_device *rtc = platform_get_drvdata(dev); | ||
| 175 | |||
| 176 | if (rtc) | ||
| 177 | rtc_device_unregister(rtc); | ||
| 178 | |||
| 179 | platform_set_drvdata(dev, NULL); | ||
| 180 | |||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | static struct platform_driver m48t86_rtc_platform_driver = { | ||
| 185 | .driver = { | ||
| 186 | .name = "rtc-m48t86", | ||
| 187 | .owner = THIS_MODULE, | ||
| 188 | }, | ||
| 189 | .probe = m48t86_rtc_probe, | ||
| 190 | .remove = __devexit_p(m48t86_rtc_remove), | ||
| 191 | }; | ||
| 192 | |||
| 193 | static int __init m48t86_rtc_init(void) | ||
| 194 | { | ||
| 195 | return platform_driver_register(&m48t86_rtc_platform_driver); | ||
| 196 | } | ||
| 197 | |||
| 198 | static void __exit m48t86_rtc_exit(void) | ||
| 199 | { | ||
| 200 | platform_driver_unregister(&m48t86_rtc_platform_driver); | ||
| 201 | } | ||
| 202 | |||
| 203 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 204 | MODULE_DESCRIPTION("M48T86 RTC driver"); | ||
| 205 | MODULE_LICENSE("GPL"); | ||
| 206 | MODULE_VERSION(DRV_VERSION); | ||
| 207 | |||
| 208 | module_init(m48t86_rtc_init); | ||
| 209 | module_exit(m48t86_rtc_exit); | ||
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c new file mode 100644 index 000000000000..d857d45bdbe8 --- /dev/null +++ b/drivers/rtc/rtc-pcf8563.c | |||
| @@ -0,0 +1,353 @@ | |||
| 1 | /* | ||
| 2 | * An I2C driver for the Philips PCF8563 RTC | ||
| 3 | * Copyright 2005-06 Tower Technologies | ||
| 4 | * | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * Maintainers: http://www.nslu2-linux.org/ | ||
| 7 | * | ||
| 8 | * based on the other drivers in this same directory. | ||
| 9 | * | ||
| 10 | * http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf | ||
| 11 | * | ||
| 12 | * This program is free software; you can redistribute it and/or modify | ||
| 13 | * it under the terms of the GNU General Public License version 2 as | ||
| 14 | * published by the Free Software Foundation. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/i2c.h> | ||
| 18 | #include <linux/bcd.h> | ||
| 19 | #include <linux/rtc.h> | ||
| 20 | |||
| 21 | #define DRV_VERSION "0.4.2" | ||
| 22 | |||
| 23 | /* Addresses to scan: none | ||
| 24 | * This chip cannot be reliably autodetected. An empty eeprom | ||
| 25 | * located at 0x51 will pass the validation routine due to | ||
| 26 | * the way the registers are implemented. | ||
| 27 | */ | ||
| 28 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | ||
| 29 | |||
| 30 | /* Module parameters */ | ||
| 31 | I2C_CLIENT_INSMOD; | ||
| 32 | |||
| 33 | #define PCF8563_REG_ST1 0x00 /* status */ | ||
| 34 | #define PCF8563_REG_ST2 0x01 | ||
| 35 | |||
| 36 | #define PCF8563_REG_SC 0x02 /* datetime */ | ||
| 37 | #define PCF8563_REG_MN 0x03 | ||
| 38 | #define PCF8563_REG_HR 0x04 | ||
| 39 | #define PCF8563_REG_DM 0x05 | ||
| 40 | #define PCF8563_REG_DW 0x06 | ||
| 41 | #define PCF8563_REG_MO 0x07 | ||
| 42 | #define PCF8563_REG_YR 0x08 | ||
| 43 | |||
| 44 | #define PCF8563_REG_AMN 0x09 /* alarm */ | ||
| 45 | #define PCF8563_REG_AHR 0x0A | ||
| 46 | #define PCF8563_REG_ADM 0x0B | ||
| 47 | #define PCF8563_REG_ADW 0x0C | ||
| 48 | |||
| 49 | #define PCF8563_REG_CLKO 0x0D /* clock out */ | ||
| 50 | #define PCF8563_REG_TMRC 0x0E /* timer control */ | ||
| 51 | #define PCF8563_REG_TMR 0x0F /* timer */ | ||
| 52 | |||
| 53 | #define PCF8563_SC_LV 0x80 /* low voltage */ | ||
| 54 | #define PCF8563_MO_C 0x80 /* century */ | ||
| 55 | |||
| 56 | static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind); | ||
| 57 | static int pcf8563_detach(struct i2c_client *client); | ||
| 58 | |||
| 59 | /* | ||
| 60 | * In the routines that deal directly with the pcf8563 hardware, we use | ||
| 61 | * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. | ||
| 62 | */ | ||
| 63 | static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm) | ||
| 64 | { | ||
| 65 | unsigned char buf[13] = { PCF8563_REG_ST1 }; | ||
| 66 | |||
| 67 | struct i2c_msg msgs[] = { | ||
| 68 | { client->addr, 0, 1, buf }, /* setup read ptr */ | ||
| 69 | { client->addr, I2C_M_RD, 13, buf }, /* read status + date */ | ||
| 70 | }; | ||
| 71 | |||
| 72 | /* read registers */ | ||
| 73 | if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { | ||
| 74 | dev_err(&client->dev, "%s: read error\n", __FUNCTION__); | ||
| 75 | return -EIO; | ||
| 76 | } | ||
| 77 | |||
| 78 | if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) | ||
| 79 | dev_info(&client->dev, | ||
| 80 | "low voltage detected, date/time is not reliable.\n"); | ||
| 81 | |||
| 82 | dev_dbg(&client->dev, | ||
| 83 | "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, " | ||
| 84 | "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", | ||
| 85 | __FUNCTION__, | ||
| 86 | buf[0], buf[1], buf[2], buf[3], | ||
| 87 | buf[4], buf[5], buf[6], buf[7], | ||
| 88 | buf[8]); | ||
| 89 | |||
| 90 | |||
| 91 | tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F); | ||
| 92 | tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F); | ||
| 93 | tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */ | ||
| 94 | tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F); | ||
| 95 | tm->tm_wday = buf[PCF8563_REG_DW] & 0x07; | ||
| 96 | tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ | ||
| 97 | tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR]) | ||
| 98 | + (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 100 : 0); | ||
| 99 | |||
| 100 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " | ||
| 101 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 102 | __FUNCTION__, | ||
| 103 | tm->tm_sec, tm->tm_min, tm->tm_hour, | ||
| 104 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 105 | |||
| 106 | /* the clock can give out invalid datetime, but we cannot return | ||
| 107 | * -EINVAL otherwise hwclock will refuse to set the time on bootup. | ||
| 108 | */ | ||
| 109 | if (rtc_valid_tm(tm) < 0) | ||
| 110 | dev_err(&client->dev, "retrieved date/time is not valid.\n"); | ||
| 111 | |||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm) | ||
| 116 | { | ||
| 117 | int i, err; | ||
| 118 | unsigned char buf[9]; | ||
| 119 | |||
| 120 | dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " | ||
| 121 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 122 | __FUNCTION__, | ||
| 123 | tm->tm_sec, tm->tm_min, tm->tm_hour, | ||
| 124 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 125 | |||
| 126 | /* hours, minutes and seconds */ | ||
| 127 | buf[PCF8563_REG_SC] = BIN2BCD(tm->tm_sec); | ||
| 128 | buf[PCF8563_REG_MN] = BIN2BCD(tm->tm_min); | ||
| 129 | buf[PCF8563_REG_HR] = BIN2BCD(tm->tm_hour); | ||
| 130 | |||
| 131 | buf[PCF8563_REG_DM] = BIN2BCD(tm->tm_mday); | ||
| 132 | |||
| 133 | /* month, 1 - 12 */ | ||
| 134 | buf[PCF8563_REG_MO] = BIN2BCD(tm->tm_mon + 1); | ||
| 135 | |||
| 136 | /* year and century */ | ||
| 137 | buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100); | ||
| 138 | if (tm->tm_year / 100) | ||
| 139 | buf[PCF8563_REG_MO] |= PCF8563_MO_C; | ||
| 140 | |||
| 141 | buf[PCF8563_REG_DW] = tm->tm_wday & 0x07; | ||
| 142 | |||
| 143 | /* write register's data */ | ||
| 144 | for (i = 0; i < 7; i++) { | ||
| 145 | unsigned char data[2] = { PCF8563_REG_SC + i, | ||
| 146 | buf[PCF8563_REG_SC + i] }; | ||
| 147 | |||
| 148 | err = i2c_master_send(client, data, sizeof(data)); | ||
| 149 | if (err != sizeof(data)) { | ||
| 150 | dev_err(&client->dev, | ||
| 151 | "%s: err=%d addr=%02x, data=%02x\n", | ||
| 152 | __FUNCTION__, err, data[0], data[1]); | ||
| 153 | return -EIO; | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | struct pcf8563_limit | ||
| 161 | { | ||
| 162 | unsigned char reg; | ||
| 163 | unsigned char mask; | ||
| 164 | unsigned char min; | ||
| 165 | unsigned char max; | ||
| 166 | }; | ||
| 167 | |||
| 168 | static int pcf8563_validate_client(struct i2c_client *client) | ||
| 169 | { | ||
| 170 | int i; | ||
| 171 | |||
| 172 | static const struct pcf8563_limit pattern[] = { | ||
| 173 | /* register, mask, min, max */ | ||
| 174 | { PCF8563_REG_SC, 0x7F, 0, 59 }, | ||
| 175 | { PCF8563_REG_MN, 0x7F, 0, 59 }, | ||
| 176 | { PCF8563_REG_HR, 0x3F, 0, 23 }, | ||
| 177 | { PCF8563_REG_DM, 0x3F, 0, 31 }, | ||
| 178 | { PCF8563_REG_MO, 0x1F, 0, 12 }, | ||
| 179 | }; | ||
| 180 | |||
| 181 | /* check limits (only registers with bcd values) */ | ||
| 182 | for (i = 0; i < ARRAY_SIZE(pattern); i++) { | ||
| 183 | int xfer; | ||
| 184 | unsigned char value; | ||
| 185 | unsigned char buf = pattern[i].reg; | ||
| 186 | |||
| 187 | struct i2c_msg msgs[] = { | ||
| 188 | { client->addr, 0, 1, &buf }, | ||
| 189 | { client->addr, I2C_M_RD, 1, &buf }, | ||
| 190 | }; | ||
| 191 | |||
| 192 | xfer = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | ||
| 193 | |||
| 194 | if (xfer != ARRAY_SIZE(msgs)) { | ||
| 195 | dev_err(&client->adapter->dev, | ||
| 196 | "%s: could not read register 0x%02X\n", | ||
| 197 | __FUNCTION__, pattern[i].reg); | ||
| 198 | |||
| 199 | return -EIO; | ||
| 200 | } | ||
| 201 | |||
| 202 | value = BCD2BIN(buf & pattern[i].mask); | ||
| 203 | |||
| 204 | if (value > pattern[i].max || | ||
| 205 | value < pattern[i].min) { | ||
| 206 | dev_dbg(&client->adapter->dev, | ||
| 207 | "%s: pattern=%d, reg=%x, mask=0x%02x, min=%d, " | ||
| 208 | "max=%d, value=%d, raw=0x%02X\n", | ||
| 209 | __FUNCTION__, i, pattern[i].reg, pattern[i].mask, | ||
| 210 | pattern[i].min, pattern[i].max, | ||
| 211 | value, buf); | ||
| 212 | |||
| 213 | return -ENODEV; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 221 | { | ||
| 222 | return pcf8563_get_datetime(to_i2c_client(dev), tm); | ||
| 223 | } | ||
| 224 | |||
| 225 | static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 226 | { | ||
| 227 | return pcf8563_set_datetime(to_i2c_client(dev), tm); | ||
| 228 | } | ||
| 229 | |||
| 230 | static int pcf8563_rtc_proc(struct device *dev, struct seq_file *seq) | ||
| 231 | { | ||
| 232 | seq_printf(seq, "24hr\t\t: yes\n"); | ||
| 233 | return 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | static struct rtc_class_ops pcf8563_rtc_ops = { | ||
| 237 | .proc = pcf8563_rtc_proc, | ||
| 238 | .read_time = pcf8563_rtc_read_time, | ||
| 239 | .set_time = pcf8563_rtc_set_time, | ||
| 240 | }; | ||
| 241 | |||
| 242 | static int pcf8563_attach(struct i2c_adapter *adapter) | ||
| 243 | { | ||
| 244 | return i2c_probe(adapter, &addr_data, pcf8563_probe); | ||
| 245 | } | ||
| 246 | |||
| 247 | static struct i2c_driver pcf8563_driver = { | ||
| 248 | .driver = { | ||
| 249 | .name = "pcf8563", | ||
| 250 | }, | ||
| 251 | .id = I2C_DRIVERID_PCF8563, | ||
| 252 | .attach_adapter = &pcf8563_attach, | ||
| 253 | .detach_client = &pcf8563_detach, | ||
| 254 | }; | ||
| 255 | |||
| 256 | static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind) | ||
| 257 | { | ||
| 258 | struct i2c_client *client; | ||
| 259 | struct rtc_device *rtc; | ||
| 260 | |||
| 261 | int err = 0; | ||
| 262 | |||
| 263 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); | ||
| 264 | |||
| 265 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { | ||
| 266 | err = -ENODEV; | ||
| 267 | goto exit; | ||
| 268 | } | ||
| 269 | |||
| 270 | if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { | ||
| 271 | err = -ENOMEM; | ||
| 272 | goto exit; | ||
| 273 | } | ||
| 274 | |||
| 275 | client->addr = address; | ||
| 276 | client->driver = &pcf8563_driver; | ||
| 277 | client->adapter = adapter; | ||
| 278 | |||
| 279 | strlcpy(client->name, pcf8563_driver.driver.name, I2C_NAME_SIZE); | ||
| 280 | |||
| 281 | /* Verify the chip is really an PCF8563 */ | ||
| 282 | if (kind < 0) { | ||
| 283 | if (pcf8563_validate_client(client) < 0) { | ||
| 284 | err = -ENODEV; | ||
| 285 | goto exit_kfree; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | /* Inform the i2c layer */ | ||
| 290 | if ((err = i2c_attach_client(client))) | ||
| 291 | goto exit_kfree; | ||
| 292 | |||
| 293 | dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); | ||
| 294 | |||
| 295 | rtc = rtc_device_register(pcf8563_driver.driver.name, &client->dev, | ||
| 296 | &pcf8563_rtc_ops, THIS_MODULE); | ||
| 297 | |||
| 298 | if (IS_ERR(rtc)) { | ||
| 299 | err = PTR_ERR(rtc); | ||
| 300 | dev_err(&client->dev, | ||
| 301 | "unable to register the class device\n"); | ||
| 302 | goto exit_detach; | ||
| 303 | } | ||
| 304 | |||
| 305 | i2c_set_clientdata(client, rtc); | ||
| 306 | |||
| 307 | return 0; | ||
| 308 | |||
| 309 | exit_detach: | ||
| 310 | i2c_detach_client(client); | ||
| 311 | |||
| 312 | exit_kfree: | ||
| 313 | kfree(client); | ||
| 314 | |||
| 315 | exit: | ||
| 316 | return err; | ||
| 317 | } | ||
| 318 | |||
| 319 | static int pcf8563_detach(struct i2c_client *client) | ||
| 320 | { | ||
| 321 | int err; | ||
| 322 | struct rtc_device *rtc = i2c_get_clientdata(client); | ||
| 323 | |||
| 324 | dev_dbg(&client->dev, "%s\n", __FUNCTION__); | ||
| 325 | |||
| 326 | if (rtc) | ||
| 327 | rtc_device_unregister(rtc); | ||
| 328 | |||
| 329 | if ((err = i2c_detach_client(client))) | ||
| 330 | return err; | ||
| 331 | |||
| 332 | kfree(client); | ||
| 333 | |||
| 334 | return 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | static int __init pcf8563_init(void) | ||
| 338 | { | ||
| 339 | return i2c_add_driver(&pcf8563_driver); | ||
| 340 | } | ||
| 341 | |||
| 342 | static void __exit pcf8563_exit(void) | ||
| 343 | { | ||
| 344 | i2c_del_driver(&pcf8563_driver); | ||
| 345 | } | ||
| 346 | |||
| 347 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 348 | MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver"); | ||
| 349 | MODULE_LICENSE("GPL"); | ||
| 350 | MODULE_VERSION(DRV_VERSION); | ||
| 351 | |||
| 352 | module_init(pcf8563_init); | ||
| 353 | module_exit(pcf8563_exit); | ||
diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c new file mode 100644 index 000000000000..90b8a97a0919 --- /dev/null +++ b/drivers/rtc/rtc-proc.c | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | /* | ||
| 2 | * RTC subsystem, proc interface | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-06 Tower Technologies | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * based on arch/arm/common/rtctime.c | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/rtc.h> | ||
| 16 | #include <linux/proc_fs.h> | ||
| 17 | #include <linux/seq_file.h> | ||
| 18 | |||
| 19 | static struct class_device *rtc_dev = NULL; | ||
| 20 | static DEFINE_MUTEX(rtc_lock); | ||
| 21 | |||
| 22 | static int rtc_proc_show(struct seq_file *seq, void *offset) | ||
| 23 | { | ||
| 24 | int err; | ||
| 25 | struct class_device *class_dev = seq->private; | ||
| 26 | struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops; | ||
| 27 | struct rtc_wkalrm alrm; | ||
| 28 | struct rtc_time tm; | ||
| 29 | |||
| 30 | err = rtc_read_time(class_dev, &tm); | ||
| 31 | if (err == 0) { | ||
| 32 | seq_printf(seq, | ||
| 33 | "rtc_time\t: %02d:%02d:%02d\n" | ||
| 34 | "rtc_date\t: %04d-%02d-%02d\n", | ||
| 35 | tm.tm_hour, tm.tm_min, tm.tm_sec, | ||
| 36 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); | ||
| 37 | } | ||
| 38 | |||
| 39 | err = rtc_read_alarm(class_dev, &alrm); | ||
| 40 | if (err == 0) { | ||
| 41 | seq_printf(seq, "alrm_time\t: "); | ||
| 42 | if ((unsigned int)alrm.time.tm_hour <= 24) | ||
| 43 | seq_printf(seq, "%02d:", alrm.time.tm_hour); | ||
| 44 | else | ||
| 45 | seq_printf(seq, "**:"); | ||
| 46 | if ((unsigned int)alrm.time.tm_min <= 59) | ||
| 47 | seq_printf(seq, "%02d:", alrm.time.tm_min); | ||
| 48 | else | ||
| 49 | seq_printf(seq, "**:"); | ||
| 50 | if ((unsigned int)alrm.time.tm_sec <= 59) | ||
| 51 | seq_printf(seq, "%02d\n", alrm.time.tm_sec); | ||
| 52 | else | ||
| 53 | seq_printf(seq, "**\n"); | ||
| 54 | |||
| 55 | seq_printf(seq, "alrm_date\t: "); | ||
| 56 | if ((unsigned int)alrm.time.tm_year <= 200) | ||
| 57 | seq_printf(seq, "%04d-", alrm.time.tm_year + 1900); | ||
| 58 | else | ||
| 59 | seq_printf(seq, "****-"); | ||
| 60 | if ((unsigned int)alrm.time.tm_mon <= 11) | ||
| 61 | seq_printf(seq, "%02d-", alrm.time.tm_mon + 1); | ||
| 62 | else | ||
| 63 | seq_printf(seq, "**-"); | ||
| 64 | if ((unsigned int)alrm.time.tm_mday <= 31) | ||
| 65 | seq_printf(seq, "%02d\n", alrm.time.tm_mday); | ||
| 66 | else | ||
| 67 | seq_printf(seq, "**\n"); | ||
| 68 | seq_printf(seq, "alrm_wakeup\t: %s\n", | ||
| 69 | alrm.enabled ? "yes" : "no"); | ||
| 70 | seq_printf(seq, "alrm_pending\t: %s\n", | ||
| 71 | alrm.pending ? "yes" : "no"); | ||
| 72 | } | ||
| 73 | |||
| 74 | if (ops->proc) | ||
| 75 | ops->proc(class_dev->dev, seq); | ||
| 76 | |||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | static int rtc_proc_open(struct inode *inode, struct file *file) | ||
| 81 | { | ||
| 82 | struct class_device *class_dev = PDE(inode)->data; | ||
| 83 | |||
| 84 | if (!try_module_get(THIS_MODULE)) | ||
| 85 | return -ENODEV; | ||
| 86 | |||
| 87 | return single_open(file, rtc_proc_show, class_dev); | ||
| 88 | } | ||
| 89 | |||
| 90 | static int rtc_proc_release(struct inode *inode, struct file *file) | ||
| 91 | { | ||
| 92 | int res = single_release(inode, file); | ||
| 93 | module_put(THIS_MODULE); | ||
| 94 | return res; | ||
| 95 | } | ||
| 96 | |||
| 97 | static struct file_operations rtc_proc_fops = { | ||
| 98 | .open = rtc_proc_open, | ||
| 99 | .read = seq_read, | ||
| 100 | .llseek = seq_lseek, | ||
| 101 | .release = rtc_proc_release, | ||
| 102 | }; | ||
| 103 | |||
| 104 | static int rtc_proc_add_device(struct class_device *class_dev, | ||
| 105 | struct class_interface *class_intf) | ||
| 106 | { | ||
| 107 | mutex_lock(&rtc_lock); | ||
| 108 | if (rtc_dev == NULL) { | ||
| 109 | struct proc_dir_entry *ent; | ||
| 110 | |||
| 111 | rtc_dev = class_dev; | ||
| 112 | |||
| 113 | ent = create_proc_entry("driver/rtc", 0, NULL); | ||
| 114 | if (ent) { | ||
| 115 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
| 116 | |||
| 117 | ent->proc_fops = &rtc_proc_fops; | ||
| 118 | ent->owner = rtc->owner; | ||
| 119 | ent->data = class_dev; | ||
| 120 | |||
| 121 | dev_info(class_dev->dev, "rtc intf: proc\n"); | ||
| 122 | } | ||
| 123 | else | ||
| 124 | rtc_dev = NULL; | ||
| 125 | } | ||
| 126 | mutex_unlock(&rtc_lock); | ||
| 127 | |||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | static void rtc_proc_remove_device(struct class_device *class_dev, | ||
| 132 | struct class_interface *class_intf) | ||
| 133 | { | ||
| 134 | mutex_lock(&rtc_lock); | ||
| 135 | if (rtc_dev == class_dev) { | ||
| 136 | remove_proc_entry("driver/rtc", NULL); | ||
| 137 | rtc_dev = NULL; | ||
| 138 | } | ||
| 139 | mutex_unlock(&rtc_lock); | ||
| 140 | } | ||
| 141 | |||
| 142 | static struct class_interface rtc_proc_interface = { | ||
| 143 | .add = &rtc_proc_add_device, | ||
| 144 | .remove = &rtc_proc_remove_device, | ||
| 145 | }; | ||
| 146 | |||
| 147 | static int __init rtc_proc_init(void) | ||
| 148 | { | ||
| 149 | return rtc_interface_register(&rtc_proc_interface); | ||
| 150 | } | ||
| 151 | |||
| 152 | static void __exit rtc_proc_exit(void) | ||
| 153 | { | ||
| 154 | class_interface_unregister(&rtc_proc_interface); | ||
| 155 | } | ||
| 156 | |||
| 157 | module_init(rtc_proc_init); | ||
| 158 | module_exit(rtc_proc_exit); | ||
| 159 | |||
| 160 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 161 | MODULE_DESCRIPTION("RTC class proc interface"); | ||
| 162 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c new file mode 100644 index 000000000000..396c8681f66c --- /dev/null +++ b/drivers/rtc/rtc-rs5c372.c | |||
| @@ -0,0 +1,294 @@ | |||
| 1 | /* | ||
| 2 | * An I2C driver for the Ricoh RS5C372 RTC | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net> | ||
| 5 | * Copyright (C) 2006 Tower Technologies | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/i2c.h> | ||
| 13 | #include <linux/rtc.h> | ||
| 14 | #include <linux/bcd.h> | ||
| 15 | |||
| 16 | #define DRV_VERSION "0.2" | ||
| 17 | |||
| 18 | /* Addresses to scan */ | ||
| 19 | static unsigned short normal_i2c[] = { /* 0x32,*/ I2C_CLIENT_END }; | ||
| 20 | |||
| 21 | /* Insmod parameters */ | ||
| 22 | I2C_CLIENT_INSMOD; | ||
| 23 | |||
| 24 | #define RS5C372_REG_SECS 0 | ||
| 25 | #define RS5C372_REG_MINS 1 | ||
| 26 | #define RS5C372_REG_HOURS 2 | ||
| 27 | #define RS5C372_REG_WDAY 3 | ||
| 28 | #define RS5C372_REG_DAY 4 | ||
| 29 | #define RS5C372_REG_MONTH 5 | ||
| 30 | #define RS5C372_REG_YEAR 6 | ||
| 31 | #define RS5C372_REG_TRIM 7 | ||
| 32 | |||
| 33 | #define RS5C372_TRIM_XSL 0x80 | ||
| 34 | #define RS5C372_TRIM_MASK 0x7F | ||
| 35 | |||
| 36 | #define RS5C372_REG_BASE 0 | ||
| 37 | |||
| 38 | static int rs5c372_attach(struct i2c_adapter *adapter); | ||
| 39 | static int rs5c372_detach(struct i2c_client *client); | ||
| 40 | static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind); | ||
| 41 | |||
| 42 | static struct i2c_driver rs5c372_driver = { | ||
| 43 | .driver = { | ||
| 44 | .name = "rs5c372", | ||
| 45 | }, | ||
| 46 | .attach_adapter = &rs5c372_attach, | ||
| 47 | .detach_client = &rs5c372_detach, | ||
| 48 | }; | ||
| 49 | |||
| 50 | static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm) | ||
| 51 | { | ||
| 52 | unsigned char buf[7] = { RS5C372_REG_BASE }; | ||
| 53 | |||
| 54 | /* this implements the 1st reading method, according | ||
| 55 | * to the datasheet. buf[0] is initialized with | ||
| 56 | * address ptr and transmission format register. | ||
| 57 | */ | ||
| 58 | struct i2c_msg msgs[] = { | ||
| 59 | { client->addr, 0, 1, buf }, | ||
| 60 | { client->addr, I2C_M_RD, 7, buf }, | ||
| 61 | }; | ||
| 62 | |||
| 63 | if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { | ||
| 64 | dev_err(&client->dev, "%s: read error\n", __FUNCTION__); | ||
| 65 | return -EIO; | ||
| 66 | } | ||
| 67 | |||
| 68 | tm->tm_sec = BCD2BIN(buf[RS5C372_REG_SECS] & 0x7f); | ||
| 69 | tm->tm_min = BCD2BIN(buf[RS5C372_REG_MINS] & 0x7f); | ||
| 70 | tm->tm_hour = BCD2BIN(buf[RS5C372_REG_HOURS] & 0x3f); | ||
| 71 | tm->tm_wday = BCD2BIN(buf[RS5C372_REG_WDAY] & 0x07); | ||
| 72 | tm->tm_mday = BCD2BIN(buf[RS5C372_REG_DAY] & 0x3f); | ||
| 73 | |||
| 74 | /* tm->tm_mon is zero-based */ | ||
| 75 | tm->tm_mon = BCD2BIN(buf[RS5C372_REG_MONTH] & 0x1f) - 1; | ||
| 76 | |||
| 77 | /* year is 1900 + tm->tm_year */ | ||
| 78 | tm->tm_year = BCD2BIN(buf[RS5C372_REG_YEAR]) + 100; | ||
| 79 | |||
| 80 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " | ||
| 81 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 82 | __FUNCTION__, | ||
| 83 | tm->tm_sec, tm->tm_min, tm->tm_hour, | ||
| 84 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) | ||
| 90 | { | ||
| 91 | unsigned char buf[8] = { RS5C372_REG_BASE }; | ||
| 92 | |||
| 93 | dev_dbg(&client->dev, | ||
| 94 | "%s: secs=%d, mins=%d, hours=%d ", | ||
| 95 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 96 | __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, | ||
| 97 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 98 | |||
| 99 | buf[1] = BIN2BCD(tm->tm_sec); | ||
| 100 | buf[2] = BIN2BCD(tm->tm_min); | ||
| 101 | buf[3] = BIN2BCD(tm->tm_hour); | ||
| 102 | buf[4] = BIN2BCD(tm->tm_wday); | ||
| 103 | buf[5] = BIN2BCD(tm->tm_mday); | ||
| 104 | buf[6] = BIN2BCD(tm->tm_mon + 1); | ||
| 105 | buf[7] = BIN2BCD(tm->tm_year - 100); | ||
| 106 | |||
| 107 | if ((i2c_master_send(client, buf, 8)) != 8) { | ||
| 108 | dev_err(&client->dev, "%s: write error\n", __FUNCTION__); | ||
| 109 | return -EIO; | ||
| 110 | } | ||
| 111 | |||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim) | ||
| 116 | { | ||
| 117 | unsigned char buf = RS5C372_REG_TRIM; | ||
| 118 | |||
| 119 | struct i2c_msg msgs[] = { | ||
| 120 | { client->addr, 0, 1, &buf }, | ||
| 121 | { client->addr, I2C_M_RD, 1, &buf }, | ||
| 122 | }; | ||
| 123 | |||
| 124 | if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { | ||
| 125 | dev_err(&client->dev, "%s: read error\n", __FUNCTION__); | ||
| 126 | return -EIO; | ||
| 127 | } | ||
| 128 | |||
| 129 | dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, trim); | ||
| 130 | |||
| 131 | if (osc) | ||
| 132 | *osc = (buf & RS5C372_TRIM_XSL) ? 32000 : 32768; | ||
| 133 | |||
| 134 | if (trim) | ||
| 135 | *trim = buf & RS5C372_TRIM_MASK; | ||
| 136 | |||
| 137 | return 0; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 141 | { | ||
| 142 | return rs5c372_get_datetime(to_i2c_client(dev), tm); | ||
| 143 | } | ||
| 144 | |||
| 145 | static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 146 | { | ||
| 147 | return rs5c372_set_datetime(to_i2c_client(dev), tm); | ||
| 148 | } | ||
| 149 | |||
| 150 | static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq) | ||
| 151 | { | ||
| 152 | int err, osc, trim; | ||
| 153 | |||
| 154 | seq_printf(seq, "24hr\t\t: yes\n"); | ||
| 155 | |||
| 156 | if ((err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim)) == 0) { | ||
| 157 | seq_printf(seq, "%d.%03d KHz\n", osc / 1000, osc % 1000); | ||
| 158 | seq_printf(seq, "trim\t: %d\n", trim); | ||
| 159 | } | ||
| 160 | |||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | |||
| 164 | static struct rtc_class_ops rs5c372_rtc_ops = { | ||
| 165 | .proc = rs5c372_rtc_proc, | ||
| 166 | .read_time = rs5c372_rtc_read_time, | ||
| 167 | .set_time = rs5c372_rtc_set_time, | ||
| 168 | }; | ||
| 169 | |||
| 170 | static ssize_t rs5c372_sysfs_show_trim(struct device *dev, | ||
| 171 | struct device_attribute *attr, char *buf) | ||
| 172 | { | ||
| 173 | int trim; | ||
| 174 | |||
| 175 | if (rs5c372_get_trim(to_i2c_client(dev), NULL, &trim) == 0) | ||
| 176 | return sprintf(buf, "0x%2x\n", trim); | ||
| 177 | |||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL); | ||
| 181 | |||
| 182 | static ssize_t rs5c372_sysfs_show_osc(struct device *dev, | ||
| 183 | struct device_attribute *attr, char *buf) | ||
| 184 | { | ||
| 185 | int osc; | ||
| 186 | |||
| 187 | if (rs5c372_get_trim(to_i2c_client(dev), &osc, NULL) == 0) | ||
| 188 | return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000); | ||
| 189 | |||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL); | ||
| 193 | |||
| 194 | static int rs5c372_attach(struct i2c_adapter *adapter) | ||
| 195 | { | ||
| 196 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); | ||
| 197 | return i2c_probe(adapter, &addr_data, rs5c372_probe); | ||
| 198 | } | ||
| 199 | |||
| 200 | static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) | ||
| 201 | { | ||
| 202 | int err = 0; | ||
| 203 | struct i2c_client *client; | ||
| 204 | struct rtc_device *rtc; | ||
| 205 | |||
| 206 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); | ||
| 207 | |||
| 208 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { | ||
| 209 | err = -ENODEV; | ||
| 210 | goto exit; | ||
| 211 | } | ||
| 212 | |||
| 213 | if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { | ||
| 214 | err = -ENOMEM; | ||
| 215 | goto exit; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* I2C client */ | ||
| 219 | client->addr = address; | ||
| 220 | client->driver = &rs5c372_driver; | ||
| 221 | client->adapter = adapter; | ||
| 222 | |||
| 223 | strlcpy(client->name, rs5c372_driver.driver.name, I2C_NAME_SIZE); | ||
| 224 | |||
| 225 | /* Inform the i2c layer */ | ||
| 226 | if ((err = i2c_attach_client(client))) | ||
| 227 | goto exit_kfree; | ||
| 228 | |||
| 229 | dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); | ||
| 230 | |||
| 231 | rtc = rtc_device_register(rs5c372_driver.driver.name, &client->dev, | ||
| 232 | &rs5c372_rtc_ops, THIS_MODULE); | ||
| 233 | |||
| 234 | if (IS_ERR(rtc)) { | ||
| 235 | err = PTR_ERR(rtc); | ||
| 236 | dev_err(&client->dev, | ||
| 237 | "unable to register the class device\n"); | ||
| 238 | goto exit_detach; | ||
| 239 | } | ||
| 240 | |||
| 241 | i2c_set_clientdata(client, rtc); | ||
| 242 | |||
| 243 | device_create_file(&client->dev, &dev_attr_trim); | ||
| 244 | device_create_file(&client->dev, &dev_attr_osc); | ||
| 245 | |||
| 246 | return 0; | ||
| 247 | |||
| 248 | exit_detach: | ||
| 249 | i2c_detach_client(client); | ||
| 250 | |||
| 251 | exit_kfree: | ||
| 252 | kfree(client); | ||
| 253 | |||
| 254 | exit: | ||
| 255 | return err; | ||
| 256 | } | ||
| 257 | |||
| 258 | static int rs5c372_detach(struct i2c_client *client) | ||
| 259 | { | ||
| 260 | int err; | ||
| 261 | struct rtc_device *rtc = i2c_get_clientdata(client); | ||
| 262 | |||
| 263 | dev_dbg(&client->dev, "%s\n", __FUNCTION__); | ||
| 264 | |||
| 265 | if (rtc) | ||
| 266 | rtc_device_unregister(rtc); | ||
| 267 | |||
| 268 | if ((err = i2c_detach_client(client))) | ||
| 269 | return err; | ||
| 270 | |||
| 271 | kfree(client); | ||
| 272 | |||
| 273 | return 0; | ||
| 274 | } | ||
| 275 | |||
| 276 | static __init int rs5c372_init(void) | ||
| 277 | { | ||
| 278 | return i2c_add_driver(&rs5c372_driver); | ||
| 279 | } | ||
| 280 | |||
| 281 | static __exit void rs5c372_exit(void) | ||
| 282 | { | ||
| 283 | i2c_del_driver(&rs5c372_driver); | ||
| 284 | } | ||
| 285 | |||
| 286 | module_init(rs5c372_init); | ||
| 287 | module_exit(rs5c372_exit); | ||
| 288 | |||
| 289 | MODULE_AUTHOR( | ||
| 290 | "Pavel Mironchik <pmironchik@optifacio.net>, " | ||
| 291 | "Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 292 | MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver"); | ||
| 293 | MODULE_LICENSE("GPL"); | ||
| 294 | MODULE_VERSION(DRV_VERSION); | ||
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c new file mode 100644 index 000000000000..83b2bb480a16 --- /dev/null +++ b/drivers/rtc/rtc-sa1100.c | |||
| @@ -0,0 +1,388 @@ | |||
| 1 | /* | ||
| 2 | * Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx | ||
| 3 | * | ||
| 4 | * Copyright (c) 2000 Nils Faerber | ||
| 5 | * | ||
| 6 | * Based on rtc.c by Paul Gortmaker | ||
| 7 | * | ||
| 8 | * Original Driver by Nils Faerber <nils@kernelconcepts.de> | ||
| 9 | * | ||
| 10 | * Modifications from: | ||
| 11 | * CIH <cih@coventive.com> | ||
| 12 | * Nicolas Pitre <nico@cam.org> | ||
| 13 | * Andrew Christian <andrew.christian@hp.com> | ||
| 14 | * | ||
| 15 | * Converted to the RTC subsystem and Driver Model | ||
| 16 | * by Richard Purdie <rpurdie@rpsys.net> | ||
| 17 | * | ||
| 18 | * This program is free software; you can redistribute it and/or | ||
| 19 | * modify it under the terms of the GNU General Public License | ||
| 20 | * as published by the Free Software Foundation; either version | ||
| 21 | * 2 of the License, or (at your option) any later version. | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/rtc.h> | ||
| 27 | #include <linux/init.h> | ||
| 28 | #include <linux/fs.h> | ||
| 29 | #include <linux/interrupt.h> | ||
| 30 | #include <linux/string.h> | ||
| 31 | #include <linux/pm.h> | ||
| 32 | |||
| 33 | #include <asm/bitops.h> | ||
| 34 | #include <asm/hardware.h> | ||
| 35 | #include <asm/irq.h> | ||
| 36 | #include <asm/rtc.h> | ||
| 37 | |||
| 38 | #ifdef CONFIG_ARCH_PXA | ||
| 39 | #include <asm/arch/pxa-regs.h> | ||
| 40 | #endif | ||
| 41 | |||
| 42 | #define TIMER_FREQ CLOCK_TICK_RATE | ||
| 43 | #define RTC_DEF_DIVIDER 32768 - 1 | ||
| 44 | #define RTC_DEF_TRIM 0 | ||
| 45 | |||
| 46 | static unsigned long rtc_freq = 1024; | ||
| 47 | static struct rtc_time rtc_alarm; | ||
| 48 | static spinlock_t sa1100_rtc_lock = SPIN_LOCK_UNLOCKED; | ||
| 49 | |||
| 50 | static int rtc_update_alarm(struct rtc_time *alrm) | ||
| 51 | { | ||
| 52 | struct rtc_time alarm_tm, now_tm; | ||
| 53 | unsigned long now, time; | ||
| 54 | int ret; | ||
| 55 | |||
| 56 | do { | ||
| 57 | now = RCNR; | ||
| 58 | rtc_time_to_tm(now, &now_tm); | ||
| 59 | rtc_next_alarm_time(&alarm_tm, &now_tm, alrm); | ||
| 60 | ret = rtc_tm_to_time(&alarm_tm, &time); | ||
| 61 | if (ret != 0) | ||
| 62 | break; | ||
| 63 | |||
| 64 | RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); | ||
| 65 | RTAR = time; | ||
| 66 | } while (now != RCNR); | ||
| 67 | |||
| 68 | return ret; | ||
| 69 | } | ||
| 70 | |||
| 71 | static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id, | ||
| 72 | struct pt_regs *regs) | ||
| 73 | { | ||
| 74 | struct platform_device *pdev = to_platform_device(dev_id); | ||
| 75 | struct rtc_device *rtc = platform_get_drvdata(pdev); | ||
| 76 | unsigned int rtsr; | ||
| 77 | unsigned long events = 0; | ||
| 78 | |||
| 79 | spin_lock(&sa1100_rtc_lock); | ||
| 80 | |||
| 81 | rtsr = RTSR; | ||
| 82 | /* clear interrupt sources */ | ||
| 83 | RTSR = 0; | ||
| 84 | RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); | ||
| 85 | |||
| 86 | /* clear alarm interrupt if it has occurred */ | ||
| 87 | if (rtsr & RTSR_AL) | ||
| 88 | rtsr &= ~RTSR_ALE; | ||
| 89 | RTSR = rtsr & (RTSR_ALE | RTSR_HZE); | ||
| 90 | |||
| 91 | /* update irq data & counter */ | ||
| 92 | if (rtsr & RTSR_AL) | ||
| 93 | events |= RTC_AF | RTC_IRQF; | ||
| 94 | if (rtsr & RTSR_HZ) | ||
| 95 | events |= RTC_UF | RTC_IRQF; | ||
| 96 | |||
| 97 | rtc_update_irq(&rtc->class_dev, 1, events); | ||
| 98 | |||
| 99 | if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm)) | ||
| 100 | rtc_update_alarm(&rtc_alarm); | ||
| 101 | |||
| 102 | spin_unlock(&sa1100_rtc_lock); | ||
| 103 | |||
| 104 | return IRQ_HANDLED; | ||
| 105 | } | ||
| 106 | |||
| 107 | static int rtc_timer1_count; | ||
| 108 | |||
| 109 | static irqreturn_t timer1_interrupt(int irq, void *dev_id, | ||
| 110 | struct pt_regs *regs) | ||
| 111 | { | ||
| 112 | struct platform_device *pdev = to_platform_device(dev_id); | ||
| 113 | struct rtc_device *rtc = platform_get_drvdata(pdev); | ||
| 114 | |||
| 115 | /* | ||
| 116 | * If we match for the first time, rtc_timer1_count will be 1. | ||
| 117 | * Otherwise, we wrapped around (very unlikely but | ||
| 118 | * still possible) so compute the amount of missed periods. | ||
| 119 | * The match reg is updated only when the data is actually retrieved | ||
| 120 | * to avoid unnecessary interrupts. | ||
| 121 | */ | ||
| 122 | OSSR = OSSR_M1; /* clear match on timer1 */ | ||
| 123 | |||
| 124 | rtc_update_irq(&rtc->class_dev, rtc_timer1_count, RTC_PF | RTC_IRQF); | ||
| 125 | |||
| 126 | if (rtc_timer1_count == 1) | ||
| 127 | rtc_timer1_count = (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))); | ||
| 128 | |||
| 129 | return IRQ_HANDLED; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int sa1100_rtc_read_callback(struct device *dev, int data) | ||
| 133 | { | ||
| 134 | if (data & RTC_PF) { | ||
| 135 | /* interpolate missed periods and set match for the next */ | ||
| 136 | unsigned long period = TIMER_FREQ/rtc_freq; | ||
| 137 | unsigned long oscr = OSCR; | ||
| 138 | unsigned long osmr1 = OSMR1; | ||
| 139 | unsigned long missed = (oscr - osmr1)/period; | ||
| 140 | data += missed << 8; | ||
| 141 | OSSR = OSSR_M1; /* clear match on timer 1 */ | ||
| 142 | OSMR1 = osmr1 + (missed + 1)*period; | ||
| 143 | /* Ensure we didn't miss another match in the mean time. | ||
| 144 | * Here we compare (match - OSCR) 8 instead of 0 -- | ||
| 145 | * see comment in pxa_timer_interrupt() for explanation. | ||
| 146 | */ | ||
| 147 | while( (signed long)((osmr1 = OSMR1) - OSCR) <= 8 ) { | ||
| 148 | data += 0x100; | ||
| 149 | OSSR = OSSR_M1; /* clear match on timer 1 */ | ||
| 150 | OSMR1 = osmr1 + period; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | return data; | ||
| 154 | } | ||
| 155 | |||
| 156 | static int sa1100_rtc_open(struct device *dev) | ||
| 157 | { | ||
| 158 | int ret; | ||
| 159 | |||
| 160 | ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, SA_INTERRUPT, | ||
| 161 | "rtc 1Hz", dev); | ||
| 162 | if (ret) { | ||
| 163 | printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTC1Hz); | ||
| 164 | goto fail_ui; | ||
| 165 | } | ||
| 166 | ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, SA_INTERRUPT, | ||
| 167 | "rtc Alrm", dev); | ||
| 168 | if (ret) { | ||
| 169 | printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTCAlrm); | ||
| 170 | goto fail_ai; | ||
| 171 | } | ||
| 172 | ret = request_irq(IRQ_OST1, timer1_interrupt, SA_INTERRUPT, | ||
| 173 | "rtc timer", dev); | ||
| 174 | if (ret) { | ||
| 175 | printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_OST1); | ||
| 176 | goto fail_pi; | ||
| 177 | } | ||
| 178 | return 0; | ||
| 179 | |||
| 180 | fail_pi: | ||
| 181 | free_irq(IRQ_RTCAlrm, NULL); | ||
| 182 | fail_ai: | ||
| 183 | free_irq(IRQ_RTC1Hz, NULL); | ||
| 184 | fail_ui: | ||
| 185 | return ret; | ||
| 186 | } | ||
| 187 | |||
| 188 | static void sa1100_rtc_release(struct device *dev) | ||
| 189 | { | ||
| 190 | spin_lock_irq(&sa1100_rtc_lock); | ||
| 191 | RTSR = 0; | ||
| 192 | OIER &= ~OIER_E1; | ||
| 193 | OSSR = OSSR_M1; | ||
| 194 | spin_unlock_irq(&sa1100_rtc_lock); | ||
| 195 | |||
| 196 | free_irq(IRQ_OST1, dev); | ||
| 197 | free_irq(IRQ_RTCAlrm, dev); | ||
| 198 | free_irq(IRQ_RTC1Hz, dev); | ||
| 199 | } | ||
| 200 | |||
| 201 | |||
| 202 | static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd, | ||
| 203 | unsigned long arg) | ||
| 204 | { | ||
| 205 | switch(cmd) { | ||
| 206 | case RTC_AIE_OFF: | ||
| 207 | spin_lock_irq(&sa1100_rtc_lock); | ||
| 208 | RTSR &= ~RTSR_ALE; | ||
| 209 | spin_unlock_irq(&sa1100_rtc_lock); | ||
| 210 | return 0; | ||
| 211 | case RTC_AIE_ON: | ||
| 212 | spin_lock_irq(&sa1100_rtc_lock); | ||
| 213 | RTSR |= RTSR_ALE; | ||
| 214 | spin_unlock_irq(&sa1100_rtc_lock); | ||
| 215 | return 0; | ||
| 216 | case RTC_UIE_OFF: | ||
| 217 | spin_lock_irq(&sa1100_rtc_lock); | ||
| 218 | RTSR &= ~RTSR_HZE; | ||
| 219 | spin_unlock_irq(&sa1100_rtc_lock); | ||
| 220 | return 0; | ||
| 221 | case RTC_UIE_ON: | ||
| 222 | spin_lock_irq(&sa1100_rtc_lock); | ||
| 223 | RTSR |= RTSR_HZE; | ||
| 224 | spin_unlock_irq(&sa1100_rtc_lock); | ||
| 225 | return 0; | ||
| 226 | case RTC_PIE_OFF: | ||
| 227 | spin_lock_irq(&sa1100_rtc_lock); | ||
| 228 | OIER &= ~OIER_E1; | ||
| 229 | spin_unlock_irq(&sa1100_rtc_lock); | ||
| 230 | return 0; | ||
| 231 | case RTC_PIE_ON: | ||
| 232 | if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE)) | ||
| 233 | return -EACCES; | ||
| 234 | spin_lock_irq(&sa1100_rtc_lock); | ||
| 235 | OSMR1 = TIMER_FREQ/rtc_freq + OSCR; | ||
| 236 | OIER |= OIER_E1; | ||
| 237 | rtc_timer1_count = 1; | ||
| 238 | spin_unlock_irq(&sa1100_rtc_lock); | ||
| 239 | return 0; | ||
| 240 | case RTC_IRQP_READ: | ||
| 241 | return put_user(rtc_freq, (unsigned long *)arg); | ||
| 242 | case RTC_IRQP_SET: | ||
| 243 | if (arg < 1 || arg > TIMER_FREQ) | ||
| 244 | return -EINVAL; | ||
| 245 | if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) | ||
| 246 | return -EACCES; | ||
| 247 | rtc_freq = arg; | ||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | return -EINVAL; | ||
| 251 | } | ||
| 252 | |||
| 253 | static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 254 | { | ||
| 255 | rtc_time_to_tm(RCNR, tm); | ||
| 256 | return 0; | ||
| 257 | } | ||
| 258 | |||
| 259 | static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 260 | { | ||
| 261 | unsigned long time; | ||
| 262 | int ret; | ||
| 263 | |||
| 264 | ret = rtc_tm_to_time(tm, &time); | ||
| 265 | if (ret == 0) | ||
| 266 | RCNR = time; | ||
| 267 | return ret; | ||
| 268 | } | ||
| 269 | |||
| 270 | static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
| 271 | { | ||
| 272 | memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time)); | ||
| 273 | alrm->pending = RTSR & RTSR_AL ? 1 : 0; | ||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | |||
| 277 | static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
| 278 | { | ||
| 279 | int ret; | ||
| 280 | |||
| 281 | spin_lock_irq(&sa1100_rtc_lock); | ||
| 282 | ret = rtc_update_alarm(&alrm->time); | ||
| 283 | if (ret == 0) { | ||
| 284 | memcpy(&rtc_alarm, &alrm->time, sizeof(struct rtc_time)); | ||
| 285 | |||
| 286 | if (alrm->enabled) | ||
| 287 | enable_irq_wake(IRQ_RTCAlrm); | ||
| 288 | else | ||
| 289 | disable_irq_wake(IRQ_RTCAlrm); | ||
| 290 | } | ||
| 291 | spin_unlock_irq(&sa1100_rtc_lock); | ||
| 292 | |||
| 293 | return ret; | ||
| 294 | } | ||
| 295 | |||
| 296 | static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) | ||
| 297 | { | ||
| 298 | seq_printf(seq, "trim/divider\t: 0x%08x\n", RTTR); | ||
| 299 | seq_printf(seq, "alarm_IRQ\t: %s\n", | ||
| 300 | (RTSR & RTSR_ALE) ? "yes" : "no" ); | ||
| 301 | seq_printf(seq, "update_IRQ\t: %s\n", | ||
| 302 | (RTSR & RTSR_HZE) ? "yes" : "no"); | ||
| 303 | seq_printf(seq, "periodic_IRQ\t: %s\n", | ||
| 304 | (OIER & OIER_E1) ? "yes" : "no"); | ||
| 305 | seq_printf(seq, "periodic_freq\t: %ld\n", rtc_freq); | ||
| 306 | |||
| 307 | return 0; | ||
| 308 | } | ||
| 309 | |||
| 310 | static struct rtc_class_ops sa1100_rtc_ops = { | ||
| 311 | .open = sa1100_rtc_open, | ||
| 312 | .read_callback = sa1100_rtc_read_callback, | ||
| 313 | .release = sa1100_rtc_release, | ||
| 314 | .ioctl = sa1100_rtc_ioctl, | ||
| 315 | .read_time = sa1100_rtc_read_time, | ||
| 316 | .set_time = sa1100_rtc_set_time, | ||
| 317 | .read_alarm = sa1100_rtc_read_alarm, | ||
| 318 | .set_alarm = sa1100_rtc_set_alarm, | ||
| 319 | .proc = sa1100_rtc_proc, | ||
| 320 | }; | ||
| 321 | |||
| 322 | static int sa1100_rtc_probe(struct platform_device *pdev) | ||
| 323 | { | ||
| 324 | struct rtc_device *rtc; | ||
| 325 | |||
| 326 | /* | ||
| 327 | * According to the manual we should be able to let RTTR be zero | ||
| 328 | * and then a default diviser for a 32.768KHz clock is used. | ||
| 329 | * Apparently this doesn't work, at least for my SA1110 rev 5. | ||
| 330 | * If the clock divider is uninitialized then reset it to the | ||
| 331 | * default value to get the 1Hz clock. | ||
| 332 | */ | ||
| 333 | if (RTTR == 0) { | ||
| 334 | RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); | ||
| 335 | printk(KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n"); | ||
| 336 | /* The current RTC value probably doesn't make sense either */ | ||
| 337 | RCNR = 0; | ||
| 338 | } | ||
| 339 | |||
| 340 | rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, | ||
| 341 | THIS_MODULE); | ||
| 342 | |||
| 343 | if (IS_ERR(rtc)) { | ||
| 344 | dev_err(&pdev->dev, "Unable to register the RTC device\n"); | ||
| 345 | return PTR_ERR(rtc); | ||
| 346 | } | ||
| 347 | |||
| 348 | platform_set_drvdata(pdev, rtc); | ||
| 349 | |||
| 350 | dev_info(&pdev->dev, "SA11xx/PXA2xx RTC Registered\n"); | ||
| 351 | |||
| 352 | return 0; | ||
| 353 | } | ||
| 354 | |||
| 355 | static int sa1100_rtc_remove(struct platform_device *pdev) | ||
| 356 | { | ||
| 357 | struct rtc_device *rtc = platform_get_drvdata(pdev); | ||
| 358 | |||
| 359 | if (rtc) | ||
| 360 | rtc_device_unregister(rtc); | ||
| 361 | |||
| 362 | return 0; | ||
| 363 | } | ||
| 364 | |||
| 365 | static struct platform_driver sa1100_rtc_driver = { | ||
| 366 | .probe = sa1100_rtc_probe, | ||
| 367 | .remove = sa1100_rtc_remove, | ||
| 368 | .driver = { | ||
| 369 | .name = "sa1100-rtc", | ||
| 370 | }, | ||
| 371 | }; | ||
| 372 | |||
| 373 | static int __init sa1100_rtc_init(void) | ||
| 374 | { | ||
| 375 | return platform_driver_register(&sa1100_rtc_driver); | ||
| 376 | } | ||
| 377 | |||
| 378 | static void __exit sa1100_rtc_exit(void) | ||
| 379 | { | ||
| 380 | platform_driver_unregister(&sa1100_rtc_driver); | ||
| 381 | } | ||
| 382 | |||
| 383 | module_init(sa1100_rtc_init); | ||
| 384 | module_exit(sa1100_rtc_exit); | ||
| 385 | |||
| 386 | MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); | ||
| 387 | MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)"); | ||
| 388 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c new file mode 100644 index 000000000000..7c1f3d2e53c4 --- /dev/null +++ b/drivers/rtc/rtc-sysfs.c | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* | ||
| 2 | * RTC subsystem, sysfs interface | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Tower Technologies | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/rtc.h> | ||
| 14 | |||
| 15 | /* device attributes */ | ||
| 16 | |||
| 17 | static ssize_t rtc_sysfs_show_name(struct class_device *dev, char *buf) | ||
| 18 | { | ||
| 19 | return sprintf(buf, "%s\n", to_rtc_device(dev)->name); | ||
| 20 | } | ||
| 21 | static CLASS_DEVICE_ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL); | ||
| 22 | |||
| 23 | static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf) | ||
| 24 | { | ||
| 25 | ssize_t retval; | ||
| 26 | struct rtc_time tm; | ||
| 27 | |||
| 28 | retval = rtc_read_time(dev, &tm); | ||
| 29 | if (retval == 0) { | ||
| 30 | retval = sprintf(buf, "%04d-%02d-%02d\n", | ||
| 31 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); | ||
| 32 | } | ||
| 33 | |||
| 34 | return retval; | ||
| 35 | } | ||
| 36 | static CLASS_DEVICE_ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL); | ||
| 37 | |||
| 38 | static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf) | ||
| 39 | { | ||
| 40 | ssize_t retval; | ||
| 41 | struct rtc_time tm; | ||
| 42 | |||
| 43 | retval = rtc_read_time(dev, &tm); | ||
| 44 | if (retval == 0) { | ||
| 45 | retval = sprintf(buf, "%02d:%02d:%02d\n", | ||
| 46 | tm.tm_hour, tm.tm_min, tm.tm_sec); | ||
| 47 | } | ||
| 48 | |||
| 49 | return retval; | ||
| 50 | } | ||
| 51 | static CLASS_DEVICE_ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL); | ||
| 52 | |||
| 53 | static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf) | ||
| 54 | { | ||
| 55 | ssize_t retval; | ||
| 56 | struct rtc_time tm; | ||
| 57 | |||
| 58 | retval = rtc_read_time(dev, &tm); | ||
| 59 | if (retval == 0) { | ||
| 60 | unsigned long time; | ||
| 61 | rtc_tm_to_time(&tm, &time); | ||
| 62 | retval = sprintf(buf, "%lu\n", time); | ||
| 63 | } | ||
| 64 | |||
| 65 | return retval; | ||
| 66 | } | ||
| 67 | static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL); | ||
| 68 | |||
| 69 | static struct attribute *rtc_attrs[] = { | ||
| 70 | &class_device_attr_name.attr, | ||
| 71 | &class_device_attr_date.attr, | ||
| 72 | &class_device_attr_time.attr, | ||
| 73 | &class_device_attr_since_epoch.attr, | ||
| 74 | NULL, | ||
| 75 | }; | ||
| 76 | |||
| 77 | static struct attribute_group rtc_attr_group = { | ||
| 78 | .attrs = rtc_attrs, | ||
| 79 | }; | ||
| 80 | |||
| 81 | static int __devinit rtc_sysfs_add_device(struct class_device *class_dev, | ||
| 82 | struct class_interface *class_intf) | ||
| 83 | { | ||
| 84 | int err; | ||
| 85 | |||
| 86 | dev_info(class_dev->dev, "rtc intf: sysfs\n"); | ||
| 87 | |||
| 88 | err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group); | ||
| 89 | if (err) | ||
| 90 | dev_err(class_dev->dev, | ||
| 91 | "failed to create sysfs attributes\n"); | ||
| 92 | |||
| 93 | return err; | ||
| 94 | } | ||
| 95 | |||
| 96 | static void rtc_sysfs_remove_device(struct class_device *class_dev, | ||
| 97 | struct class_interface *class_intf) | ||
| 98 | { | ||
| 99 | sysfs_remove_group(&class_dev->kobj, &rtc_attr_group); | ||
| 100 | } | ||
| 101 | |||
| 102 | /* interface registration */ | ||
| 103 | |||
| 104 | static struct class_interface rtc_sysfs_interface = { | ||
| 105 | .add = &rtc_sysfs_add_device, | ||
| 106 | .remove = &rtc_sysfs_remove_device, | ||
| 107 | }; | ||
| 108 | |||
| 109 | static int __init rtc_sysfs_init(void) | ||
| 110 | { | ||
| 111 | return rtc_interface_register(&rtc_sysfs_interface); | ||
| 112 | } | ||
| 113 | |||
| 114 | static void __exit rtc_sysfs_exit(void) | ||
| 115 | { | ||
| 116 | class_interface_unregister(&rtc_sysfs_interface); | ||
| 117 | } | ||
| 118 | |||
| 119 | module_init(rtc_sysfs_init); | ||
| 120 | module_exit(rtc_sysfs_exit); | ||
| 121 | |||
| 122 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 123 | MODULE_DESCRIPTION("RTC class sysfs interface"); | ||
| 124 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c new file mode 100644 index 000000000000..43d107487820 --- /dev/null +++ b/drivers/rtc/rtc-test.c | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | /* | ||
| 2 | * An RTC test device/driver | ||
| 3 | * Copyright (C) 2005 Tower Technologies | ||
| 4 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/err.h> | ||
| 13 | #include <linux/rtc.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | |||
| 16 | static struct platform_device *test0 = NULL, *test1 = NULL; | ||
| 17 | |||
| 18 | static int test_rtc_read_alarm(struct device *dev, | ||
| 19 | struct rtc_wkalrm *alrm) | ||
| 20 | { | ||
| 21 | return 0; | ||
| 22 | } | ||
| 23 | |||
| 24 | static int test_rtc_set_alarm(struct device *dev, | ||
| 25 | struct rtc_wkalrm *alrm) | ||
| 26 | { | ||
| 27 | return 0; | ||
| 28 | } | ||
| 29 | |||
| 30 | static int test_rtc_read_time(struct device *dev, | ||
| 31 | struct rtc_time *tm) | ||
| 32 | { | ||
| 33 | rtc_time_to_tm(get_seconds(), tm); | ||
| 34 | return 0; | ||
| 35 | } | ||
| 36 | |||
| 37 | static int test_rtc_set_time(struct device *dev, | ||
| 38 | struct rtc_time *tm) | ||
| 39 | { | ||
| 40 | return 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | static int test_rtc_set_mmss(struct device *dev, unsigned long secs) | ||
| 44 | { | ||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | static int test_rtc_proc(struct device *dev, struct seq_file *seq) | ||
| 49 | { | ||
| 50 | struct platform_device *plat_dev = to_platform_device(dev); | ||
| 51 | |||
| 52 | seq_printf(seq, "24hr\t\t: yes\n"); | ||
| 53 | seq_printf(seq, "test\t\t: yes\n"); | ||
| 54 | seq_printf(seq, "id\t\t: %d\n", plat_dev->id); | ||
| 55 | |||
| 56 | return 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | static int test_rtc_ioctl(struct device *dev, unsigned int cmd, | ||
| 60 | unsigned long arg) | ||
| 61 | { | ||
| 62 | /* We do support interrupts, they're generated | ||
| 63 | * using the sysfs interface. | ||
| 64 | */ | ||
| 65 | switch (cmd) { | ||
| 66 | case RTC_PIE_ON: | ||
| 67 | case RTC_PIE_OFF: | ||
| 68 | case RTC_UIE_ON: | ||
| 69 | case RTC_UIE_OFF: | ||
| 70 | case RTC_AIE_ON: | ||
| 71 | case RTC_AIE_OFF: | ||
| 72 | return 0; | ||
| 73 | |||
| 74 | default: | ||
| 75 | return -EINVAL; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | static struct rtc_class_ops test_rtc_ops = { | ||
| 80 | .proc = test_rtc_proc, | ||
| 81 | .read_time = test_rtc_read_time, | ||
| 82 | .set_time = test_rtc_set_time, | ||
| 83 | .read_alarm = test_rtc_read_alarm, | ||
| 84 | .set_alarm = test_rtc_set_alarm, | ||
| 85 | .set_mmss = test_rtc_set_mmss, | ||
| 86 | .ioctl = test_rtc_ioctl, | ||
| 87 | }; | ||
| 88 | |||
| 89 | static ssize_t test_irq_show(struct device *dev, | ||
| 90 | struct device_attribute *attr, char *buf) | ||
| 91 | { | ||
| 92 | return sprintf(buf, "%d\n", 42); | ||
| 93 | } | ||
| 94 | static ssize_t test_irq_store(struct device *dev, | ||
| 95 | struct device_attribute *attr, | ||
| 96 | const char *buf, size_t count) | ||
| 97 | { | ||
| 98 | int retval; | ||
| 99 | struct platform_device *plat_dev = to_platform_device(dev); | ||
| 100 | struct rtc_device *rtc = platform_get_drvdata(plat_dev); | ||
| 101 | |||
| 102 | retval = count; | ||
| 103 | if (strncmp(buf, "tick", 4) == 0) | ||
| 104 | rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF); | ||
| 105 | else if (strncmp(buf, "alarm", 5) == 0) | ||
| 106 | rtc_update_irq(&rtc->class_dev, 1, RTC_AF | RTC_IRQF); | ||
| 107 | else if (strncmp(buf, "update", 6) == 0) | ||
| 108 | rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF); | ||
| 109 | else | ||
| 110 | retval = -EINVAL; | ||
| 111 | |||
| 112 | return retval; | ||
| 113 | } | ||
| 114 | static DEVICE_ATTR(irq, S_IRUGO | S_IWUSR, test_irq_show, test_irq_store); | ||
| 115 | |||
| 116 | static int test_probe(struct platform_device *plat_dev) | ||
| 117 | { | ||
| 118 | int err; | ||
| 119 | struct rtc_device *rtc = rtc_device_register("test", &plat_dev->dev, | ||
| 120 | &test_rtc_ops, THIS_MODULE); | ||
| 121 | if (IS_ERR(rtc)) { | ||
| 122 | err = PTR_ERR(rtc); | ||
| 123 | dev_err(&plat_dev->dev, | ||
| 124 | "unable to register the class device\n"); | ||
| 125 | return err; | ||
| 126 | } | ||
| 127 | device_create_file(&plat_dev->dev, &dev_attr_irq); | ||
| 128 | |||
| 129 | platform_set_drvdata(plat_dev, rtc); | ||
| 130 | |||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | static int __devexit test_remove(struct platform_device *plat_dev) | ||
| 135 | { | ||
| 136 | struct rtc_device *rtc = platform_get_drvdata(plat_dev); | ||
| 137 | |||
| 138 | rtc_device_unregister(rtc); | ||
| 139 | device_remove_file(&plat_dev->dev, &dev_attr_irq); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | static struct platform_driver test_drv = { | ||
| 145 | .probe = test_probe, | ||
| 146 | .remove = __devexit_p(test_remove), | ||
| 147 | .driver = { | ||
| 148 | .name = "rtc-test", | ||
| 149 | .owner = THIS_MODULE, | ||
| 150 | }, | ||
| 151 | }; | ||
| 152 | |||
| 153 | static int __init test_init(void) | ||
| 154 | { | ||
| 155 | int err; | ||
| 156 | |||
| 157 | if ((err = platform_driver_register(&test_drv))) | ||
| 158 | return err; | ||
| 159 | |||
| 160 | if ((test0 = platform_device_alloc("rtc-test", 0)) == NULL) { | ||
| 161 | err = -ENOMEM; | ||
| 162 | goto exit_driver_unregister; | ||
| 163 | } | ||
| 164 | |||
| 165 | if ((test1 = platform_device_alloc("rtc-test", 1)) == NULL) { | ||
| 166 | err = -ENOMEM; | ||
| 167 | goto exit_free_test0; | ||
| 168 | } | ||
| 169 | |||
| 170 | if ((err = platform_device_add(test0))) | ||
| 171 | goto exit_free_test1; | ||
| 172 | |||
| 173 | if ((err = platform_device_add(test1))) | ||
| 174 | goto exit_device_unregister; | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | |||
| 178 | exit_device_unregister: | ||
| 179 | platform_device_unregister(test0); | ||
| 180 | |||
| 181 | exit_free_test1: | ||
| 182 | platform_device_put(test1); | ||
| 183 | |||
| 184 | exit_free_test0: | ||
| 185 | platform_device_put(test0); | ||
| 186 | |||
| 187 | exit_driver_unregister: | ||
| 188 | platform_driver_unregister(&test_drv); | ||
| 189 | return err; | ||
| 190 | } | ||
| 191 | |||
| 192 | static void __exit test_exit(void) | ||
| 193 | { | ||
| 194 | platform_device_unregister(test0); | ||
| 195 | platform_device_unregister(test1); | ||
| 196 | platform_driver_unregister(&test_drv); | ||
| 197 | } | ||
| 198 | |||
| 199 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
| 200 | MODULE_DESCRIPTION("RTC test driver/device"); | ||
| 201 | MODULE_LICENSE("GPL"); | ||
| 202 | |||
| 203 | module_init(test_init); | ||
| 204 | module_exit(test_exit); | ||
diff --git a/drivers/i2c/chips/x1205.c b/drivers/rtc/rtc-x1205.c index 245fffa92dbd..621d17afc0d9 100644 --- a/drivers/i2c/chips/x1205.c +++ b/drivers/rtc/rtc-x1205.c | |||
| @@ -1,32 +1,25 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * x1205.c - An i2c driver for the Xicor X1205 RTC | 2 | * An i2c driver for the Xicor/Intersil X1205 RTC |
| 3 | * Copyright 2004 Karen Spearel | 3 | * Copyright 2004 Karen Spearel |
| 4 | * Copyright 2005 Alessandro Zummo | 4 | * Copyright 2005 Alessandro Zummo |
| 5 | * | 5 | * |
| 6 | * please send all reports to: | 6 | * please send all reports to: |
| 7 | * kas11 at tampabay dot rr dot com | 7 | * Karen Spearel <kas111 at gmail dot com> |
| 8 | * a dot zummo at towertech dot it | 8 | * Alessandro Zummo <a.zummo@towertech.it> |
| 9 | * | 9 | * |
| 10 | * based on the other drivers in this same directory. | 10 | * based on a lot of other RTC drivers. |
| 11 | * | 11 | * |
| 12 | * This program is free software; you can redistribute it and/or modify | 12 | * This program is free software; you can redistribute it and/or modify |
| 13 | * it under the terms of the GNU General Public License as published by | 13 | * it under the terms of the GNU General Public License version 2 as |
| 14 | * the Free Software Foundation; either version 2 of the License, or | 14 | * published by the Free Software Foundation. |
| 15 | * (at your option) any later version. | ||
| 16 | */ | 15 | */ |
| 17 | 16 | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | #include <linux/i2c.h> | 17 | #include <linux/i2c.h> |
| 22 | #include <linux/string.h> | ||
| 23 | #include <linux/bcd.h> | 18 | #include <linux/bcd.h> |
| 24 | #include <linux/rtc.h> | 19 | #include <linux/rtc.h> |
| 25 | #include <linux/list.h> | 20 | #include <linux/delay.h> |
| 26 | 21 | ||
| 27 | #include <linux/x1205.h> | 22 | #define DRV_VERSION "1.0.6" |
| 28 | |||
| 29 | #define DRV_VERSION "0.9.9" | ||
| 30 | 23 | ||
| 31 | /* Addresses to scan: none. This chip is located at | 24 | /* Addresses to scan: none. This chip is located at |
| 32 | * 0x6f and uses a two bytes register addressing. | 25 | * 0x6f and uses a two bytes register addressing. |
| @@ -40,8 +33,6 @@ static unsigned short normal_i2c[] = { I2C_CLIENT_END }; | |||
| 40 | 33 | ||
| 41 | /* Insmod parameters */ | 34 | /* Insmod parameters */ |
| 42 | I2C_CLIENT_INSMOD; | 35 | I2C_CLIENT_INSMOD; |
| 43 | I2C_CLIENT_MODULE_PARM(hctosys, | ||
| 44 | "Set the system time from the hardware clock upon initialization"); | ||
| 45 | 36 | ||
| 46 | /* offsets into CCR area */ | 37 | /* offsets into CCR area */ |
| 47 | 38 | ||
| @@ -101,107 +92,35 @@ I2C_CLIENT_MODULE_PARM(hctosys, | |||
| 101 | static int x1205_attach(struct i2c_adapter *adapter); | 92 | static int x1205_attach(struct i2c_adapter *adapter); |
| 102 | static int x1205_detach(struct i2c_client *client); | 93 | static int x1205_detach(struct i2c_client *client); |
| 103 | static int x1205_probe(struct i2c_adapter *adapter, int address, int kind); | 94 | static int x1205_probe(struct i2c_adapter *adapter, int address, int kind); |
| 104 | static int x1205_command(struct i2c_client *client, unsigned int cmd, | ||
| 105 | void *arg); | ||
| 106 | 95 | ||
| 107 | static struct i2c_driver x1205_driver = { | 96 | static struct i2c_driver x1205_driver = { |
| 108 | .driver = { | 97 | .driver = { |
| 109 | .name = "x1205", | 98 | .name = "x1205", |
| 110 | }, | 99 | }, |
| 100 | .id = I2C_DRIVERID_X1205, | ||
| 111 | .attach_adapter = &x1205_attach, | 101 | .attach_adapter = &x1205_attach, |
| 112 | .detach_client = &x1205_detach, | 102 | .detach_client = &x1205_detach, |
| 113 | }; | 103 | }; |
| 114 | 104 | ||
| 115 | struct x1205_data { | ||
| 116 | struct i2c_client client; | ||
| 117 | struct list_head list; | ||
| 118 | unsigned int epoch; | ||
| 119 | }; | ||
| 120 | |||
| 121 | static const unsigned char days_in_mo[] = | ||
| 122 | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | ||
| 123 | |||
| 124 | static LIST_HEAD(x1205_clients); | ||
| 125 | |||
| 126 | /* Workaround until the I2C subsytem will allow to send | ||
| 127 | * commands to a specific client. This function will send the command | ||
| 128 | * to the first client. | ||
| 129 | */ | ||
| 130 | int x1205_do_command(unsigned int cmd, void *arg) | ||
| 131 | { | ||
| 132 | struct list_head *walk; | ||
| 133 | struct list_head *tmp; | ||
| 134 | struct x1205_data *data; | ||
| 135 | |||
| 136 | list_for_each_safe(walk, tmp, &x1205_clients) { | ||
| 137 | data = list_entry(walk, struct x1205_data, list); | ||
| 138 | return x1205_command(&data->client, cmd, arg); | ||
| 139 | } | ||
| 140 | |||
| 141 | return -ENODEV; | ||
| 142 | } | ||
| 143 | |||
| 144 | #define is_leap(year) \ | ||
| 145 | ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) | ||
| 146 | |||
| 147 | /* make sure the rtc_time values are in bounds */ | ||
| 148 | static int x1205_validate_tm(struct rtc_time *tm) | ||
| 149 | { | ||
| 150 | int year = tm->tm_year + 1900; | ||
| 151 | |||
| 152 | if ((tm->tm_year < 70) || (tm->tm_year > 255)) | ||
| 153 | return -EINVAL; | ||
| 154 | |||
| 155 | if ((tm->tm_mon > 11) || (tm->tm_mday == 0)) | ||
| 156 | return -EINVAL; | ||
| 157 | |||
| 158 | if (tm->tm_mday > days_in_mo[tm->tm_mon] | ||
| 159 | + ((tm->tm_mon == 1) && is_leap(year))) | ||
| 160 | return -EINVAL; | ||
| 161 | |||
| 162 | if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60)) | ||
| 163 | return -EINVAL; | ||
| 164 | |||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* | 105 | /* |
| 169 | * In the routines that deal directly with the x1205 hardware, we use | 106 | * In the routines that deal directly with the x1205 hardware, we use |
| 170 | * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch | 107 | * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch |
| 171 | * Epoch is initialized as 2000. Time is set to UTC. | 108 | * Epoch is initialized as 2000. Time is set to UTC. |
| 172 | */ | 109 | */ |
| 173 | static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, | 110 | static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, |
| 174 | u8 reg_base) | 111 | unsigned char reg_base) |
| 175 | { | 112 | { |
| 176 | unsigned char dt_addr[2] = { 0, reg_base }; | 113 | unsigned char dt_addr[2] = { 0, reg_base }; |
| 177 | static unsigned char sr_addr[2] = { 0, X1205_REG_SR }; | ||
| 178 | 114 | ||
| 179 | unsigned char buf[8], sr; | 115 | unsigned char buf[8]; |
| 180 | 116 | ||
| 181 | struct i2c_msg msgs[] = { | 117 | struct i2c_msg msgs[] = { |
| 182 | { client->addr, 0, 2, sr_addr }, /* setup read ptr */ | ||
| 183 | { client->addr, I2C_M_RD, 1, &sr }, /* read status */ | ||
| 184 | { client->addr, 0, 2, dt_addr }, /* setup read ptr */ | 118 | { client->addr, 0, 2, dt_addr }, /* setup read ptr */ |
| 185 | { client->addr, I2C_M_RD, 8, buf }, /* read date */ | 119 | { client->addr, I2C_M_RD, 8, buf }, /* read date */ |
| 186 | }; | 120 | }; |
| 187 | 121 | ||
| 188 | struct x1205_data *data = i2c_get_clientdata(client); | ||
| 189 | |||
| 190 | /* read status register */ | ||
| 191 | if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { | ||
| 192 | dev_err(&client->dev, "%s: read error\n", __FUNCTION__); | ||
| 193 | return -EIO; | ||
| 194 | } | ||
| 195 | |||
| 196 | /* check for battery failure */ | ||
| 197 | if (sr & X1205_SR_RTCF) { | ||
| 198 | dev_warn(&client->dev, | ||
| 199 | "Clock had a power failure, you must set the date.\n"); | ||
| 200 | return -EINVAL; | ||
| 201 | } | ||
| 202 | |||
| 203 | /* read date registers */ | 122 | /* read date registers */ |
| 204 | if ((i2c_transfer(client->adapter, &msgs[2], 2)) != 2) { | 123 | if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { |
| 205 | dev_err(&client->dev, "%s: read error\n", __FUNCTION__); | 124 | dev_err(&client->dev, "%s: read error\n", __FUNCTION__); |
| 206 | return -EIO; | 125 | return -EIO; |
| 207 | } | 126 | } |
| @@ -217,9 +136,9 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, | |||
| 217 | tm->tm_min = BCD2BIN(buf[CCR_MIN]); | 136 | tm->tm_min = BCD2BIN(buf[CCR_MIN]); |
| 218 | tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */ | 137 | tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */ |
| 219 | tm->tm_mday = BCD2BIN(buf[CCR_MDAY]); | 138 | tm->tm_mday = BCD2BIN(buf[CCR_MDAY]); |
| 220 | tm->tm_mon = BCD2BIN(buf[CCR_MONTH]); | 139 | tm->tm_mon = BCD2BIN(buf[CCR_MONTH]) - 1; /* mon is 0-11 */ |
| 221 | data->epoch = BCD2BIN(buf[CCR_Y2K]) * 100; | 140 | tm->tm_year = BCD2BIN(buf[CCR_YEAR]) |
| 222 | tm->tm_year = BCD2BIN(buf[CCR_YEAR]) + data->epoch - 1900; | 141 | + (BCD2BIN(buf[CCR_Y2K]) * 100) - 1900; |
| 223 | tm->tm_wday = buf[CCR_WDAY]; | 142 | tm->tm_wday = buf[CCR_WDAY]; |
| 224 | 143 | ||
| 225 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " | 144 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " |
| @@ -231,11 +150,28 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, | |||
| 231 | return 0; | 150 | return 0; |
| 232 | } | 151 | } |
| 233 | 152 | ||
| 153 | static int x1205_get_status(struct i2c_client *client, unsigned char *sr) | ||
| 154 | { | ||
| 155 | static unsigned char sr_addr[2] = { 0, X1205_REG_SR }; | ||
| 156 | |||
| 157 | struct i2c_msg msgs[] = { | ||
| 158 | { client->addr, 0, 2, sr_addr }, /* setup read ptr */ | ||
| 159 | { client->addr, I2C_M_RD, 1, sr }, /* read status */ | ||
| 160 | }; | ||
| 161 | |||
| 162 | /* read status register */ | ||
| 163 | if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { | ||
| 164 | dev_err(&client->dev, "%s: read error\n", __FUNCTION__); | ||
| 165 | return -EIO; | ||
| 166 | } | ||
| 167 | |||
| 168 | return 0; | ||
| 169 | } | ||
| 170 | |||
| 234 | static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, | 171 | static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, |
| 235 | int datetoo, u8 reg_base) | 172 | int datetoo, u8 reg_base) |
| 236 | { | 173 | { |
| 237 | int i, err, xfer; | 174 | int i, xfer; |
| 238 | |||
| 239 | unsigned char buf[8]; | 175 | unsigned char buf[8]; |
| 240 | 176 | ||
| 241 | static const unsigned char wel[3] = { 0, X1205_REG_SR, | 177 | static const unsigned char wel[3] = { 0, X1205_REG_SR, |
| @@ -246,17 +182,10 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, | |||
| 246 | 182 | ||
| 247 | static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 }; | 183 | static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 }; |
| 248 | 184 | ||
| 249 | struct x1205_data *data = i2c_get_clientdata(client); | 185 | dev_dbg(&client->dev, |
| 250 | 186 | "%s: secs=%d, mins=%d, hours=%d\n", | |
| 251 | /* check if all values in the tm struct are correct */ | ||
| 252 | if ((err = x1205_validate_tm(tm)) < 0) | ||
| 253 | return err; | ||
| 254 | |||
| 255 | dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " | ||
| 256 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 257 | __FUNCTION__, | 187 | __FUNCTION__, |
| 258 | tm->tm_sec, tm->tm_min, tm->tm_hour, | 188 | tm->tm_sec, tm->tm_min, tm->tm_hour); |
| 259 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 260 | 189 | ||
| 261 | buf[CCR_SEC] = BIN2BCD(tm->tm_sec); | 190 | buf[CCR_SEC] = BIN2BCD(tm->tm_sec); |
| 262 | buf[CCR_MIN] = BIN2BCD(tm->tm_min); | 191 | buf[CCR_MIN] = BIN2BCD(tm->tm_min); |
| @@ -266,26 +195,29 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, | |||
| 266 | 195 | ||
| 267 | /* should we also set the date? */ | 196 | /* should we also set the date? */ |
| 268 | if (datetoo) { | 197 | if (datetoo) { |
| 198 | dev_dbg(&client->dev, | ||
| 199 | "%s: mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 200 | __FUNCTION__, | ||
| 201 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 202 | |||
| 269 | buf[CCR_MDAY] = BIN2BCD(tm->tm_mday); | 203 | buf[CCR_MDAY] = BIN2BCD(tm->tm_mday); |
| 270 | 204 | ||
| 271 | /* month, 0 - 11 */ | 205 | /* month, 1 - 12 */ |
| 272 | buf[CCR_MONTH] = BIN2BCD(tm->tm_mon); | 206 | buf[CCR_MONTH] = BIN2BCD(tm->tm_mon + 1); |
| 273 | 207 | ||
| 274 | /* year, since 1900 */ | 208 | /* year, since the rtc epoch*/ |
| 275 | buf[CCR_YEAR] = BIN2BCD(tm->tm_year + 1900 - data->epoch); | 209 | buf[CCR_YEAR] = BIN2BCD(tm->tm_year % 100); |
| 276 | buf[CCR_WDAY] = tm->tm_wday & 0x07; | 210 | buf[CCR_WDAY] = tm->tm_wday & 0x07; |
| 277 | buf[CCR_Y2K] = BIN2BCD(data->epoch / 100); | 211 | buf[CCR_Y2K] = BIN2BCD(tm->tm_year / 100); |
| 278 | } | 212 | } |
| 279 | 213 | ||
| 280 | /* this sequence is required to unlock the chip */ | 214 | /* this sequence is required to unlock the chip */ |
| 281 | xfer = i2c_master_send(client, wel, 3); | 215 | if ((xfer = i2c_master_send(client, wel, 3)) != 3) { |
| 282 | if (xfer != 3) { | ||
| 283 | dev_err(&client->dev, "%s: wel - %d\n", __FUNCTION__, xfer); | 216 | dev_err(&client->dev, "%s: wel - %d\n", __FUNCTION__, xfer); |
| 284 | return -EIO; | 217 | return -EIO; |
| 285 | } | 218 | } |
| 286 | 219 | ||
| 287 | xfer = i2c_master_send(client, rwel, 3); | 220 | if ((xfer = i2c_master_send(client, rwel, 3)) != 3) { |
| 288 | if (xfer != 3) { | ||
| 289 | dev_err(&client->dev, "%s: rwel - %d\n", __FUNCTION__, xfer); | 221 | dev_err(&client->dev, "%s: rwel - %d\n", __FUNCTION__, xfer); |
| 290 | return -EIO; | 222 | return -EIO; |
| 291 | } | 223 | } |
| @@ -305,8 +237,7 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, | |||
| 305 | }; | 237 | }; |
| 306 | 238 | ||
| 307 | /* disable further writes */ | 239 | /* disable further writes */ |
| 308 | xfer = i2c_master_send(client, diswe, 3); | 240 | if ((xfer = i2c_master_send(client, diswe, 3)) != 3) { |
| 309 | if (xfer != 3) { | ||
| 310 | dev_err(&client->dev, "%s: diswe - %d\n", __FUNCTION__, xfer); | 241 | dev_err(&client->dev, "%s: diswe - %d\n", __FUNCTION__, xfer); |
| 311 | return -EIO; | 242 | return -EIO; |
| 312 | } | 243 | } |
| @@ -314,6 +245,20 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, | |||
| 314 | return 0; | 245 | return 0; |
| 315 | } | 246 | } |
| 316 | 247 | ||
| 248 | static int x1205_fix_osc(struct i2c_client *client) | ||
| 249 | { | ||
| 250 | int err; | ||
| 251 | struct rtc_time tm; | ||
| 252 | |||
| 253 | tm.tm_hour = tm.tm_min = tm.tm_sec = 0; | ||
| 254 | |||
| 255 | if ((err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE)) < 0) | ||
| 256 | dev_err(&client->dev, | ||
| 257 | "unable to restart the oscillator\n"); | ||
| 258 | |||
| 259 | return err; | ||
| 260 | } | ||
| 261 | |||
| 317 | static int x1205_get_dtrim(struct i2c_client *client, int *trim) | 262 | static int x1205_get_dtrim(struct i2c_client *client, int *trim) |
| 318 | { | 263 | { |
| 319 | unsigned char dtr; | 264 | unsigned char dtr; |
| @@ -380,60 +325,9 @@ static int x1205_get_atrim(struct i2c_client *client, int *trim) | |||
| 380 | return 0; | 325 | return 0; |
| 381 | } | 326 | } |
| 382 | 327 | ||
| 383 | static int x1205_hctosys(struct i2c_client *client) | ||
| 384 | { | ||
| 385 | int err; | ||
| 386 | |||
| 387 | struct rtc_time tm; | ||
| 388 | struct timespec tv; | ||
| 389 | |||
| 390 | err = x1205_command(client, X1205_CMD_GETDATETIME, &tm); | ||
| 391 | |||
| 392 | if (err) { | ||
| 393 | dev_err(&client->dev, | ||
| 394 | "Unable to set the system clock\n"); | ||
| 395 | return err; | ||
| 396 | } | ||
| 397 | |||
| 398 | /* IMPORTANT: the RTC only stores whole seconds. It is arbitrary | ||
| 399 | * whether it stores the most close value or the value with partial | ||
| 400 | * seconds truncated. However, it is important that we use it to store | ||
| 401 | * the truncated value. This is because otherwise it is necessary, | ||
| 402 | * in an rtc sync function, to read both xtime.tv_sec and | ||
| 403 | * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read | ||
| 404 | * of >32bits is not possible. So storing the most close value would | ||
| 405 | * slow down the sync API. So here we have the truncated value and | ||
| 406 | * the best guess is to add 0.5s. | ||
| 407 | */ | ||
| 408 | |||
| 409 | tv.tv_nsec = NSEC_PER_SEC >> 1; | ||
| 410 | |||
| 411 | /* WARNING: this is not the C library 'mktime' call, it is a built in | ||
| 412 | * inline function from include/linux/time.h. It expects (requires) | ||
| 413 | * the month to be in the range 1-12 | ||
| 414 | */ | ||
| 415 | |||
| 416 | tv.tv_sec = mktime(tm.tm_year + 1900, tm.tm_mon + 1, | ||
| 417 | tm.tm_mday, tm.tm_hour, | ||
| 418 | tm.tm_min, tm.tm_sec); | ||
| 419 | |||
| 420 | do_settimeofday(&tv); | ||
| 421 | |||
| 422 | dev_info(&client->dev, | ||
| 423 | "setting the system clock to %d-%d-%d %d:%d:%d\n", | ||
| 424 | tm.tm_year + 1900, tm.tm_mon + 1, | ||
| 425 | tm.tm_mday, tm.tm_hour, tm.tm_min, | ||
| 426 | tm.tm_sec); | ||
| 427 | |||
| 428 | return 0; | ||
| 429 | } | ||
| 430 | |||
| 431 | struct x1205_limit | 328 | struct x1205_limit |
| 432 | { | 329 | { |
| 433 | unsigned char reg; | 330 | unsigned char reg, mask, min, max; |
| 434 | unsigned char mask; | ||
| 435 | unsigned char min; | ||
| 436 | unsigned char max; | ||
| 437 | }; | 331 | }; |
| 438 | 332 | ||
| 439 | static int x1205_validate_client(struct i2c_client *client) | 333 | static int x1205_validate_client(struct i2c_client *client) |
| @@ -477,11 +371,10 @@ static int x1205_validate_client(struct i2c_client *client) | |||
| 477 | { client->addr, I2C_M_RD, 1, &buf }, | 371 | { client->addr, I2C_M_RD, 1, &buf }, |
| 478 | }; | 372 | }; |
| 479 | 373 | ||
| 480 | xfer = i2c_transfer(client->adapter, msgs, 2); | 374 | if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) { |
| 481 | if (xfer != 2) { | ||
| 482 | dev_err(&client->adapter->dev, | 375 | dev_err(&client->adapter->dev, |
| 483 | "%s: could not read register %x\n", | 376 | "%s: could not read register %x\n", |
| 484 | __FUNCTION__, addr[1]); | 377 | __FUNCTION__, probe_zero_pattern[i]); |
| 485 | 378 | ||
| 486 | return -EIO; | 379 | return -EIO; |
| 487 | } | 380 | } |
| @@ -489,7 +382,7 @@ static int x1205_validate_client(struct i2c_client *client) | |||
| 489 | if ((buf & probe_zero_pattern[i+1]) != 0) { | 382 | if ((buf & probe_zero_pattern[i+1]) != 0) { |
| 490 | dev_err(&client->adapter->dev, | 383 | dev_err(&client->adapter->dev, |
| 491 | "%s: register=%02x, zero pattern=%d, value=%x\n", | 384 | "%s: register=%02x, zero pattern=%d, value=%x\n", |
| 492 | __FUNCTION__, addr[1], i, buf); | 385 | __FUNCTION__, probe_zero_pattern[i], i, buf); |
| 493 | 386 | ||
| 494 | return -ENODEV; | 387 | return -ENODEV; |
| 495 | } | 388 | } |
| @@ -506,12 +399,10 @@ static int x1205_validate_client(struct i2c_client *client) | |||
| 506 | { client->addr, I2C_M_RD, 1, ® }, | 399 | { client->addr, I2C_M_RD, 1, ® }, |
| 507 | }; | 400 | }; |
| 508 | 401 | ||
| 509 | xfer = i2c_transfer(client->adapter, msgs, 2); | 402 | if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) { |
| 510 | |||
| 511 | if (xfer != 2) { | ||
| 512 | dev_err(&client->adapter->dev, | 403 | dev_err(&client->adapter->dev, |
| 513 | "%s: could not read register %x\n", | 404 | "%s: could not read register %x\n", |
| 514 | __FUNCTION__, addr[1]); | 405 | __FUNCTION__, probe_limits_pattern[i].reg); |
| 515 | 406 | ||
| 516 | return -EIO; | 407 | return -EIO; |
| 517 | } | 408 | } |
| @@ -522,7 +413,8 @@ static int x1205_validate_client(struct i2c_client *client) | |||
| 522 | value < probe_limits_pattern[i].min) { | 413 | value < probe_limits_pattern[i].min) { |
| 523 | dev_dbg(&client->adapter->dev, | 414 | dev_dbg(&client->adapter->dev, |
| 524 | "%s: register=%x, lim pattern=%d, value=%d\n", | 415 | "%s: register=%x, lim pattern=%d, value=%d\n", |
| 525 | __FUNCTION__, addr[1], i, value); | 416 | __FUNCTION__, probe_limits_pattern[i].reg, |
| 417 | i, value); | ||
| 526 | 418 | ||
| 527 | return -ENODEV; | 419 | return -ENODEV; |
| 528 | } | 420 | } |
| @@ -531,37 +423,89 @@ static int x1205_validate_client(struct i2c_client *client) | |||
| 531 | return 0; | 423 | return 0; |
| 532 | } | 424 | } |
| 533 | 425 | ||
| 534 | static int x1205_attach(struct i2c_adapter *adapter) | 426 | static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
| 535 | { | 427 | { |
| 536 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); | 428 | return x1205_get_datetime(to_i2c_client(dev), |
| 429 | &alrm->time, X1205_ALM0_BASE); | ||
| 430 | } | ||
| 537 | 431 | ||
| 538 | return i2c_probe(adapter, &addr_data, x1205_probe); | 432 | static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
| 433 | { | ||
| 434 | return x1205_set_datetime(to_i2c_client(dev), | ||
| 435 | &alrm->time, 1, X1205_ALM0_BASE); | ||
| 539 | } | 436 | } |
| 540 | 437 | ||
| 541 | int x1205_direct_attach(int adapter_id, | 438 | static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm) |
| 542 | struct i2c_client_address_data *address_data) | ||
| 543 | { | 439 | { |
| 544 | int err; | 440 | return x1205_get_datetime(to_i2c_client(dev), |
| 545 | struct i2c_adapter *adapter = i2c_get_adapter(adapter_id); | 441 | tm, X1205_CCR_BASE); |
| 442 | } | ||
| 546 | 443 | ||
| 547 | if (adapter) { | 444 | static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm) |
| 548 | err = i2c_probe(adapter, | 445 | { |
| 549 | address_data, x1205_probe); | 446 | return x1205_set_datetime(to_i2c_client(dev), |
| 447 | tm, 1, X1205_CCR_BASE); | ||
| 448 | } | ||
| 550 | 449 | ||
| 551 | i2c_put_adapter(adapter); | 450 | static int x1205_rtc_proc(struct device *dev, struct seq_file *seq) |
| 451 | { | ||
| 452 | int err, dtrim, atrim; | ||
| 552 | 453 | ||
| 553 | return err; | 454 | seq_printf(seq, "24hr\t\t: yes\n"); |
| 554 | } | ||
| 555 | 455 | ||
| 556 | return -ENODEV; | 456 | if ((err = x1205_get_dtrim(to_i2c_client(dev), &dtrim)) == 0) |
| 457 | seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim); | ||
| 458 | |||
| 459 | if ((err = x1205_get_atrim(to_i2c_client(dev), &atrim)) == 0) | ||
| 460 | seq_printf(seq, "analog_trim\t: %d.%02d pF\n", | ||
| 461 | atrim / 1000, atrim % 1000); | ||
| 462 | return 0; | ||
| 557 | } | 463 | } |
| 558 | 464 | ||
| 559 | static int x1205_probe(struct i2c_adapter *adapter, int address, int kind) | 465 | static struct rtc_class_ops x1205_rtc_ops = { |
| 466 | .proc = x1205_rtc_proc, | ||
| 467 | .read_time = x1205_rtc_read_time, | ||
| 468 | .set_time = x1205_rtc_set_time, | ||
| 469 | .read_alarm = x1205_rtc_read_alarm, | ||
| 470 | .set_alarm = x1205_rtc_set_alarm, | ||
| 471 | }; | ||
| 472 | |||
| 473 | static ssize_t x1205_sysfs_show_atrim(struct device *dev, | ||
| 474 | struct device_attribute *attr, char *buf) | ||
| 560 | { | 475 | { |
| 561 | struct i2c_client *client; | 476 | int atrim; |
| 562 | struct x1205_data *data; | 477 | |
| 478 | if (x1205_get_atrim(to_i2c_client(dev), &atrim) == 0) | ||
| 479 | return sprintf(buf, "%d.%02d pF\n", | ||
| 480 | atrim / 1000, atrim % 1000); | ||
| 481 | return 0; | ||
| 482 | } | ||
| 483 | static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL); | ||
| 484 | |||
| 485 | static ssize_t x1205_sysfs_show_dtrim(struct device *dev, | ||
| 486 | struct device_attribute *attr, char *buf) | ||
| 487 | { | ||
| 488 | int dtrim; | ||
| 489 | |||
| 490 | if (x1205_get_dtrim(to_i2c_client(dev), &dtrim) == 0) | ||
| 491 | return sprintf(buf, "%d ppm\n", dtrim); | ||
| 492 | |||
| 493 | return 0; | ||
| 494 | } | ||
| 495 | static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL); | ||
| 563 | 496 | ||
| 497 | static int x1205_attach(struct i2c_adapter *adapter) | ||
| 498 | { | ||
| 499 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); | ||
| 500 | return i2c_probe(adapter, &addr_data, x1205_probe); | ||
| 501 | } | ||
| 502 | |||
| 503 | static int x1205_probe(struct i2c_adapter *adapter, int address, int kind) | ||
| 504 | { | ||
| 564 | int err = 0; | 505 | int err = 0; |
| 506 | unsigned char sr; | ||
| 507 | struct i2c_client *client; | ||
| 508 | struct rtc_device *rtc; | ||
| 565 | 509 | ||
| 566 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); | 510 | dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); |
| 567 | 511 | ||
| @@ -570,22 +514,17 @@ static int x1205_probe(struct i2c_adapter *adapter, int address, int kind) | |||
| 570 | goto exit; | 514 | goto exit; |
| 571 | } | 515 | } |
| 572 | 516 | ||
| 573 | if (!(data = kzalloc(sizeof(struct x1205_data), GFP_KERNEL))) { | 517 | if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { |
| 574 | err = -ENOMEM; | 518 | err = -ENOMEM; |
| 575 | goto exit; | 519 | goto exit; |
| 576 | } | 520 | } |
| 577 | 521 | ||
| 578 | /* Initialize our structures */ | 522 | /* I2C client */ |
| 579 | data->epoch = 2000; | ||
| 580 | |||
| 581 | client = &data->client; | ||
| 582 | client->addr = address; | 523 | client->addr = address; |
| 583 | client->driver = &x1205_driver; | 524 | client->driver = &x1205_driver; |
| 584 | client->adapter = adapter; | 525 | client->adapter = adapter; |
| 585 | 526 | ||
| 586 | strlcpy(client->name, "x1205", I2C_NAME_SIZE); | 527 | strlcpy(client->name, x1205_driver.driver.name, I2C_NAME_SIZE); |
| 587 | |||
| 588 | i2c_set_clientdata(client, data); | ||
| 589 | 528 | ||
| 590 | /* Verify the chip is really an X1205 */ | 529 | /* Verify the chip is really an X1205 */ |
| 591 | if (kind < 0) { | 530 | if (kind < 0) { |
| @@ -599,18 +538,43 @@ static int x1205_probe(struct i2c_adapter *adapter, int address, int kind) | |||
| 599 | if ((err = i2c_attach_client(client))) | 538 | if ((err = i2c_attach_client(client))) |
| 600 | goto exit_kfree; | 539 | goto exit_kfree; |
| 601 | 540 | ||
| 602 | list_add(&data->list, &x1205_clients); | ||
| 603 | |||
| 604 | dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); | 541 | dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); |
| 605 | 542 | ||
| 606 | /* If requested, set the system time */ | 543 | rtc = rtc_device_register(x1205_driver.driver.name, &client->dev, |
| 607 | if (hctosys) | 544 | &x1205_rtc_ops, THIS_MODULE); |
| 608 | x1205_hctosys(client); | 545 | |
| 546 | if (IS_ERR(rtc)) { | ||
| 547 | err = PTR_ERR(rtc); | ||
| 548 | dev_err(&client->dev, | ||
| 549 | "unable to register the class device\n"); | ||
| 550 | goto exit_detach; | ||
| 551 | } | ||
| 552 | |||
| 553 | i2c_set_clientdata(client, rtc); | ||
| 554 | |||
| 555 | /* Check for power failures and eventualy enable the osc */ | ||
| 556 | if ((err = x1205_get_status(client, &sr)) == 0) { | ||
| 557 | if (sr & X1205_SR_RTCF) { | ||
| 558 | dev_err(&client->dev, | ||
| 559 | "power failure detected, " | ||
| 560 | "please set the clock\n"); | ||
| 561 | udelay(50); | ||
| 562 | x1205_fix_osc(client); | ||
| 563 | } | ||
| 564 | } | ||
| 565 | else | ||
| 566 | dev_err(&client->dev, "couldn't read status\n"); | ||
| 567 | |||
| 568 | device_create_file(&client->dev, &dev_attr_atrim); | ||
| 569 | device_create_file(&client->dev, &dev_attr_dtrim); | ||
| 609 | 570 | ||
| 610 | return 0; | 571 | return 0; |
| 611 | 572 | ||
| 573 | exit_detach: | ||
| 574 | i2c_detach_client(client); | ||
| 575 | |||
| 612 | exit_kfree: | 576 | exit_kfree: |
| 613 | kfree(data); | 577 | kfree(client); |
| 614 | 578 | ||
| 615 | exit: | 579 | exit: |
| 616 | return err; | 580 | return err; |
| @@ -619,61 +583,21 @@ exit: | |||
| 619 | static int x1205_detach(struct i2c_client *client) | 583 | static int x1205_detach(struct i2c_client *client) |
| 620 | { | 584 | { |
| 621 | int err; | 585 | int err; |
| 622 | struct x1205_data *data = i2c_get_clientdata(client); | 586 | struct rtc_device *rtc = i2c_get_clientdata(client); |
| 623 | 587 | ||
| 624 | dev_dbg(&client->dev, "%s\n", __FUNCTION__); | 588 | dev_dbg(&client->dev, "%s\n", __FUNCTION__); |
| 625 | 589 | ||
| 590 | if (rtc) | ||
| 591 | rtc_device_unregister(rtc); | ||
| 592 | |||
| 626 | if ((err = i2c_detach_client(client))) | 593 | if ((err = i2c_detach_client(client))) |
| 627 | return err; | 594 | return err; |
| 628 | 595 | ||
| 629 | list_del(&data->list); | 596 | kfree(client); |
| 630 | |||
| 631 | kfree(data); | ||
| 632 | 597 | ||
| 633 | return 0; | 598 | return 0; |
| 634 | } | 599 | } |
| 635 | 600 | ||
| 636 | static int x1205_command(struct i2c_client *client, unsigned int cmd, | ||
| 637 | void *param) | ||
| 638 | { | ||
| 639 | if (param == NULL) | ||
| 640 | return -EINVAL; | ||
| 641 | |||
| 642 | if (!capable(CAP_SYS_TIME)) | ||
| 643 | return -EACCES; | ||
| 644 | |||
| 645 | dev_dbg(&client->dev, "%s: cmd=%d\n", __FUNCTION__, cmd); | ||
| 646 | |||
| 647 | switch (cmd) { | ||
| 648 | case X1205_CMD_GETDATETIME: | ||
| 649 | return x1205_get_datetime(client, param, X1205_CCR_BASE); | ||
| 650 | |||
| 651 | case X1205_CMD_SETTIME: | ||
| 652 | return x1205_set_datetime(client, param, 0, | ||
| 653 | X1205_CCR_BASE); | ||
| 654 | |||
| 655 | case X1205_CMD_SETDATETIME: | ||
| 656 | return x1205_set_datetime(client, param, 1, | ||
| 657 | X1205_CCR_BASE); | ||
| 658 | |||
| 659 | case X1205_CMD_GETALARM: | ||
| 660 | return x1205_get_datetime(client, param, X1205_ALM0_BASE); | ||
| 661 | |||
| 662 | case X1205_CMD_SETALARM: | ||
| 663 | return x1205_set_datetime(client, param, 1, | ||
| 664 | X1205_ALM0_BASE); | ||
| 665 | |||
| 666 | case X1205_CMD_GETDTRIM: | ||
| 667 | return x1205_get_dtrim(client, param); | ||
| 668 | |||
| 669 | case X1205_CMD_GETATRIM: | ||
| 670 | return x1205_get_atrim(client, param); | ||
| 671 | |||
| 672 | default: | ||
| 673 | return -EINVAL; | ||
| 674 | } | ||
| 675 | } | ||
| 676 | |||
| 677 | static int __init x1205_init(void) | 601 | static int __init x1205_init(void) |
| 678 | { | 602 | { |
| 679 | return i2c_add_driver(&x1205_driver); | 603 | return i2c_add_driver(&x1205_driver); |
| @@ -685,14 +609,11 @@ static void __exit x1205_exit(void) | |||
| 685 | } | 609 | } |
| 686 | 610 | ||
| 687 | MODULE_AUTHOR( | 611 | MODULE_AUTHOR( |
| 688 | "Karen Spearel <kas11@tampabay.rr.com>, " | 612 | "Karen Spearel <kas111 at gmail dot com>, " |
| 689 | "Alessandro Zummo <a.zummo@towertech.it>"); | 613 | "Alessandro Zummo <a.zummo@towertech.it>"); |
| 690 | MODULE_DESCRIPTION("Xicor X1205 RTC driver"); | 614 | MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver"); |
| 691 | MODULE_LICENSE("GPL"); | 615 | MODULE_LICENSE("GPL"); |
| 692 | MODULE_VERSION(DRV_VERSION); | 616 | MODULE_VERSION(DRV_VERSION); |
| 693 | 617 | ||
| 694 | EXPORT_SYMBOL_GPL(x1205_do_command); | ||
| 695 | EXPORT_SYMBOL_GPL(x1205_direct_attach); | ||
| 696 | |||
| 697 | module_init(x1205_init); | 618 | module_init(x1205_init); |
| 698 | module_exit(x1205_exit); | 619 | module_exit(x1205_exit); |
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 62e3cda859af..7f7013e80a88 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c | |||
| @@ -671,7 +671,7 @@ static struct file_operations gdth_fops = { | |||
| 671 | static struct notifier_block gdth_notifier = { | 671 | static struct notifier_block gdth_notifier = { |
| 672 | gdth_halt, NULL, 0 | 672 | gdth_halt, NULL, 0 |
| 673 | }; | 673 | }; |
| 674 | 674 | static int notifier_disabled = 0; | |
| 675 | 675 | ||
| 676 | static void gdth_delay(int milliseconds) | 676 | static void gdth_delay(int milliseconds) |
| 677 | { | 677 | { |
| @@ -4595,13 +4595,13 @@ static int __init gdth_detect(struct scsi_host_template *shtp) | |||
| 4595 | add_timer(&gdth_timer); | 4595 | add_timer(&gdth_timer); |
| 4596 | #endif | 4596 | #endif |
| 4597 | major = register_chrdev(0,"gdth",&gdth_fops); | 4597 | major = register_chrdev(0,"gdth",&gdth_fops); |
| 4598 | notifier_disabled = 0; | ||
| 4598 | register_reboot_notifier(&gdth_notifier); | 4599 | register_reboot_notifier(&gdth_notifier); |
| 4599 | } | 4600 | } |
| 4600 | gdth_polling = FALSE; | 4601 | gdth_polling = FALSE; |
| 4601 | return gdth_ctr_vcount; | 4602 | return gdth_ctr_vcount; |
| 4602 | } | 4603 | } |
| 4603 | 4604 | ||
| 4604 | |||
| 4605 | static int gdth_release(struct Scsi_Host *shp) | 4605 | static int gdth_release(struct Scsi_Host *shp) |
| 4606 | { | 4606 | { |
| 4607 | int hanum; | 4607 | int hanum; |
| @@ -5632,10 +5632,14 @@ static int gdth_halt(struct notifier_block *nb, ulong event, void *buf) | |||
| 5632 | char cmnd[MAX_COMMAND_SIZE]; | 5632 | char cmnd[MAX_COMMAND_SIZE]; |
| 5633 | #endif | 5633 | #endif |
| 5634 | 5634 | ||
| 5635 | if (notifier_disabled) | ||
| 5636 | return NOTIFY_OK; | ||
| 5637 | |||
| 5635 | TRACE2(("gdth_halt() event %d\n",(int)event)); | 5638 | TRACE2(("gdth_halt() event %d\n",(int)event)); |
| 5636 | if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) | 5639 | if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) |
| 5637 | return NOTIFY_DONE; | 5640 | return NOTIFY_DONE; |
| 5638 | 5641 | ||
| 5642 | notifier_disabled = 1; | ||
| 5639 | printk("GDT-HA: Flushing all host drives .. "); | 5643 | printk("GDT-HA: Flushing all host drives .. "); |
| 5640 | for (hanum = 0; hanum < gdth_ctr_count; ++hanum) { | 5644 | for (hanum = 0; hanum < gdth_ctr_count; ++hanum) { |
| 5641 | gdth_flush(hanum); | 5645 | gdth_flush(hanum); |
| @@ -5679,7 +5683,6 @@ static int gdth_halt(struct notifier_block *nb, ulong event, void *buf) | |||
| 5679 | #ifdef GDTH_STATISTICS | 5683 | #ifdef GDTH_STATISTICS |
| 5680 | del_timer(&gdth_timer); | 5684 | del_timer(&gdth_timer); |
| 5681 | #endif | 5685 | #endif |
| 5682 | unregister_reboot_notifier(&gdth_notifier); | ||
| 5683 | return NOTIFY_OK; | 5686 | return NOTIFY_OK; |
| 5684 | } | 5687 | } |
| 5685 | 5688 | ||
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c index 4b55285de9a0..fe0ed54fa0ae 100644 --- a/drivers/usb/core/notify.c +++ b/drivers/usb/core/notify.c | |||
| @@ -16,57 +16,7 @@ | |||
| 16 | #include <linux/mutex.h> | 16 | #include <linux/mutex.h> |
| 17 | #include "usb.h" | 17 | #include "usb.h" |
| 18 | 18 | ||
| 19 | 19 | static BLOCKING_NOTIFIER_HEAD(usb_notifier_list); | |
| 20 | static struct notifier_block *usb_notifier_list; | ||
| 21 | static DEFINE_MUTEX(usb_notifier_lock); | ||
| 22 | |||
| 23 | static void usb_notifier_chain_register(struct notifier_block **list, | ||
| 24 | struct notifier_block *n) | ||
| 25 | { | ||
| 26 | mutex_lock(&usb_notifier_lock); | ||
| 27 | while (*list) { | ||
| 28 | if (n->priority > (*list)->priority) | ||
| 29 | break; | ||
| 30 | list = &((*list)->next); | ||
| 31 | } | ||
| 32 | n->next = *list; | ||
| 33 | *list = n; | ||
| 34 | mutex_unlock(&usb_notifier_lock); | ||
| 35 | } | ||
| 36 | |||
| 37 | static void usb_notifier_chain_unregister(struct notifier_block **nl, | ||
| 38 | struct notifier_block *n) | ||
| 39 | { | ||
| 40 | mutex_lock(&usb_notifier_lock); | ||
| 41 | while ((*nl)!=NULL) { | ||
| 42 | if ((*nl)==n) { | ||
| 43 | *nl = n->next; | ||
| 44 | goto exit; | ||
| 45 | } | ||
| 46 | nl=&((*nl)->next); | ||
| 47 | } | ||
| 48 | exit: | ||
| 49 | mutex_unlock(&usb_notifier_lock); | ||
| 50 | } | ||
| 51 | |||
| 52 | static int usb_notifier_call_chain(struct notifier_block **n, | ||
| 53 | unsigned long val, void *v) | ||
| 54 | { | ||
| 55 | int ret=NOTIFY_DONE; | ||
| 56 | struct notifier_block *nb = *n; | ||
| 57 | |||
| 58 | mutex_lock(&usb_notifier_lock); | ||
| 59 | while (nb) { | ||
| 60 | ret = nb->notifier_call(nb,val,v); | ||
| 61 | if (ret&NOTIFY_STOP_MASK) { | ||
| 62 | goto exit; | ||
| 63 | } | ||
| 64 | nb = nb->next; | ||
| 65 | } | ||
| 66 | exit: | ||
| 67 | mutex_unlock(&usb_notifier_lock); | ||
| 68 | return ret; | ||
| 69 | } | ||
| 70 | 20 | ||
| 71 | /** | 21 | /** |
| 72 | * usb_register_notify - register a notifier callback whenever a usb change happens | 22 | * usb_register_notify - register a notifier callback whenever a usb change happens |
| @@ -76,7 +26,7 @@ exit: | |||
| 76 | */ | 26 | */ |
| 77 | void usb_register_notify(struct notifier_block *nb) | 27 | void usb_register_notify(struct notifier_block *nb) |
| 78 | { | 28 | { |
| 79 | usb_notifier_chain_register(&usb_notifier_list, nb); | 29 | blocking_notifier_chain_register(&usb_notifier_list, nb); |
| 80 | } | 30 | } |
| 81 | EXPORT_SYMBOL_GPL(usb_register_notify); | 31 | EXPORT_SYMBOL_GPL(usb_register_notify); |
| 82 | 32 | ||
| @@ -89,27 +39,28 @@ EXPORT_SYMBOL_GPL(usb_register_notify); | |||
| 89 | */ | 39 | */ |
| 90 | void usb_unregister_notify(struct notifier_block *nb) | 40 | void usb_unregister_notify(struct notifier_block *nb) |
| 91 | { | 41 | { |
| 92 | usb_notifier_chain_unregister(&usb_notifier_list, nb); | 42 | blocking_notifier_chain_unregister(&usb_notifier_list, nb); |
| 93 | } | 43 | } |
| 94 | EXPORT_SYMBOL_GPL(usb_unregister_notify); | 44 | EXPORT_SYMBOL_GPL(usb_unregister_notify); |
| 95 | 45 | ||
| 96 | 46 | ||
| 97 | void usb_notify_add_device(struct usb_device *udev) | 47 | void usb_notify_add_device(struct usb_device *udev) |
| 98 | { | 48 | { |
| 99 | usb_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev); | 49 | blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev); |
| 100 | } | 50 | } |
| 101 | 51 | ||
| 102 | void usb_notify_remove_device(struct usb_device *udev) | 52 | void usb_notify_remove_device(struct usb_device *udev) |
| 103 | { | 53 | { |
| 104 | usb_notifier_call_chain(&usb_notifier_list, USB_DEVICE_REMOVE, udev); | 54 | blocking_notifier_call_chain(&usb_notifier_list, |
| 55 | USB_DEVICE_REMOVE, udev); | ||
| 105 | } | 56 | } |
| 106 | 57 | ||
| 107 | void usb_notify_add_bus(struct usb_bus *ubus) | 58 | void usb_notify_add_bus(struct usb_bus *ubus) |
| 108 | { | 59 | { |
| 109 | usb_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus); | 60 | blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus); |
| 110 | } | 61 | } |
| 111 | 62 | ||
| 112 | void usb_notify_remove_bus(struct usb_bus *ubus) | 63 | void usb_notify_remove_bus(struct usb_bus *ubus) |
| 113 | { | 64 | { |
| 114 | usb_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus); | 65 | blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus); |
| 115 | } | 66 | } |
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index fdebd60a3250..22e9d696fdd2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
| @@ -70,6 +70,22 @@ config FB_MACMODES | |||
| 70 | depends on FB | 70 | depends on FB |
| 71 | default n | 71 | default n |
| 72 | 72 | ||
| 73 | config FB_FIRMWARE_EDID | ||
| 74 | bool "Enable firmware EDID" | ||
| 75 | depends on FB | ||
| 76 | default y | ||
| 77 | ---help--- | ||
| 78 | This enables access to the EDID transferred from the firmware. | ||
| 79 | On the i386, this is from the Video BIOS. Enable this if DDC/I2C | ||
| 80 | transfers do not work for your driver and if you are using | ||
| 81 | nvidiafb, i810fb or savagefb. | ||
| 82 | |||
| 83 | In general, choosing Y for this option is safe. If you | ||
| 84 | experience extremely long delays while booting before you get | ||
| 85 | something on your display, try setting this to N. Matrox cards in | ||
| 86 | combination with certain motherboards and monitors are known to | ||
| 87 | suffer from this problem. | ||
| 88 | |||
| 73 | config FB_MODE_HELPERS | 89 | config FB_MODE_HELPERS |
| 74 | bool "Enable Video Mode Handling Helpers" | 90 | bool "Enable Video Mode Handling Helpers" |
| 75 | depends on FB | 91 | depends on FB |
| @@ -1202,6 +1218,17 @@ config FB_AU1100 | |||
| 1202 | bool "Au1100 LCD Driver" | 1218 | bool "Au1100 LCD Driver" |
| 1203 | depends on (FB = y) && EXPERIMENTAL && PCI && MIPS && MIPS_PB1100=y | 1219 | depends on (FB = y) && EXPERIMENTAL && PCI && MIPS && MIPS_PB1100=y |
| 1204 | 1220 | ||
| 1221 | config FB_AU1200 | ||
| 1222 | bool "Au1200 LCD Driver" | ||
| 1223 | depends on FB && MIPS && SOC_AU1200 | ||
| 1224 | select FB_CFB_FILLRECT | ||
| 1225 | select FB_CFB_COPYAREA | ||
| 1226 | select FB_CFB_IMAGEBLIT | ||
| 1227 | help | ||
| 1228 | This is the framebuffer driver for the AMD Au1200 SOC. It can drive | ||
| 1229 | various panels and CRTs by passing in kernel cmd line option | ||
| 1230 | au1200fb:panel=<name>. | ||
| 1231 | |||
| 1205 | source "drivers/video/geode/Kconfig" | 1232 | source "drivers/video/geode/Kconfig" |
| 1206 | 1233 | ||
| 1207 | config FB_FFB | 1234 | config FB_FFB |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index aa434e725c0d..cb90218515ac 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
| @@ -86,6 +86,7 @@ obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o | |||
| 86 | obj-$(CONFIG_FB_PXA) += pxafb.o | 86 | obj-$(CONFIG_FB_PXA) += pxafb.o |
| 87 | obj-$(CONFIG_FB_W100) += w100fb.o | 87 | obj-$(CONFIG_FB_W100) += w100fb.o |
| 88 | obj-$(CONFIG_FB_AU1100) += au1100fb.o | 88 | obj-$(CONFIG_FB_AU1100) += au1100fb.o |
| 89 | obj-$(CONFIG_FB_AU1200) += au1200fb.o | ||
| 89 | obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o | 90 | obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o |
| 90 | obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o | 91 | obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o |
| 91 | obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o | 92 | obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o |
diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index 76448d6ae896..98baecccb3fd 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c | |||
| @@ -1308,7 +1308,7 @@ static int __init acornfb_probe(struct platform_device *dev) | |||
| 1308 | /* | 1308 | /* |
| 1309 | * Try to select a suitable default mode | 1309 | * Try to select a suitable default mode |
| 1310 | */ | 1310 | */ |
| 1311 | for (i = 0; i < sizeof(modedb) / sizeof(*modedb); i++) { | 1311 | for (i = 0; i < ARRAY_SIZE(modedb); i++) { |
| 1312 | unsigned long hs; | 1312 | unsigned long hs; |
| 1313 | 1313 | ||
| 1314 | hs = modedb[i].refresh * | 1314 | hs = modedb[i].refresh * |
| @@ -1380,7 +1380,7 @@ static int __init acornfb_probe(struct platform_device *dev) | |||
| 1380 | */ | 1380 | */ |
| 1381 | free_unused_pages(PAGE_OFFSET + size, PAGE_OFFSET + MAX_SIZE); | 1381 | free_unused_pages(PAGE_OFFSET + size, PAGE_OFFSET + MAX_SIZE); |
| 1382 | #endif | 1382 | #endif |
| 1383 | 1383 | ||
| 1384 | fb_info.fix.smem_len = size; | 1384 | fb_info.fix.smem_len = size; |
| 1385 | current_par.palette_size = VIDC_PALETTE_SIZE; | 1385 | current_par.palette_size = VIDC_PALETTE_SIZE; |
| 1386 | 1386 | ||
| @@ -1391,7 +1391,7 @@ static int __init acornfb_probe(struct platform_device *dev) | |||
| 1391 | */ | 1391 | */ |
| 1392 | do { | 1392 | do { |
| 1393 | rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb, | 1393 | rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb, |
| 1394 | sizeof(modedb) / sizeof(*modedb), | 1394 | ARRAY_SIZE(modedb), |
| 1395 | &acornfb_default_mode, DEFAULT_BPP); | 1395 | &acornfb_default_mode, DEFAULT_BPP); |
| 1396 | /* | 1396 | /* |
| 1397 | * If we found an exact match, all ok. | 1397 | * If we found an exact match, all ok. |
| @@ -1408,7 +1408,7 @@ static int __init acornfb_probe(struct platform_device *dev) | |||
| 1408 | break; | 1408 | break; |
| 1409 | 1409 | ||
| 1410 | rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb, | 1410 | rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb, |
| 1411 | sizeof(modedb) / sizeof(*modedb), | 1411 | ARRAY_SIZE(modedb), |
| 1412 | &acornfb_default_mode, DEFAULT_BPP); | 1412 | &acornfb_default_mode, DEFAULT_BPP); |
| 1413 | if (rc) | 1413 | if (rc) |
| 1414 | break; | 1414 | break; |
diff --git a/drivers/video/asiliantfb.c b/drivers/video/asiliantfb.c index c924d81f7978..29f9f0dfe3b4 100644 --- a/drivers/video/asiliantfb.c +++ b/drivers/video/asiliantfb.c | |||
| @@ -353,8 +353,6 @@ struct chips_init_reg { | |||
| 353 | unsigned char data; | 353 | unsigned char data; |
| 354 | }; | 354 | }; |
| 355 | 355 | ||
| 356 | #define N_ELTS(x) (sizeof(x) / sizeof(x[0])) | ||
| 357 | |||
| 358 | static struct chips_init_reg chips_init_sr[] = | 356 | static struct chips_init_reg chips_init_sr[] = |
| 359 | { | 357 | { |
| 360 | {0x00, 0x03}, /* Reset register */ | 358 | {0x00, 0x03}, /* Reset register */ |
| @@ -460,22 +458,22 @@ static void __devinit chips_hw_init(struct fb_info *p) | |||
| 460 | { | 458 | { |
| 461 | int i; | 459 | int i; |
| 462 | 460 | ||
| 463 | for (i = 0; i < N_ELTS(chips_init_xr); ++i) | 461 | for (i = 0; i < ARRAY_SIZE(chips_init_xr); ++i) |
| 464 | write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); | 462 | write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); |
| 465 | write_xr(0x81, 0x12); | 463 | write_xr(0x81, 0x12); |
| 466 | write_xr(0x82, 0x08); | 464 | write_xr(0x82, 0x08); |
| 467 | write_xr(0x20, 0x00); | 465 | write_xr(0x20, 0x00); |
| 468 | for (i = 0; i < N_ELTS(chips_init_sr); ++i) | 466 | for (i = 0; i < ARRAY_SIZE(chips_init_sr); ++i) |
| 469 | write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); | 467 | write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); |
| 470 | for (i = 0; i < N_ELTS(chips_init_gr); ++i) | 468 | for (i = 0; i < ARRAY_SIZE(chips_init_gr); ++i) |
| 471 | write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); | 469 | write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); |
| 472 | for (i = 0; i < N_ELTS(chips_init_ar); ++i) | 470 | for (i = 0; i < ARRAY_SIZE(chips_init_ar); ++i) |
| 473 | write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); | 471 | write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); |
| 474 | /* Enable video output in attribute index register */ | 472 | /* Enable video output in attribute index register */ |
| 475 | writeb(0x20, mmio_base + 0x780); | 473 | writeb(0x20, mmio_base + 0x780); |
| 476 | for (i = 0; i < N_ELTS(chips_init_cr); ++i) | 474 | for (i = 0; i < ARRAY_SIZE(chips_init_cr); ++i) |
| 477 | write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); | 475 | write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); |
| 478 | for (i = 0; i < N_ELTS(chips_init_fr); ++i) | 476 | for (i = 0; i < ARRAY_SIZE(chips_init_fr); ++i) |
| 479 | write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); | 477 | write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); |
| 480 | } | 478 | } |
| 481 | 479 | ||
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 620c9a934e0e..821c6da8e42c 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c | |||
| @@ -1725,9 +1725,9 @@ static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id * | |||
| 1725 | strcpy(video_card, "Rage128 XX "); | 1725 | strcpy(video_card, "Rage128 XX "); |
| 1726 | video_card[8] = ent->device >> 8; | 1726 | video_card[8] = ent->device >> 8; |
| 1727 | video_card[9] = ent->device & 0xFF; | 1727 | video_card[9] = ent->device & 0xFF; |
| 1728 | 1728 | ||
| 1729 | /* range check to make sure */ | 1729 | /* range check to make sure */ |
| 1730 | if (ent->driver_data < (sizeof(r128_family)/sizeof(char *))) | 1730 | if (ent->driver_data < ARRAY_SIZE(r128_family)) |
| 1731 | strncat(video_card, r128_family[ent->driver_data], sizeof(video_card)); | 1731 | strncat(video_card, r128_family[ent->driver_data], sizeof(video_card)); |
| 1732 | 1732 | ||
| 1733 | printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev); | 1733 | printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev); |
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 485be386a8ff..e799fcca365a 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c | |||
| @@ -434,7 +434,7 @@ static int __devinit correct_chipset(struct atyfb_par *par) | |||
| 434 | const char *name; | 434 | const char *name; |
| 435 | int i; | 435 | int i; |
| 436 | 436 | ||
| 437 | for (i = sizeof(aty_chips) / sizeof(*aty_chips) - 1; i >= 0; i--) | 437 | for (i = ARRAY_SIZE(aty_chips) - 1; i >= 0; i--) |
| 438 | if (par->pci_id == aty_chips[i].pci_id) | 438 | if (par->pci_id == aty_chips[i].pci_id) |
| 439 | break; | 439 | break; |
| 440 | 440 | ||
| @@ -2168,10 +2168,10 @@ static void __init aty_calc_mem_refresh(struct atyfb_par *par, int xclk) | |||
| 2168 | 2168 | ||
| 2169 | if (IS_XL(par->pci_id) || IS_MOBILITY(par->pci_id)) { | 2169 | if (IS_XL(par->pci_id) || IS_MOBILITY(par->pci_id)) { |
| 2170 | refresh_tbl = ragexl_tbl; | 2170 | refresh_tbl = ragexl_tbl; |
| 2171 | size = sizeof(ragexl_tbl)/sizeof(int); | 2171 | size = ARRAY_SIZE(ragexl_tbl); |
| 2172 | } else { | 2172 | } else { |
| 2173 | refresh_tbl = ragepro_tbl; | 2173 | refresh_tbl = ragepro_tbl; |
| 2174 | size = sizeof(ragepro_tbl)/sizeof(int); | 2174 | size = ARRAY_SIZE(ragepro_tbl); |
| 2175 | } | 2175 | } |
| 2176 | 2176 | ||
| 2177 | for (i=0; i < size; i++) { | 2177 | for (i=0; i < size; i++) { |
| @@ -2298,6 +2298,10 @@ static int __init aty_init(struct fb_info *info, const char *name) | |||
| 2298 | case CLK_ATI18818_1: | 2298 | case CLK_ATI18818_1: |
| 2299 | par->pll_ops = &aty_pll_ati18818_1; | 2299 | par->pll_ops = &aty_pll_ati18818_1; |
| 2300 | break; | 2300 | break; |
| 2301 | case CLK_IBMRGB514: | ||
| 2302 | par->pll_ops = &aty_pll_ibm514; | ||
| 2303 | break; | ||
| 2304 | #if 0 /* dead code */ | ||
| 2301 | case CLK_STG1703: | 2305 | case CLK_STG1703: |
| 2302 | par->pll_ops = &aty_pll_stg1703; | 2306 | par->pll_ops = &aty_pll_stg1703; |
| 2303 | break; | 2307 | break; |
| @@ -2307,9 +2311,7 @@ static int __init aty_init(struct fb_info *info, const char *name) | |||
| 2307 | case CLK_ATT20C408: | 2311 | case CLK_ATT20C408: |
| 2308 | par->pll_ops = &aty_pll_att20c408; | 2312 | par->pll_ops = &aty_pll_att20c408; |
| 2309 | break; | 2313 | break; |
| 2310 | case CLK_IBMRGB514: | 2314 | #endif |
| 2311 | par->pll_ops = &aty_pll_ibm514; | ||
| 2312 | break; | ||
| 2313 | default: | 2315 | default: |
| 2314 | PRINTKI("aty_init: CLK type not implemented yet!"); | 2316 | PRINTKI("aty_init: CLK type not implemented yet!"); |
| 2315 | par->pll_ops = &aty_pll_unsupported; | 2317 | par->pll_ops = &aty_pll_unsupported; |
| @@ -3397,7 +3399,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi | |||
| 3397 | struct atyfb_par *par; | 3399 | struct atyfb_par *par; |
| 3398 | int i, rc = -ENOMEM; | 3400 | int i, rc = -ENOMEM; |
| 3399 | 3401 | ||
| 3400 | for (i = sizeof(aty_chips) / sizeof(*aty_chips) - 1; i >= 0; i--) | 3402 | for (i = ARRAY_SIZE(aty_chips); i >= 0; i--) |
| 3401 | if (pdev->device == aty_chips[i].pci_id) | 3403 | if (pdev->device == aty_chips[i].pci_id) |
| 3402 | break; | 3404 | break; |
| 3403 | 3405 | ||
diff --git a/drivers/video/aty/mach64_gx.c b/drivers/video/aty/mach64_gx.c index 01fdff79483b..2045639cb671 100644 --- a/drivers/video/aty/mach64_gx.c +++ b/drivers/video/aty/mach64_gx.c | |||
| @@ -149,8 +149,7 @@ static int aty_var_to_pll_514(const struct fb_info *info, u32 vclk_per, | |||
| 149 | }; | 149 | }; |
| 150 | int i; | 150 | int i; |
| 151 | 151 | ||
| 152 | for (i = 0; i < sizeof(RGB514_clocks) / sizeof(*RGB514_clocks); | 152 | for (i = 0; i < ARRAY_SIZE(RGB514_clocks); i++) |
| 153 | i++) | ||
| 154 | if (vclk_per <= RGB514_clocks[i].limit) { | 153 | if (vclk_per <= RGB514_clocks[i].limit) { |
| 155 | pll->ibm514.m = RGB514_clocks[i].m; | 154 | pll->ibm514.m = RGB514_clocks[i].m; |
| 156 | pll->ibm514.n = RGB514_clocks[i].n; | 155 | pll->ibm514.n = RGB514_clocks[i].n; |
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index c9f0c5a07e6e..9a6b5b39b88e 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c | |||
| @@ -1067,7 +1067,7 @@ static int radeon_setcolreg (unsigned regno, unsigned red, unsigned green, | |||
| 1067 | 1067 | ||
| 1068 | 1068 | ||
| 1069 | if (regno > 255) | 1069 | if (regno > 255) |
| 1070 | return 1; | 1070 | return -EINVAL; |
| 1071 | 1071 | ||
| 1072 | red >>= 8; | 1072 | red >>= 8; |
| 1073 | green >>= 8; | 1073 | green >>= 8; |
| @@ -1086,9 +1086,9 @@ static int radeon_setcolreg (unsigned regno, unsigned red, unsigned green, | |||
| 1086 | pindex = regno * 8; | 1086 | pindex = regno * 8; |
| 1087 | 1087 | ||
| 1088 | if (rinfo->depth == 16 && regno > 63) | 1088 | if (rinfo->depth == 16 && regno > 63) |
| 1089 | return 1; | 1089 | return -EINVAL; |
| 1090 | if (rinfo->depth == 15 && regno > 31) | 1090 | if (rinfo->depth == 15 && regno > 31) |
| 1091 | return 1; | 1091 | return -EINVAL; |
| 1092 | 1092 | ||
| 1093 | /* For 565, the green component is mixed one order | 1093 | /* For 565, the green component is mixed one order |
| 1094 | * below | 1094 | * below |
diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c new file mode 100644 index 000000000000..b367de30b98c --- /dev/null +++ b/drivers/video/au1200fb.c | |||
| @@ -0,0 +1,3844 @@ | |||
| 1 | /* | ||
| 2 | * BRIEF MODULE DESCRIPTION | ||
| 3 | * Au1200 LCD Driver. | ||
| 4 | * | ||
| 5 | * Copyright 2004-2005 AMD | ||
| 6 | * Author: AMD | ||
| 7 | * | ||
| 8 | * Based on: | ||
| 9 | * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device | ||
| 10 | * Created 28 Dec 1997 by Geert Uytterhoeven | ||
| 11 | * | ||
| 12 | * This program is free software; you can redistribute it and/or modify it | ||
| 13 | * under the terms of the GNU General Public License as published by the | ||
| 14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 15 | * option) any later version. | ||
| 16 | * | ||
| 17 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
| 20 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 23 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 24 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 27 | * | ||
| 28 | * You should have received a copy of the GNU General Public License along | ||
| 29 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 30 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 31 | */ | ||
| 32 | |||
| 33 | #include <linux/module.h> | ||
| 34 | #include <linux/platform_device.h> | ||
| 35 | #include <linux/kernel.h> | ||
| 36 | #include <linux/errno.h> | ||
| 37 | #include <linux/string.h> | ||
| 38 | #include <linux/mm.h> | ||
| 39 | #include <linux/fb.h> | ||
| 40 | #include <linux/init.h> | ||
| 41 | #include <linux/interrupt.h> | ||
| 42 | #include <linux/ctype.h> | ||
| 43 | #include <linux/dma-mapping.h> | ||
| 44 | |||
| 45 | #include <asm/mach-au1x00/au1000.h> | ||
| 46 | #include "au1200fb.h" | ||
| 47 | |||
| 48 | #ifdef CONFIG_PM | ||
| 49 | #include <asm/mach-au1x00/au1xxx_pm.h> | ||
| 50 | #endif | ||
| 51 | |||
| 52 | #ifndef CONFIG_FB_AU1200_DEVS | ||
| 53 | #define CONFIG_FB_AU1200_DEVS 4 | ||
| 54 | #endif | ||
| 55 | |||
| 56 | #define DRIVER_NAME "au1200fb" | ||
| 57 | #define DRIVER_DESC "LCD controller driver for AU1200 processors" | ||
| 58 | |||
| 59 | #define DEBUG 1 | ||
| 60 | |||
| 61 | #define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) | ||
| 62 | #define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) | ||
| 63 | #define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) | ||
| 64 | |||
| 65 | #if DEBUG | ||
| 66 | #define print_dbg(f, arg...) printk(KERN_DEBUG __FILE__ ": " f "\n", ## arg) | ||
| 67 | #else | ||
| 68 | #define print_dbg(f, arg...) do {} while (0) | ||
| 69 | #endif | ||
| 70 | |||
| 71 | |||
| 72 | #define AU1200_LCD_FB_IOCTL 0x46FF | ||
| 73 | |||
| 74 | #define AU1200_LCD_SET_SCREEN 1 | ||
| 75 | #define AU1200_LCD_GET_SCREEN 2 | ||
| 76 | #define AU1200_LCD_SET_WINDOW 3 | ||
| 77 | #define AU1200_LCD_GET_WINDOW 4 | ||
| 78 | #define AU1200_LCD_SET_PANEL 5 | ||
| 79 | #define AU1200_LCD_GET_PANEL 6 | ||
| 80 | |||
| 81 | #define SCREEN_SIZE (1<< 1) | ||
| 82 | #define SCREEN_BACKCOLOR (1<< 2) | ||
| 83 | #define SCREEN_BRIGHTNESS (1<< 3) | ||
| 84 | #define SCREEN_COLORKEY (1<< 4) | ||
| 85 | #define SCREEN_MASK (1<< 5) | ||
| 86 | |||
| 87 | struct au1200_lcd_global_regs_t { | ||
| 88 | unsigned int flags; | ||
| 89 | unsigned int xsize; | ||
| 90 | unsigned int ysize; | ||
| 91 | unsigned int backcolor; | ||
| 92 | unsigned int brightness; | ||
| 93 | unsigned int colorkey; | ||
| 94 | unsigned int mask; | ||
| 95 | unsigned int panel_choice; | ||
| 96 | char panel_desc[80]; | ||
| 97 | |||
| 98 | }; | ||
| 99 | |||
| 100 | #define WIN_POSITION (1<< 0) | ||
| 101 | #define WIN_ALPHA_COLOR (1<< 1) | ||
| 102 | #define WIN_ALPHA_MODE (1<< 2) | ||
| 103 | #define WIN_PRIORITY (1<< 3) | ||
| 104 | #define WIN_CHANNEL (1<< 4) | ||
| 105 | #define WIN_BUFFER_FORMAT (1<< 5) | ||
| 106 | #define WIN_COLOR_ORDER (1<< 6) | ||
| 107 | #define WIN_PIXEL_ORDER (1<< 7) | ||
| 108 | #define WIN_SIZE (1<< 8) | ||
| 109 | #define WIN_COLORKEY_MODE (1<< 9) | ||
| 110 | #define WIN_DOUBLE_BUFFER_MODE (1<< 10) | ||
| 111 | #define WIN_RAM_ARRAY_MODE (1<< 11) | ||
| 112 | #define WIN_BUFFER_SCALE (1<< 12) | ||
| 113 | #define WIN_ENABLE (1<< 13) | ||
| 114 | |||
| 115 | struct au1200_lcd_window_regs_t { | ||
| 116 | unsigned int flags; | ||
| 117 | unsigned int xpos; | ||
| 118 | unsigned int ypos; | ||
| 119 | unsigned int alpha_color; | ||
| 120 | unsigned int alpha_mode; | ||
| 121 | unsigned int priority; | ||
| 122 | unsigned int channel; | ||
| 123 | unsigned int buffer_format; | ||
| 124 | unsigned int color_order; | ||
| 125 | unsigned int pixel_order; | ||
| 126 | unsigned int xsize; | ||
| 127 | unsigned int ysize; | ||
| 128 | unsigned int colorkey_mode; | ||
| 129 | unsigned int double_buffer_mode; | ||
| 130 | unsigned int ram_array_mode; | ||
| 131 | unsigned int xscale; | ||
| 132 | unsigned int yscale; | ||
| 133 | unsigned int enable; | ||
| 134 | }; | ||
| 135 | |||
| 136 | |||
| 137 | struct au1200_lcd_iodata_t { | ||
| 138 | unsigned int subcmd; | ||
| 139 | struct au1200_lcd_global_regs_t global; | ||
| 140 | struct au1200_lcd_window_regs_t window; | ||
| 141 | }; | ||
| 142 | |||
| 143 | #if defined(__BIG_ENDIAN) | ||
| 144 | #define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_11 | ||
| 145 | #else | ||
| 146 | #define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_00 | ||
| 147 | #endif | ||
| 148 | #define LCD_CONTROL_DEFAULT_SBPPF LCD_CONTROL_SBPPF_565 | ||
| 149 | |||
| 150 | /* Private, per-framebuffer management information (independent of the panel itself) */ | ||
| 151 | struct au1200fb_device { | ||
| 152 | struct fb_info fb_info; /* FB driver info record */ | ||
| 153 | |||
| 154 | int plane; | ||
| 155 | unsigned char* fb_mem; /* FrameBuffer memory map */ | ||
| 156 | unsigned int fb_len; | ||
| 157 | dma_addr_t fb_phys; | ||
| 158 | }; | ||
| 159 | |||
| 160 | static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS]; | ||
| 161 | /********************************************************************/ | ||
| 162 | |||
| 163 | /* LCD controller restrictions */ | ||
| 164 | #define AU1200_LCD_MAX_XRES 1280 | ||
| 165 | #define AU1200_LCD_MAX_YRES 1024 | ||
| 166 | #define AU1200_LCD_MAX_BPP 32 | ||
| 167 | #define AU1200_LCD_MAX_CLK 96000000 /* fixme: this needs to go away ? */ | ||
| 168 | #define AU1200_LCD_NBR_PALETTE_ENTRIES 256 | ||
| 169 | |||
| 170 | /* Default number of visible screen buffer to allocate */ | ||
| 171 | #define AU1200FB_NBR_VIDEO_BUFFERS 1 | ||
| 172 | |||
| 173 | /********************************************************************/ | ||
| 174 | |||
| 175 | static struct au1200_lcd *lcd = (struct au1200_lcd *) AU1200_LCD_ADDR; | ||
| 176 | static int window_index = 2; /* default is zero */ | ||
| 177 | static int panel_index = 2; /* default is zero */ | ||
| 178 | static struct window_settings *win; | ||
| 179 | static struct panel_settings *panel; | ||
| 180 | static int noblanking = 1; | ||
| 181 | static int nohwcursor = 0; | ||
| 182 | |||
| 183 | struct window_settings { | ||
| 184 | unsigned char name[64]; | ||
| 185 | uint32 mode_backcolor; | ||
| 186 | uint32 mode_colorkey; | ||
| 187 | uint32 mode_colorkeymsk; | ||
| 188 | struct { | ||
| 189 | int xres; | ||
| 190 | int yres; | ||
| 191 | int xpos; | ||
| 192 | int ypos; | ||
| 193 | uint32 mode_winctrl1; /* winctrl1[FRM,CCO,PO,PIPE] */ | ||
| 194 | uint32 mode_winenable; | ||
| 195 | } w[4]; | ||
| 196 | }; | ||
| 197 | |||
| 198 | #if defined(__BIG_ENDIAN) | ||
| 199 | #define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_00 | ||
| 200 | #else | ||
| 201 | #define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_01 | ||
| 202 | #endif | ||
| 203 | |||
| 204 | extern int board_au1200fb_panel_init (void); | ||
| 205 | extern int board_au1200fb_panel_shutdown (void); | ||
| 206 | |||
| 207 | #ifdef CONFIG_PM | ||
| 208 | int au1200fb_pm_callback(au1xxx_power_dev_t *dev, | ||
| 209 | au1xxx_request_t request, void *data); | ||
| 210 | au1xxx_power_dev_t *LCD_pm_dev; | ||
| 211 | #endif | ||
| 212 | |||
| 213 | /* | ||
| 214 | * Default window configurations | ||
| 215 | */ | ||
| 216 | static struct window_settings windows[] = { | ||
| 217 | { /* Index 0 */ | ||
| 218 | "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", | ||
| 219 | /* mode_backcolor */ 0x006600ff, | ||
| 220 | /* mode_colorkey,msk*/ 0, 0, | ||
| 221 | { | ||
| 222 | { | ||
| 223 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 224 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 225 | LCD_WINCTRL1_PO_16BPP, | ||
| 226 | /* mode_winenable*/ LCD_WINENABLE_WEN0, | ||
| 227 | }, | ||
| 228 | { | ||
| 229 | /* xres, yres, xpos, ypos */ 100, 100, 100, 100, | ||
| 230 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 231 | LCD_WINCTRL1_PO_16BPP | | ||
| 232 | LCD_WINCTRL1_PIPE, | ||
| 233 | /* mode_winenable*/ LCD_WINENABLE_WEN1, | ||
| 234 | }, | ||
| 235 | { | ||
| 236 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 237 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 238 | LCD_WINCTRL1_PO_16BPP, | ||
| 239 | /* mode_winenable*/ 0, | ||
| 240 | }, | ||
| 241 | { | ||
| 242 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 243 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 244 | LCD_WINCTRL1_PO_16BPP | | ||
| 245 | LCD_WINCTRL1_PIPE, | ||
| 246 | /* mode_winenable*/ 0, | ||
| 247 | }, | ||
| 248 | }, | ||
| 249 | }, | ||
| 250 | |||
| 251 | { /* Index 1 */ | ||
| 252 | "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", | ||
| 253 | /* mode_backcolor */ 0x006600ff, | ||
| 254 | /* mode_colorkey,msk*/ 0, 0, | ||
| 255 | { | ||
| 256 | { | ||
| 257 | /* xres, yres, xpos, ypos */ 320, 240, 5, 5, | ||
| 258 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_24BPP | | ||
| 259 | LCD_WINCTRL1_PO_00, | ||
| 260 | /* mode_winenable*/ LCD_WINENABLE_WEN0, | ||
| 261 | }, | ||
| 262 | { | ||
| 263 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 264 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | ||
| 265 | | LCD_WINCTRL1_PO_16BPP, | ||
| 266 | /* mode_winenable*/ 0, | ||
| 267 | }, | ||
| 268 | { | ||
| 269 | /* xres, yres, xpos, ypos */ 100, 100, 0, 0, | ||
| 270 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 271 | LCD_WINCTRL1_PO_16BPP | | ||
| 272 | LCD_WINCTRL1_PIPE, | ||
| 273 | /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, | ||
| 274 | }, | ||
| 275 | { | ||
| 276 | /* xres, yres, xpos, ypos */ 200, 25, 0, 0, | ||
| 277 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 278 | LCD_WINCTRL1_PO_16BPP | | ||
| 279 | LCD_WINCTRL1_PIPE, | ||
| 280 | /* mode_winenable*/ 0, | ||
| 281 | }, | ||
| 282 | }, | ||
| 283 | }, | ||
| 284 | { /* Index 2 */ | ||
| 285 | "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", | ||
| 286 | /* mode_backcolor */ 0x006600ff, | ||
| 287 | /* mode_colorkey,msk*/ 0, 0, | ||
| 288 | { | ||
| 289 | { | ||
| 290 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 291 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 292 | LCD_WINCTRL1_PO_16BPP, | ||
| 293 | /* mode_winenable*/ LCD_WINENABLE_WEN0, | ||
| 294 | }, | ||
| 295 | { | ||
| 296 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 297 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 298 | LCD_WINCTRL1_PO_16BPP, | ||
| 299 | /* mode_winenable*/ 0, | ||
| 300 | }, | ||
| 301 | { | ||
| 302 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 303 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_32BPP | | ||
| 304 | LCD_WINCTRL1_PO_00|LCD_WINCTRL1_PIPE, | ||
| 305 | /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, | ||
| 306 | }, | ||
| 307 | { | ||
| 308 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 309 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 310 | LCD_WINCTRL1_PO_16BPP | | ||
| 311 | LCD_WINCTRL1_PIPE, | ||
| 312 | /* mode_winenable*/ 0, | ||
| 313 | }, | ||
| 314 | }, | ||
| 315 | }, | ||
| 316 | /* Need VGA 640 @ 24bpp, @ 32bpp */ | ||
| 317 | /* Need VGA 800 @ 24bpp, @ 32bpp */ | ||
| 318 | /* Need VGA 1024 @ 24bpp, @ 32bpp */ | ||
| 319 | }; | ||
| 320 | |||
| 321 | /* | ||
| 322 | * Controller configurations for various panels. | ||
| 323 | */ | ||
| 324 | |||
| 325 | struct panel_settings | ||
| 326 | { | ||
| 327 | const char name[25]; /* Full name <vendor>_<model> */ | ||
| 328 | |||
| 329 | struct fb_monspecs monspecs; /* FB monitor specs */ | ||
| 330 | |||
| 331 | /* panel timings */ | ||
| 332 | uint32 mode_screen; | ||
| 333 | uint32 mode_horztiming; | ||
| 334 | uint32 mode_verttiming; | ||
| 335 | uint32 mode_clkcontrol; | ||
| 336 | uint32 mode_pwmdiv; | ||
| 337 | uint32 mode_pwmhi; | ||
| 338 | uint32 mode_outmask; | ||
| 339 | uint32 mode_fifoctrl; | ||
| 340 | uint32 mode_toyclksrc; | ||
| 341 | uint32 mode_backlight; | ||
| 342 | uint32 mode_auxpll; | ||
| 343 | int (*device_init)(void); | ||
| 344 | int (*device_shutdown)(void); | ||
| 345 | #define Xres min_xres | ||
| 346 | #define Yres min_yres | ||
| 347 | u32 min_xres; /* Minimum horizontal resolution */ | ||
| 348 | u32 max_xres; /* Maximum horizontal resolution */ | ||
| 349 | u32 min_yres; /* Minimum vertical resolution */ | ||
| 350 | u32 max_yres; /* Maximum vertical resolution */ | ||
| 351 | }; | ||
| 352 | |||
| 353 | /********************************************************************/ | ||
| 354 | /* fixme: Maybe a modedb for the CRT ? otherwise panels should be as-is */ | ||
| 355 | |||
| 356 | /* List of panels known to work with the AU1200 LCD controller. | ||
| 357 | * To add a new panel, enter the same specifications as the | ||
| 358 | * Generic_TFT one, and MAKE SURE that it doesn't conflicts | ||
| 359 | * with the controller restrictions. Restrictions are: | ||
| 360 | * | ||
| 361 | * STN color panels: max_bpp <= 12 | ||
| 362 | * STN mono panels: max_bpp <= 4 | ||
| 363 | * TFT panels: max_bpp <= 16 | ||
| 364 | * max_xres <= 800 | ||
| 365 | * max_yres <= 600 | ||
| 366 | */ | ||
| 367 | static struct panel_settings known_lcd_panels[] = | ||
| 368 | { | ||
| 369 | [0] = { /* QVGA 320x240 H:33.3kHz V:110Hz */ | ||
| 370 | .name = "QVGA_320x240", | ||
| 371 | .monspecs = { | ||
| 372 | .modedb = NULL, | ||
| 373 | .modedb_len = 0, | ||
| 374 | .hfmin = 30000, | ||
| 375 | .hfmax = 70000, | ||
| 376 | .vfmin = 60, | ||
| 377 | .vfmax = 60, | ||
| 378 | .dclkmin = 6000000, | ||
| 379 | .dclkmax = 28000000, | ||
| 380 | .input = FB_DISP_RGB, | ||
| 381 | }, | ||
| 382 | .mode_screen = LCD_SCREEN_SX_N(320) | | ||
| 383 | LCD_SCREEN_SY_N(240), | ||
| 384 | .mode_horztiming = 0x00c4623b, | ||
| 385 | .mode_verttiming = 0x00502814, | ||
| 386 | .mode_clkcontrol = 0x00020002, /* /4=24Mhz */ | ||
| 387 | .mode_pwmdiv = 0x00000000, | ||
| 388 | .mode_pwmhi = 0x00000000, | ||
| 389 | .mode_outmask = 0x00FFFFFF, | ||
| 390 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 391 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 392 | .mode_backlight = 0x00000000, | ||
| 393 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 394 | .device_init = NULL, | ||
| 395 | .device_shutdown = NULL, | ||
| 396 | 320, 320, | ||
| 397 | 240, 240, | ||
| 398 | }, | ||
| 399 | |||
| 400 | [1] = { /* VGA 640x480 H:30.3kHz V:58Hz */ | ||
| 401 | .name = "VGA_640x480", | ||
| 402 | .monspecs = { | ||
| 403 | .modedb = NULL, | ||
| 404 | .modedb_len = 0, | ||
| 405 | .hfmin = 30000, | ||
| 406 | .hfmax = 70000, | ||
| 407 | .vfmin = 60, | ||
| 408 | .vfmax = 60, | ||
| 409 | .dclkmin = 6000000, | ||
| 410 | .dclkmax = 28000000, | ||
| 411 | .input = FB_DISP_RGB, | ||
| 412 | }, | ||
| 413 | .mode_screen = 0x13f9df80, | ||
| 414 | .mode_horztiming = 0x003c5859, | ||
| 415 | .mode_verttiming = 0x00741201, | ||
| 416 | .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ | ||
| 417 | .mode_pwmdiv = 0x00000000, | ||
| 418 | .mode_pwmhi = 0x00000000, | ||
| 419 | .mode_outmask = 0x00FFFFFF, | ||
| 420 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 421 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 422 | .mode_backlight = 0x00000000, | ||
| 423 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 424 | .device_init = NULL, | ||
| 425 | .device_shutdown = NULL, | ||
| 426 | 640, 480, | ||
| 427 | 640, 480, | ||
| 428 | }, | ||
| 429 | |||
| 430 | [2] = { /* SVGA 800x600 H:46.1kHz V:69Hz */ | ||
| 431 | .name = "SVGA_800x600", | ||
| 432 | .monspecs = { | ||
| 433 | .modedb = NULL, | ||
| 434 | .modedb_len = 0, | ||
| 435 | .hfmin = 30000, | ||
| 436 | .hfmax = 70000, | ||
| 437 | .vfmin = 60, | ||
| 438 | .vfmax = 60, | ||
| 439 | .dclkmin = 6000000, | ||
| 440 | .dclkmax = 28000000, | ||
| 441 | .input = FB_DISP_RGB, | ||
| 442 | }, | ||
| 443 | .mode_screen = 0x18fa5780, | ||
| 444 | .mode_horztiming = 0x00dc7e77, | ||
| 445 | .mode_verttiming = 0x00584805, | ||
| 446 | .mode_clkcontrol = 0x00020000, /* /2=48Mhz */ | ||
| 447 | .mode_pwmdiv = 0x00000000, | ||
| 448 | .mode_pwmhi = 0x00000000, | ||
| 449 | .mode_outmask = 0x00FFFFFF, | ||
| 450 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 451 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 452 | .mode_backlight = 0x00000000, | ||
| 453 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 454 | .device_init = NULL, | ||
| 455 | .device_shutdown = NULL, | ||
| 456 | 800, 800, | ||
| 457 | 600, 600, | ||
| 458 | }, | ||
| 459 | |||
| 460 | [3] = { /* XVGA 1024x768 H:56.2kHz V:70Hz */ | ||
| 461 | .name = "XVGA_1024x768", | ||
| 462 | .monspecs = { | ||
| 463 | .modedb = NULL, | ||
| 464 | .modedb_len = 0, | ||
| 465 | .hfmin = 30000, | ||
| 466 | .hfmax = 70000, | ||
| 467 | .vfmin = 60, | ||
| 468 | .vfmax = 60, | ||
| 469 | .dclkmin = 6000000, | ||
| 470 | .dclkmax = 28000000, | ||
| 471 | .input = FB_DISP_RGB, | ||
| 472 | }, | ||
| 473 | .mode_screen = 0x1ffaff80, | ||
| 474 | .mode_horztiming = 0x007d0e57, | ||
| 475 | .mode_verttiming = 0x00740a01, | ||
| 476 | .mode_clkcontrol = 0x000A0000, /* /1 */ | ||
| 477 | .mode_pwmdiv = 0x00000000, | ||
| 478 | .mode_pwmhi = 0x00000000, | ||
| 479 | .mode_outmask = 0x00FFFFFF, | ||
| 480 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 481 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 482 | .mode_backlight = 0x00000000, | ||
| 483 | .mode_auxpll = 6, /* 72MHz AUXPLL */ | ||
| 484 | .device_init = NULL, | ||
| 485 | .device_shutdown = NULL, | ||
| 486 | 1024, 1024, | ||
| 487 | 768, 768, | ||
| 488 | }, | ||
| 489 | |||
| 490 | [4] = { /* XVGA XVGA 1280x1024 H:68.5kHz V:65Hz */ | ||
| 491 | .name = "XVGA_1280x1024", | ||
| 492 | .monspecs = { | ||
| 493 | .modedb = NULL, | ||
| 494 | .modedb_len = 0, | ||
| 495 | .hfmin = 30000, | ||
| 496 | .hfmax = 70000, | ||
| 497 | .vfmin = 60, | ||
| 498 | .vfmax = 60, | ||
| 499 | .dclkmin = 6000000, | ||
| 500 | .dclkmax = 28000000, | ||
| 501 | .input = FB_DISP_RGB, | ||
| 502 | }, | ||
| 503 | .mode_screen = 0x27fbff80, | ||
| 504 | .mode_horztiming = 0x00cdb2c7, | ||
| 505 | .mode_verttiming = 0x00600002, | ||
| 506 | .mode_clkcontrol = 0x000A0000, /* /1 */ | ||
| 507 | .mode_pwmdiv = 0x00000000, | ||
| 508 | .mode_pwmhi = 0x00000000, | ||
| 509 | .mode_outmask = 0x00FFFFFF, | ||
| 510 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 511 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 512 | .mode_backlight = 0x00000000, | ||
| 513 | .mode_auxpll = 10, /* 120MHz AUXPLL */ | ||
| 514 | .device_init = NULL, | ||
| 515 | .device_shutdown = NULL, | ||
| 516 | 1280, 1280, | ||
| 517 | 1024, 1024, | ||
| 518 | }, | ||
| 519 | |||
| 520 | [5] = { /* Samsung 1024x768 TFT */ | ||
| 521 | .name = "Samsung_1024x768_TFT", | ||
| 522 | .monspecs = { | ||
| 523 | .modedb = NULL, | ||
| 524 | .modedb_len = 0, | ||
| 525 | .hfmin = 30000, | ||
| 526 | .hfmax = 70000, | ||
| 527 | .vfmin = 60, | ||
| 528 | .vfmax = 60, | ||
| 529 | .dclkmin = 6000000, | ||
| 530 | .dclkmax = 28000000, | ||
| 531 | .input = FB_DISP_RGB, | ||
| 532 | }, | ||
| 533 | .mode_screen = 0x1ffaff80, | ||
| 534 | .mode_horztiming = 0x018cc677, | ||
| 535 | .mode_verttiming = 0x00241217, | ||
| 536 | .mode_clkcontrol = 0x00000000, /* SCB 0x1 /4=24Mhz */ | ||
| 537 | .mode_pwmdiv = 0x8000063f, /* SCB 0x0 */ | ||
| 538 | .mode_pwmhi = 0x03400000, /* SCB 0x0 */ | ||
| 539 | .mode_outmask = 0x00FFFFFF, | ||
| 540 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 541 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 542 | .mode_backlight = 0x00000000, | ||
| 543 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 544 | .device_init = board_au1200fb_panel_init, | ||
| 545 | .device_shutdown = board_au1200fb_panel_shutdown, | ||
| 546 | 1024, 1024, | ||
| 547 | 768, 768, | ||
| 548 | }, | ||
| 549 | |||
| 550 | [6] = { /* Toshiba 640x480 TFT */ | ||
| 551 | .name = "Toshiba_640x480_TFT", | ||
| 552 | .monspecs = { | ||
| 553 | .modedb = NULL, | ||
| 554 | .modedb_len = 0, | ||
| 555 | .hfmin = 30000, | ||
| 556 | .hfmax = 70000, | ||
| 557 | .vfmin = 60, | ||
| 558 | .vfmax = 60, | ||
| 559 | .dclkmin = 6000000, | ||
| 560 | .dclkmax = 28000000, | ||
| 561 | .input = FB_DISP_RGB, | ||
| 562 | }, | ||
| 563 | .mode_screen = LCD_SCREEN_SX_N(640) | | ||
| 564 | LCD_SCREEN_SY_N(480), | ||
| 565 | .mode_horztiming = LCD_HORZTIMING_HPW_N(96) | | ||
| 566 | LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(51), | ||
| 567 | .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | | ||
| 568 | LCD_VERTTIMING_VND1_N(11) | LCD_VERTTIMING_VND2_N(32), | ||
| 569 | .mode_clkcontrol = 0x00000000, /* /4=24Mhz */ | ||
| 570 | .mode_pwmdiv = 0x8000063f, | ||
| 571 | .mode_pwmhi = 0x03400000, | ||
| 572 | .mode_outmask = 0x00fcfcfc, | ||
| 573 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 574 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 575 | .mode_backlight = 0x00000000, | ||
| 576 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 577 | .device_init = board_au1200fb_panel_init, | ||
| 578 | .device_shutdown = board_au1200fb_panel_shutdown, | ||
| 579 | 640, 480, | ||
| 580 | 640, 480, | ||
| 581 | }, | ||
| 582 | |||
| 583 | [7] = { /* Sharp 320x240 TFT */ | ||
| 584 | .name = "Sharp_320x240_TFT", | ||
| 585 | .monspecs = { | ||
| 586 | .modedb = NULL, | ||
| 587 | .modedb_len = 0, | ||
| 588 | .hfmin = 12500, | ||
| 589 | .hfmax = 20000, | ||
| 590 | .vfmin = 38, | ||
| 591 | .vfmax = 81, | ||
| 592 | .dclkmin = 4500000, | ||
| 593 | .dclkmax = 6800000, | ||
| 594 | .input = FB_DISP_RGB, | ||
| 595 | }, | ||
| 596 | .mode_screen = LCD_SCREEN_SX_N(320) | | ||
| 597 | LCD_SCREEN_SY_N(240), | ||
| 598 | .mode_horztiming = LCD_HORZTIMING_HPW_N(60) | | ||
| 599 | LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(2), | ||
| 600 | .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | | ||
| 601 | LCD_VERTTIMING_VND1_N(2) | LCD_VERTTIMING_VND2_N(5), | ||
| 602 | .mode_clkcontrol = LCD_CLKCONTROL_PCD_N(7), /*16=6Mhz*/ | ||
| 603 | .mode_pwmdiv = 0x8000063f, | ||
| 604 | .mode_pwmhi = 0x03400000, | ||
| 605 | .mode_outmask = 0x00fcfcfc, | ||
| 606 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 607 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 608 | .mode_backlight = 0x00000000, | ||
| 609 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 610 | .device_init = board_au1200fb_panel_init, | ||
| 611 | .device_shutdown = board_au1200fb_panel_shutdown, | ||
| 612 | 320, 320, | ||
| 613 | 240, 240, | ||
| 614 | }, | ||
| 615 | |||
| 616 | [8] = { /* Toppoly TD070WGCB2 7" 856x480 TFT */ | ||
| 617 | .name = "Toppoly_TD070WGCB2", | ||
| 618 | .monspecs = { | ||
| 619 | .modedb = NULL, | ||
| 620 | .modedb_len = 0, | ||
| 621 | .hfmin = 30000, | ||
| 622 | .hfmax = 70000, | ||
| 623 | .vfmin = 60, | ||
| 624 | .vfmax = 60, | ||
| 625 | .dclkmin = 6000000, | ||
| 626 | .dclkmax = 28000000, | ||
| 627 | .input = FB_DISP_RGB, | ||
| 628 | }, | ||
| 629 | .mode_screen = LCD_SCREEN_SX_N(856) | | ||
| 630 | LCD_SCREEN_SY_N(480), | ||
| 631 | .mode_horztiming = LCD_HORZTIMING_HND2_N(43) | | ||
| 632 | LCD_HORZTIMING_HND1_N(43) | LCD_HORZTIMING_HPW_N(114), | ||
| 633 | .mode_verttiming = LCD_VERTTIMING_VND2_N(20) | | ||
| 634 | LCD_VERTTIMING_VND1_N(21) | LCD_VERTTIMING_VPW_N(4), | ||
| 635 | .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ | ||
| 636 | .mode_pwmdiv = 0x8000063f, | ||
| 637 | .mode_pwmhi = 0x03400000, | ||
| 638 | .mode_outmask = 0x00fcfcfc, | ||
| 639 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 640 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 641 | .mode_backlight = 0x00000000, | ||
| 642 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 643 | .device_init = board_au1200fb_panel_init, | ||
| 644 | .device_shutdown = board_au1200fb_panel_shutdown, | ||
| 645 | 856, 856, | ||
| 646 | 480, 480, | ||
| 647 | }, | ||
| 648 | }; | ||
| 649 | |||
| 650 | #define NUM_PANELS (ARRAY_SIZE(known_lcd_panels)) | ||
| 651 | |||
| 652 | /********************************************************************/ | ||
| 653 | |||
| 654 | #ifdef CONFIG_PM | ||
| 655 | static int set_brightness(unsigned int brightness) | ||
| 656 | { | ||
| 657 | unsigned int hi1, divider; | ||
| 658 | |||
| 659 | /* limit brightness pwm duty to >= 30/1600 */ | ||
| 660 | if (brightness < 30) { | ||
| 661 | brightness = 30; | ||
| 662 | } | ||
| 663 | divider = (lcd->pwmdiv & 0x3FFFF) + 1; | ||
| 664 | hi1 = (lcd->pwmhi >> 16) + 1; | ||
| 665 | hi1 = (((brightness & 0xFF) + 1) * divider >> 8); | ||
| 666 | lcd->pwmhi &= 0xFFFF; | ||
| 667 | lcd->pwmhi |= (hi1 << 16); | ||
| 668 | |||
| 669 | return brightness; | ||
| 670 | } | ||
| 671 | #endif /* CONFIG_PM */ | ||
| 672 | |||
| 673 | static int winbpp (unsigned int winctrl1) | ||
| 674 | { | ||
| 675 | int bits = 0; | ||
| 676 | |||
| 677 | /* how many bits are needed for each pixel format */ | ||
| 678 | switch (winctrl1 & LCD_WINCTRL1_FRM) { | ||
| 679 | case LCD_WINCTRL1_FRM_1BPP: | ||
| 680 | bits = 1; | ||
| 681 | break; | ||
| 682 | case LCD_WINCTRL1_FRM_2BPP: | ||
| 683 | bits = 2; | ||
| 684 | break; | ||
| 685 | case LCD_WINCTRL1_FRM_4BPP: | ||
| 686 | bits = 4; | ||
| 687 | break; | ||
| 688 | case LCD_WINCTRL1_FRM_8BPP: | ||
| 689 | bits = 8; | ||
| 690 | break; | ||
| 691 | case LCD_WINCTRL1_FRM_12BPP: | ||
| 692 | case LCD_WINCTRL1_FRM_16BPP655: | ||
| 693 | case LCD_WINCTRL1_FRM_16BPP565: | ||
| 694 | case LCD_WINCTRL1_FRM_16BPP556: | ||
| 695 | case LCD_WINCTRL1_FRM_16BPPI1555: | ||
| 696 | case LCD_WINCTRL1_FRM_16BPPI5551: | ||
| 697 | case LCD_WINCTRL1_FRM_16BPPA1555: | ||
| 698 | case LCD_WINCTRL1_FRM_16BPPA5551: | ||
| 699 | bits = 16; | ||
| 700 | break; | ||
| 701 | case LCD_WINCTRL1_FRM_24BPP: | ||
| 702 | case LCD_WINCTRL1_FRM_32BPP: | ||
| 703 | bits = 32; | ||
| 704 | break; | ||
| 705 | } | ||
| 706 | |||
| 707 | return bits; | ||
| 708 | } | ||
| 709 | |||
| 710 | static int fbinfo2index (struct fb_info *fb_info) | ||
| 711 | { | ||
| 712 | int i; | ||
| 713 | |||
| 714 | for (i = 0; i < CONFIG_FB_AU1200_DEVS; ++i) { | ||
| 715 | if (fb_info == (struct fb_info *)(&_au1200fb_devices[i].fb_info)) | ||
| 716 | return i; | ||
| 717 | } | ||
| 718 | printk("au1200fb: ERROR: fbinfo2index failed!\n"); | ||
| 719 | return -1; | ||
| 720 | } | ||
| 721 | |||
| 722 | static int au1200_setlocation (struct au1200fb_device *fbdev, int plane, | ||
| 723 | int xpos, int ypos) | ||
| 724 | { | ||
| 725 | uint32 winctrl0, winctrl1, winenable, fb_offset = 0; | ||
| 726 | int xsz, ysz; | ||
| 727 | |||
| 728 | /* FIX!!! NOT CHECKING FOR COMPLETE OFFSCREEN YET */ | ||
| 729 | |||
| 730 | winctrl0 = lcd->window[plane].winctrl0; | ||
| 731 | winctrl1 = lcd->window[plane].winctrl1; | ||
| 732 | winctrl0 &= (LCD_WINCTRL0_A | LCD_WINCTRL0_AEN); | ||
| 733 | winctrl1 &= ~(LCD_WINCTRL1_SZX | LCD_WINCTRL1_SZY); | ||
| 734 | |||
| 735 | /* Check for off-screen adjustments */ | ||
| 736 | xsz = win->w[plane].xres; | ||
| 737 | ysz = win->w[plane].yres; | ||
| 738 | if ((xpos + win->w[plane].xres) > panel->Xres) { | ||
| 739 | /* Off-screen to the right */ | ||
| 740 | xsz = panel->Xres - xpos; /* off by 1 ??? */ | ||
| 741 | /*printk("off screen right\n");*/ | ||
| 742 | } | ||
| 743 | |||
| 744 | if ((ypos + win->w[plane].yres) > panel->Yres) { | ||
| 745 | /* Off-screen to the bottom */ | ||
| 746 | ysz = panel->Yres - ypos; /* off by 1 ??? */ | ||
| 747 | /*printk("off screen bottom\n");*/ | ||
| 748 | } | ||
| 749 | |||
| 750 | if (xpos < 0) { | ||
| 751 | /* Off-screen to the left */ | ||
| 752 | xsz = win->w[plane].xres + xpos; | ||
| 753 | fb_offset += (((0 - xpos) * winbpp(lcd->window[plane].winctrl1))/8); | ||
| 754 | xpos = 0; | ||
| 755 | /*printk("off screen left\n");*/ | ||
| 756 | } | ||
| 757 | |||
| 758 | if (ypos < 0) { | ||
| 759 | /* Off-screen to the top */ | ||
| 760 | ysz = win->w[plane].yres + ypos; | ||
| 761 | /* fixme: fb_offset += ((0-ypos)*fb_pars[plane].line_length); */ | ||
| 762 | ypos = 0; | ||
| 763 | /*printk("off screen top\n");*/ | ||
| 764 | } | ||
| 765 | |||
| 766 | /* record settings */ | ||
| 767 | win->w[plane].xpos = xpos; | ||
| 768 | win->w[plane].ypos = ypos; | ||
| 769 | |||
| 770 | xsz -= 1; | ||
| 771 | ysz -= 1; | ||
| 772 | winctrl0 |= (xpos << 21); | ||
| 773 | winctrl0 |= (ypos << 10); | ||
| 774 | winctrl1 |= (xsz << 11); | ||
| 775 | winctrl1 |= (ysz << 0); | ||
| 776 | |||
| 777 | /* Disable the window while making changes, then restore WINEN */ | ||
| 778 | winenable = lcd->winenable & (1 << plane); | ||
| 779 | au_sync(); | ||
| 780 | lcd->winenable &= ~(1 << plane); | ||
| 781 | lcd->window[plane].winctrl0 = winctrl0; | ||
| 782 | lcd->window[plane].winctrl1 = winctrl1; | ||
| 783 | lcd->window[plane].winbuf0 = | ||
| 784 | lcd->window[plane].winbuf1 = fbdev->fb_phys; | ||
| 785 | lcd->window[plane].winbufctrl = 0; /* select winbuf0 */ | ||
| 786 | lcd->winenable |= winenable; | ||
| 787 | au_sync(); | ||
| 788 | |||
| 789 | return 0; | ||
| 790 | } | ||
| 791 | |||
| 792 | static void au1200_setpanel (struct panel_settings *newpanel) | ||
| 793 | { | ||
| 794 | /* | ||
| 795 | * Perform global setup/init of LCD controller | ||
| 796 | */ | ||
| 797 | uint32 winenable; | ||
| 798 | |||
| 799 | /* Make sure all windows disabled */ | ||
| 800 | winenable = lcd->winenable; | ||
| 801 | lcd->winenable = 0; | ||
| 802 | au_sync(); | ||
| 803 | /* | ||
| 804 | * Ensure everything is disabled before reconfiguring | ||
| 805 | */ | ||
| 806 | if (lcd->screen & LCD_SCREEN_SEN) { | ||
| 807 | /* Wait for vertical sync period */ | ||
| 808 | lcd->intstatus = LCD_INT_SS; | ||
| 809 | while ((lcd->intstatus & LCD_INT_SS) == 0) { | ||
| 810 | au_sync(); | ||
| 811 | } | ||
| 812 | |||
| 813 | lcd->screen &= ~LCD_SCREEN_SEN; /*disable the controller*/ | ||
| 814 | |||
| 815 | do { | ||
| 816 | lcd->intstatus = lcd->intstatus; /*clear interrupts*/ | ||
| 817 | au_sync(); | ||
| 818 | /*wait for controller to shut down*/ | ||
| 819 | } while ((lcd->intstatus & LCD_INT_SD) == 0); | ||
| 820 | |||
| 821 | /* Call shutdown of current panel (if up) */ | ||
| 822 | /* this must occur last, because if an external clock is driving | ||
| 823 | the controller, the clock cannot be turned off before first | ||
| 824 | shutting down the controller. | ||
| 825 | */ | ||
| 826 | if (panel->device_shutdown != NULL) | ||
| 827 | panel->device_shutdown(); | ||
| 828 | } | ||
| 829 | |||
| 830 | /* Newpanel == NULL indicates a shutdown operation only */ | ||
| 831 | if (newpanel == NULL) | ||
| 832 | return; | ||
| 833 | |||
| 834 | panel = newpanel; | ||
| 835 | |||
| 836 | printk("Panel(%s), %dx%d\n", panel->name, panel->Xres, panel->Yres); | ||
| 837 | |||
| 838 | /* | ||
| 839 | * Setup clocking if internal LCD clock source (assumes sys_auxpll valid) | ||
| 840 | */ | ||
| 841 | if (!(panel->mode_clkcontrol & LCD_CLKCONTROL_EXT)) | ||
| 842 | { | ||
| 843 | uint32 sys_clksrc; | ||
| 844 | au_writel(panel->mode_auxpll, SYS_AUXPLL); | ||
| 845 | sys_clksrc = au_readl(SYS_CLKSRC) & ~0x0000001f; | ||
| 846 | sys_clksrc |= panel->mode_toyclksrc; | ||
| 847 | au_writel(sys_clksrc, SYS_CLKSRC); | ||
| 848 | } | ||
| 849 | |||
| 850 | /* | ||
| 851 | * Configure panel timings | ||
| 852 | */ | ||
| 853 | lcd->screen = panel->mode_screen; | ||
| 854 | lcd->horztiming = panel->mode_horztiming; | ||
| 855 | lcd->verttiming = panel->mode_verttiming; | ||
| 856 | lcd->clkcontrol = panel->mode_clkcontrol; | ||
| 857 | lcd->pwmdiv = panel->mode_pwmdiv; | ||
| 858 | lcd->pwmhi = panel->mode_pwmhi; | ||
| 859 | lcd->outmask = panel->mode_outmask; | ||
| 860 | lcd->fifoctrl = panel->mode_fifoctrl; | ||
| 861 | au_sync(); | ||
| 862 | |||
| 863 | /* fixme: Check window settings to make sure still valid | ||
| 864 | * for new geometry */ | ||
| 865 | #if 0 | ||
| 866 | au1200_setlocation(fbdev, 0, win->w[0].xpos, win->w[0].ypos); | ||
| 867 | au1200_setlocation(fbdev, 1, win->w[1].xpos, win->w[1].ypos); | ||
| 868 | au1200_setlocation(fbdev, 2, win->w[2].xpos, win->w[2].ypos); | ||
| 869 | au1200_setlocation(fbdev, 3, win->w[3].xpos, win->w[3].ypos); | ||
| 870 | #endif | ||
| 871 | lcd->winenable = winenable; | ||
| 872 | |||
| 873 | /* | ||
| 874 | * Re-enable screen now that it is configured | ||
| 875 | */ | ||
| 876 | lcd->screen |= LCD_SCREEN_SEN; | ||
| 877 | au_sync(); | ||
| 878 | |||
| 879 | /* Call init of panel */ | ||
| 880 | if (panel->device_init != NULL) panel->device_init(); | ||
| 881 | |||
| 882 | /* FIX!!!! not appropriate on panel change!!! Global setup/init */ | ||
| 883 | lcd->intenable = 0; | ||
| 884 | lcd->intstatus = ~0; | ||
| 885 | lcd->backcolor = win->mode_backcolor; | ||
| 886 | |||
| 887 | /* Setup Color Key - FIX!!! */ | ||
| 888 | lcd->colorkey = win->mode_colorkey; | ||
| 889 | lcd->colorkeymsk = win->mode_colorkeymsk; | ||
| 890 | |||
| 891 | /* Setup HWCursor - FIX!!! Need to support this eventually */ | ||
| 892 | lcd->hwc.cursorctrl = 0; | ||
| 893 | lcd->hwc.cursorpos = 0; | ||
| 894 | lcd->hwc.cursorcolor0 = 0; | ||
| 895 | lcd->hwc.cursorcolor1 = 0; | ||
| 896 | lcd->hwc.cursorcolor2 = 0; | ||
| 897 | lcd->hwc.cursorcolor3 = 0; | ||
| 898 | |||
| 899 | |||
| 900 | #if 0 | ||
| 901 | #define D(X) printk("%25s: %08X\n", #X, X) | ||
| 902 | D(lcd->screen); | ||
| 903 | D(lcd->horztiming); | ||
| 904 | D(lcd->verttiming); | ||
| 905 | D(lcd->clkcontrol); | ||
| 906 | D(lcd->pwmdiv); | ||
| 907 | D(lcd->pwmhi); | ||
| 908 | D(lcd->outmask); | ||
| 909 | D(lcd->fifoctrl); | ||
| 910 | D(lcd->window[0].winctrl0); | ||
| 911 | D(lcd->window[0].winctrl1); | ||
| 912 | D(lcd->window[0].winctrl2); | ||
| 913 | D(lcd->window[0].winbuf0); | ||
| 914 | D(lcd->window[0].winbuf1); | ||
| 915 | D(lcd->window[0].winbufctrl); | ||
| 916 | D(lcd->window[1].winctrl0); | ||
| 917 | D(lcd->window[1].winctrl1); | ||
| 918 | D(lcd->window[1].winctrl2); | ||
| 919 | D(lcd->window[1].winbuf0); | ||
| 920 | D(lcd->window[1].winbuf1); | ||
| 921 | D(lcd->window[1].winbufctrl); | ||
| 922 | D(lcd->window[2].winctrl0); | ||
| 923 | D(lcd->window[2].winctrl1); | ||
| 924 | D(lcd->window[2].winctrl2); | ||
| 925 | D(lcd->window[2].winbuf0); | ||
| 926 | D(lcd->window[2].winbuf1); | ||
| 927 | D(lcd->window[2].winbufctrl); | ||
| 928 | D(lcd->window[3].winctrl0); | ||
| 929 | D(lcd->window[3].winctrl1); | ||
| 930 | D(lcd->window[3].winctrl2); | ||
| 931 | D(lcd->window[3].winbuf0); | ||
| 932 | D(lcd->window[3].winbuf1); | ||
| 933 | D(lcd->window[3].winbufctrl); | ||
| 934 | D(lcd->winenable); | ||
| 935 | D(lcd->intenable); | ||
| 936 | D(lcd->intstatus); | ||
| 937 | D(lcd->backcolor); | ||
| 938 | D(lcd->winenable); | ||
| 939 | D(lcd->colorkey); | ||
| 940 | D(lcd->colorkeymsk); | ||
| 941 | D(lcd->hwc.cursorctrl); | ||
| 942 | D(lcd->hwc.cursorpos); | ||
| 943 | D(lcd->hwc.cursorcolor0); | ||
| 944 | D(lcd->hwc.cursorcolor1); | ||
| 945 | D(lcd->hwc.cursorcolor2); | ||
| 946 | D(lcd->hwc.cursorcolor3); | ||
| 947 | #endif | ||
| 948 | } | ||
| 949 | |||
| 950 | static void au1200_setmode(struct au1200fb_device *fbdev) | ||
| 951 | { | ||
| 952 | int plane = fbdev->plane; | ||
| 953 | /* Window/plane setup */ | ||
| 954 | lcd->window[plane].winctrl1 = ( 0 | ||
| 955 | | LCD_WINCTRL1_PRI_N(plane) | ||
| 956 | | win->w[plane].mode_winctrl1 /* FRM,CCO,PO,PIPE */ | ||
| 957 | ) ; | ||
| 958 | |||
| 959 | au1200_setlocation(fbdev, plane, win->w[plane].xpos, win->w[plane].ypos); | ||
| 960 | |||
| 961 | lcd->window[plane].winctrl2 = ( 0 | ||
| 962 | | LCD_WINCTRL2_CKMODE_00 | ||
| 963 | | LCD_WINCTRL2_DBM | ||
| 964 | | LCD_WINCTRL2_BX_N( fbdev->fb_info.fix.line_length) | ||
| 965 | | LCD_WINCTRL2_SCX_1 | ||
| 966 | | LCD_WINCTRL2_SCY_1 | ||
| 967 | ) ; | ||
| 968 | lcd->winenable |= win->w[plane].mode_winenable; | ||
| 969 | au_sync(); | ||
| 970 | } | ||
| 971 | |||
| 972 | |||
| 973 | /* Inline helpers */ | ||
| 974 | |||
| 975 | /*#define panel_is_dual(panel) ((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ | ||
| 976 | /*#define panel_is_active(panel)((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ | ||
| 977 | |||
| 978 | #define panel_is_color(panel) ((panel->mode_screen & LCD_SCREEN_PT) <= LCD_SCREEN_PT_CDSTN) | ||
| 979 | |||
| 980 | /* Bitfields format supported by the controller. */ | ||
| 981 | static struct fb_bitfield rgb_bitfields[][4] = { | ||
| 982 | /* Red, Green, Blue, Transp */ | ||
| 983 | [LCD_WINCTRL1_FRM_16BPP655 >> 25] = | ||
| 984 | { { 10, 6, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, | ||
| 985 | |||
| 986 | [LCD_WINCTRL1_FRM_16BPP565 >> 25] = | ||
| 987 | { { 11, 5, 0 }, { 5, 6, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, | ||
| 988 | |||
| 989 | [LCD_WINCTRL1_FRM_16BPP556 >> 25] = | ||
| 990 | { { 11, 5, 0 }, { 6, 5, 0 }, { 0, 6, 0 }, { 0, 0, 0 } }, | ||
| 991 | |||
| 992 | [LCD_WINCTRL1_FRM_16BPPI1555 >> 25] = | ||
| 993 | { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, | ||
| 994 | |||
| 995 | [LCD_WINCTRL1_FRM_16BPPI5551 >> 25] = | ||
| 996 | { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 0, 0 } }, | ||
| 997 | |||
| 998 | [LCD_WINCTRL1_FRM_16BPPA1555 >> 25] = | ||
| 999 | { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 15, 1, 0 } }, | ||
| 1000 | |||
| 1001 | [LCD_WINCTRL1_FRM_16BPPA5551 >> 25] = | ||
| 1002 | { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 1, 0 } }, | ||
| 1003 | |||
| 1004 | [LCD_WINCTRL1_FRM_24BPP >> 25] = | ||
| 1005 | { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 } }, | ||
| 1006 | |||
| 1007 | [LCD_WINCTRL1_FRM_32BPP >> 25] = | ||
| 1008 | { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 24, 0, 0 } }, | ||
| 1009 | }; | ||
| 1010 | |||
| 1011 | /*-------------------------------------------------------------------------*/ | ||
| 1012 | |||
| 1013 | /* Helpers */ | ||
| 1014 | |||
| 1015 | static void au1200fb_update_fbinfo(struct fb_info *fbi) | ||
| 1016 | { | ||
| 1017 | /* FIX!!!! This also needs to take the window pixel format into account!!! */ | ||
| 1018 | |||
| 1019 | /* Update var-dependent FB info */ | ||
| 1020 | if (panel_is_color(panel)) { | ||
| 1021 | if (fbi->var.bits_per_pixel <= 8) { | ||
| 1022 | /* palettized */ | ||
| 1023 | fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
| 1024 | fbi->fix.line_length = fbi->var.xres_virtual / | ||
| 1025 | (8/fbi->var.bits_per_pixel); | ||
| 1026 | } else { | ||
| 1027 | /* non-palettized */ | ||
| 1028 | fbi->fix.visual = FB_VISUAL_TRUECOLOR; | ||
| 1029 | fbi->fix.line_length = fbi->var.xres_virtual * (fbi->var.bits_per_pixel / 8); | ||
| 1030 | } | ||
| 1031 | } else { | ||
| 1032 | /* mono FIX!!! mono 8 and 4 bits */ | ||
| 1033 | fbi->fix.visual = FB_VISUAL_MONO10; | ||
| 1034 | fbi->fix.line_length = fbi->var.xres_virtual / 8; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | fbi->screen_size = fbi->fix.line_length * fbi->var.yres_virtual; | ||
| 1038 | print_dbg("line length: %d\n", fbi->fix.line_length); | ||
| 1039 | print_dbg("bits_per_pixel: %d\n", fbi->var.bits_per_pixel); | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | /*-------------------------------------------------------------------------*/ | ||
| 1043 | |||
| 1044 | /* AU1200 framebuffer driver */ | ||
| 1045 | |||
| 1046 | /* fb_check_var | ||
| 1047 | * Validate var settings with hardware restrictions and modify it if necessary | ||
| 1048 | */ | ||
| 1049 | static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, | ||
| 1050 | struct fb_info *fbi) | ||
| 1051 | { | ||
| 1052 | struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; | ||
| 1053 | u32 pixclock; | ||
| 1054 | int screen_size, plane; | ||
| 1055 | |||
| 1056 | plane = fbdev->plane; | ||
| 1057 | |||
| 1058 | /* Make sure that the mode respect all LCD controller and | ||
| 1059 | * panel restrictions. */ | ||
| 1060 | var->xres = win->w[plane].xres; | ||
| 1061 | var->yres = win->w[plane].yres; | ||
| 1062 | |||
| 1063 | /* No need for virtual resolution support */ | ||
| 1064 | var->xres_virtual = var->xres; | ||
| 1065 | var->yres_virtual = var->yres; | ||
| 1066 | |||
| 1067 | var->bits_per_pixel = winbpp(win->w[plane].mode_winctrl1); | ||
| 1068 | |||
| 1069 | screen_size = var->xres_virtual * var->yres_virtual; | ||
| 1070 | if (var->bits_per_pixel > 8) screen_size *= (var->bits_per_pixel / 8); | ||
| 1071 | else screen_size /= (8/var->bits_per_pixel); | ||
| 1072 | |||
| 1073 | if (fbdev->fb_len < screen_size) | ||
| 1074 | return -EINVAL; /* Virtual screen is to big, abort */ | ||
| 1075 | |||
| 1076 | /* FIX!!!! what are the implicaitons of ignoring this for windows ??? */ | ||
| 1077 | /* The max LCD clock is fixed to 48MHz (value of AUX_CLK). The pixel | ||
| 1078 | * clock can only be obtain by dividing this value by an even integer. | ||
| 1079 | * Fallback to a slower pixel clock if necessary. */ | ||
| 1080 | pixclock = max((u32)(PICOS2KHZ(var->pixclock) * 1000), fbi->monspecs.dclkmin); | ||
| 1081 | pixclock = min(pixclock, min(fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2)); | ||
| 1082 | |||
| 1083 | if (AU1200_LCD_MAX_CLK % pixclock) { | ||
| 1084 | int diff = AU1200_LCD_MAX_CLK % pixclock; | ||
| 1085 | pixclock -= diff; | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | var->pixclock = KHZ2PICOS(pixclock/1000); | ||
| 1089 | #if 0 | ||
| 1090 | if (!panel_is_active(panel)) { | ||
| 1091 | int pcd = AU1200_LCD_MAX_CLK / (pixclock * 2) - 1; | ||
| 1092 | |||
| 1093 | if (!panel_is_color(panel) | ||
| 1094 | && (panel->control_base & LCD_CONTROL_MPI) && (pcd < 3)) { | ||
| 1095 | /* STN 8bit mono panel support is up to 6MHz pixclock */ | ||
| 1096 | var->pixclock = KHZ2PICOS(6000); | ||
| 1097 | } else if (!pcd) { | ||
| 1098 | /* Other STN panel support is up to 12MHz */ | ||
| 1099 | var->pixclock = KHZ2PICOS(12000); | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | #endif | ||
| 1103 | /* Set bitfield accordingly */ | ||
| 1104 | switch (var->bits_per_pixel) { | ||
| 1105 | case 16: | ||
| 1106 | { | ||
| 1107 | /* 16bpp True color. | ||
| 1108 | * These must be set to MATCH WINCTRL[FORM] */ | ||
| 1109 | int idx; | ||
| 1110 | idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; | ||
| 1111 | var->red = rgb_bitfields[idx][0]; | ||
| 1112 | var->green = rgb_bitfields[idx][1]; | ||
| 1113 | var->blue = rgb_bitfields[idx][2]; | ||
| 1114 | var->transp = rgb_bitfields[idx][3]; | ||
| 1115 | break; | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | case 32: | ||
| 1119 | { | ||
| 1120 | /* 32bpp True color. | ||
| 1121 | * These must be set to MATCH WINCTRL[FORM] */ | ||
| 1122 | int idx; | ||
| 1123 | idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; | ||
| 1124 | var->red = rgb_bitfields[idx][0]; | ||
| 1125 | var->green = rgb_bitfields[idx][1]; | ||
| 1126 | var->blue = rgb_bitfields[idx][2]; | ||
| 1127 | var->transp = rgb_bitfields[idx][3]; | ||
| 1128 | break; | ||
| 1129 | } | ||
| 1130 | default: | ||
| 1131 | print_dbg("Unsupported depth %dbpp", var->bits_per_pixel); | ||
| 1132 | return -EINVAL; | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | return 0; | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | /* fb_set_par | ||
| 1139 | * Set hardware with var settings. This will enable the controller with a | ||
| 1140 | * specific mode, normally validated with the fb_check_var method | ||
| 1141 | */ | ||
| 1142 | static int au1200fb_fb_set_par(struct fb_info *fbi) | ||
| 1143 | { | ||
| 1144 | struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; | ||
| 1145 | |||
| 1146 | au1200fb_update_fbinfo(fbi); | ||
| 1147 | au1200_setmode(fbdev); | ||
| 1148 | |||
| 1149 | return 0; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | /* fb_setcolreg | ||
| 1153 | * Set color in LCD palette. | ||
| 1154 | */ | ||
| 1155 | static int au1200fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
| 1156 | unsigned blue, unsigned transp, struct fb_info *fbi) | ||
| 1157 | { | ||
| 1158 | volatile u32 *palette = lcd->palette; | ||
| 1159 | u32 value; | ||
| 1160 | |||
| 1161 | if (regno > (AU1200_LCD_NBR_PALETTE_ENTRIES - 1)) | ||
| 1162 | return -EINVAL; | ||
| 1163 | |||
| 1164 | if (fbi->var.grayscale) { | ||
| 1165 | /* Convert color to grayscale */ | ||
| 1166 | red = green = blue = | ||
| 1167 | (19595 * red + 38470 * green + 7471 * blue) >> 16; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) { | ||
| 1171 | /* Place color in the pseudopalette */ | ||
| 1172 | if (regno > 16) | ||
| 1173 | return -EINVAL; | ||
| 1174 | |||
| 1175 | palette = (u32*) fbi->pseudo_palette; | ||
| 1176 | |||
| 1177 | red >>= (16 - fbi->var.red.length); | ||
| 1178 | green >>= (16 - fbi->var.green.length); | ||
| 1179 | blue >>= (16 - fbi->var.blue.length); | ||
| 1180 | |||
| 1181 | value = (red << fbi->var.red.offset) | | ||
| 1182 | (green << fbi->var.green.offset)| | ||
| 1183 | (blue << fbi->var.blue.offset); | ||
| 1184 | value &= 0xFFFF; | ||
| 1185 | |||
| 1186 | } else if (1 /*FIX!!! panel_is_active(fbdev->panel)*/) { | ||
| 1187 | /* COLOR TFT PALLETTIZED (use RGB 565) */ | ||
| 1188 | value = (red & 0xF800)|((green >> 5) & | ||
| 1189 | 0x07E0)|((blue >> 11) & 0x001F); | ||
| 1190 | value &= 0xFFFF; | ||
| 1191 | |||
| 1192 | } else if (0 /*panel_is_color(fbdev->panel)*/) { | ||
| 1193 | /* COLOR STN MODE */ | ||
| 1194 | value = 0x1234; | ||
| 1195 | value &= 0xFFF; | ||
| 1196 | } else { | ||
| 1197 | /* MONOCHROME MODE */ | ||
| 1198 | value = (green >> 12) & 0x000F; | ||
| 1199 | value &= 0xF; | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | palette[regno] = value; | ||
| 1203 | |||
| 1204 | return 0; | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | /* fb_blank | ||
| 1208 | * Blank the screen. Depending on the mode, the screen will be | ||
| 1209 | * activated with the backlight color, or desactivated | ||
| 1210 | */ | ||
| 1211 | static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi) | ||
| 1212 | { | ||
| 1213 | /* Short-circuit screen blanking */ | ||
| 1214 | if (noblanking) | ||
| 1215 | return 0; | ||
| 1216 | |||
| 1217 | switch (blank_mode) { | ||
| 1218 | |||
| 1219 | case FB_BLANK_UNBLANK: | ||
| 1220 | case FB_BLANK_NORMAL: | ||
| 1221 | /* printk("turn on panel\n"); */ | ||
| 1222 | au1200_setpanel(panel); | ||
| 1223 | break; | ||
| 1224 | case FB_BLANK_VSYNC_SUSPEND: | ||
| 1225 | case FB_BLANK_HSYNC_SUSPEND: | ||
| 1226 | case FB_BLANK_POWERDOWN: | ||
| 1227 | /* printk("turn off panel\n"); */ | ||
| 1228 | au1200_setpanel(NULL); | ||
| 1229 | break; | ||
| 1230 | default: | ||
| 1231 | break; | ||
| 1232 | |||
| 1233 | } | ||
| 1234 | |||
| 1235 | /* FB_BLANK_NORMAL is a soft blank */ | ||
| 1236 | return (blank_mode == FB_BLANK_NORMAL) ? -EINVAL : 0; | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | /* fb_mmap | ||
| 1240 | * Map video memory in user space. We don't use the generic fb_mmap | ||
| 1241 | * method mainly to allow the use of the TLB streaming flag (CCA=6) | ||
| 1242 | */ | ||
| 1243 | static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) | ||
| 1244 | |||
| 1245 | { | ||
| 1246 | unsigned int len; | ||
| 1247 | unsigned long start=0, off; | ||
| 1248 | struct au1200fb_device *fbdev = (struct au1200fb_device *) info; | ||
| 1249 | |||
| 1250 | #ifdef CONFIG_PM | ||
| 1251 | au1xxx_pm_access(LCD_pm_dev); | ||
| 1252 | #endif | ||
| 1253 | |||
| 1254 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { | ||
| 1255 | return -EINVAL; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | start = fbdev->fb_phys & PAGE_MASK; | ||
| 1259 | len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len); | ||
| 1260 | |||
| 1261 | off = vma->vm_pgoff << PAGE_SHIFT; | ||
| 1262 | |||
| 1263 | if ((vma->vm_end - vma->vm_start + off) > len) { | ||
| 1264 | return -EINVAL; | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | off += start; | ||
| 1268 | vma->vm_pgoff = off >> PAGE_SHIFT; | ||
| 1269 | |||
| 1270 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
| 1271 | pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */ | ||
| 1272 | |||
| 1273 | vma->vm_flags |= VM_IO; | ||
| 1274 | |||
| 1275 | return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, | ||
| 1276 | vma->vm_end - vma->vm_start, | ||
| 1277 | vma->vm_page_prot); | ||
| 1278 | |||
| 1279 | return 0; | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) | ||
| 1283 | { | ||
| 1284 | |||
| 1285 | unsigned int hi1, divider; | ||
| 1286 | |||
| 1287 | /* SCREEN_SIZE: user cannot reset size, must switch panel choice */ | ||
| 1288 | |||
| 1289 | if (pdata->flags & SCREEN_BACKCOLOR) | ||
| 1290 | lcd->backcolor = pdata->backcolor; | ||
| 1291 | |||
| 1292 | if (pdata->flags & SCREEN_BRIGHTNESS) { | ||
| 1293 | |||
| 1294 | // limit brightness pwm duty to >= 30/1600 | ||
| 1295 | if (pdata->brightness < 30) { | ||
| 1296 | pdata->brightness = 30; | ||
| 1297 | } | ||
| 1298 | divider = (lcd->pwmdiv & 0x3FFFF) + 1; | ||
| 1299 | hi1 = (lcd->pwmhi >> 16) + 1; | ||
| 1300 | hi1 = (((pdata->brightness & 0xFF)+1) * divider >> 8); | ||
| 1301 | lcd->pwmhi &= 0xFFFF; | ||
| 1302 | lcd->pwmhi |= (hi1 << 16); | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | if (pdata->flags & SCREEN_COLORKEY) | ||
| 1306 | lcd->colorkey = pdata->colorkey; | ||
| 1307 | |||
| 1308 | if (pdata->flags & SCREEN_MASK) | ||
| 1309 | lcd->colorkeymsk = pdata->mask; | ||
| 1310 | au_sync(); | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | static void get_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) | ||
| 1314 | { | ||
| 1315 | unsigned int hi1, divider; | ||
| 1316 | |||
| 1317 | pdata->xsize = ((lcd->screen & LCD_SCREEN_SX) >> 19) + 1; | ||
| 1318 | pdata->ysize = ((lcd->screen & LCD_SCREEN_SY) >> 8) + 1; | ||
| 1319 | |||
| 1320 | pdata->backcolor = lcd->backcolor; | ||
| 1321 | pdata->colorkey = lcd->colorkey; | ||
| 1322 | pdata->mask = lcd->colorkeymsk; | ||
| 1323 | |||
| 1324 | // brightness | ||
| 1325 | hi1 = (lcd->pwmhi >> 16) + 1; | ||
| 1326 | divider = (lcd->pwmdiv & 0x3FFFF) + 1; | ||
| 1327 | pdata->brightness = ((hi1 << 8) / divider) - 1; | ||
| 1328 | au_sync(); | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | static void set_window(unsigned int plane, | ||
| 1332 | struct au1200_lcd_window_regs_t *pdata) | ||
| 1333 | { | ||
| 1334 | unsigned int val, bpp; | ||
| 1335 | |||
| 1336 | /* Window control register 0 */ | ||
| 1337 | if (pdata->flags & WIN_POSITION) { | ||
| 1338 | val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_OX | | ||
| 1339 | LCD_WINCTRL0_OY); | ||
| 1340 | val |= ((pdata->xpos << 21) & LCD_WINCTRL0_OX); | ||
| 1341 | val |= ((pdata->ypos << 10) & LCD_WINCTRL0_OY); | ||
| 1342 | lcd->window[plane].winctrl0 = val; | ||
| 1343 | } | ||
| 1344 | if (pdata->flags & WIN_ALPHA_COLOR) { | ||
| 1345 | val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_A); | ||
| 1346 | val |= ((pdata->alpha_color << 2) & LCD_WINCTRL0_A); | ||
| 1347 | lcd->window[plane].winctrl0 = val; | ||
| 1348 | } | ||
| 1349 | if (pdata->flags & WIN_ALPHA_MODE) { | ||
| 1350 | val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_AEN); | ||
| 1351 | val |= ((pdata->alpha_mode << 1) & LCD_WINCTRL0_AEN); | ||
| 1352 | lcd->window[plane].winctrl0 = val; | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | /* Window control register 1 */ | ||
| 1356 | if (pdata->flags & WIN_PRIORITY) { | ||
| 1357 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PRI); | ||
| 1358 | val |= ((pdata->priority << 30) & LCD_WINCTRL1_PRI); | ||
| 1359 | lcd->window[plane].winctrl1 = val; | ||
| 1360 | } | ||
| 1361 | if (pdata->flags & WIN_CHANNEL) { | ||
| 1362 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PIPE); | ||
| 1363 | val |= ((pdata->channel << 29) & LCD_WINCTRL1_PIPE); | ||
| 1364 | lcd->window[plane].winctrl1 = val; | ||
| 1365 | } | ||
| 1366 | if (pdata->flags & WIN_BUFFER_FORMAT) { | ||
| 1367 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_FRM); | ||
| 1368 | val |= ((pdata->buffer_format << 25) & LCD_WINCTRL1_FRM); | ||
| 1369 | lcd->window[plane].winctrl1 = val; | ||
| 1370 | } | ||
| 1371 | if (pdata->flags & WIN_COLOR_ORDER) { | ||
| 1372 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_CCO); | ||
| 1373 | val |= ((pdata->color_order << 24) & LCD_WINCTRL1_CCO); | ||
| 1374 | lcd->window[plane].winctrl1 = val; | ||
| 1375 | } | ||
| 1376 | if (pdata->flags & WIN_PIXEL_ORDER) { | ||
| 1377 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PO); | ||
| 1378 | val |= ((pdata->pixel_order << 22) & LCD_WINCTRL1_PO); | ||
| 1379 | lcd->window[plane].winctrl1 = val; | ||
| 1380 | } | ||
| 1381 | if (pdata->flags & WIN_SIZE) { | ||
| 1382 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_SZX | | ||
| 1383 | LCD_WINCTRL1_SZY); | ||
| 1384 | val |= (((pdata->xsize << 11) - 1) & LCD_WINCTRL1_SZX); | ||
| 1385 | val |= (((pdata->ysize) - 1) & LCD_WINCTRL1_SZY); | ||
| 1386 | lcd->window[plane].winctrl1 = val; | ||
| 1387 | /* program buffer line width */ | ||
| 1388 | bpp = winbpp(val) / 8; | ||
| 1389 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_BX); | ||
| 1390 | val |= (((pdata->xsize * bpp) << 8) & LCD_WINCTRL2_BX); | ||
| 1391 | lcd->window[plane].winctrl2 = val; | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | /* Window control register 2 */ | ||
| 1395 | if (pdata->flags & WIN_COLORKEY_MODE) { | ||
| 1396 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_CKMODE); | ||
| 1397 | val |= ((pdata->colorkey_mode << 24) & LCD_WINCTRL2_CKMODE); | ||
| 1398 | lcd->window[plane].winctrl2 = val; | ||
| 1399 | } | ||
| 1400 | if (pdata->flags & WIN_DOUBLE_BUFFER_MODE) { | ||
| 1401 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_DBM); | ||
| 1402 | val |= ((pdata->double_buffer_mode << 23) & LCD_WINCTRL2_DBM); | ||
| 1403 | lcd->window[plane].winctrl2 = val; | ||
| 1404 | } | ||
| 1405 | if (pdata->flags & WIN_RAM_ARRAY_MODE) { | ||
| 1406 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_RAM); | ||
| 1407 | val |= ((pdata->ram_array_mode << 21) & LCD_WINCTRL2_RAM); | ||
| 1408 | lcd->window[plane].winctrl2 = val; | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | /* Buffer line width programmed with WIN_SIZE */ | ||
| 1412 | |||
| 1413 | if (pdata->flags & WIN_BUFFER_SCALE) { | ||
| 1414 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_SCX | | ||
| 1415 | LCD_WINCTRL2_SCY); | ||
| 1416 | val |= ((pdata->xsize << 11) & LCD_WINCTRL2_SCX); | ||
| 1417 | val |= ((pdata->ysize) & LCD_WINCTRL2_SCY); | ||
| 1418 | lcd->window[plane].winctrl2 = val; | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | if (pdata->flags & WIN_ENABLE) { | ||
| 1422 | val = lcd->winenable; | ||
| 1423 | val &= ~(1<<plane); | ||
| 1424 | val |= (pdata->enable & 1) << plane; | ||
| 1425 | lcd->winenable = val; | ||
| 1426 | } | ||
| 1427 | au_sync(); | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | static void get_window(unsigned int plane, | ||
| 1431 | struct au1200_lcd_window_regs_t *pdata) | ||
| 1432 | { | ||
| 1433 | /* Window control register 0 */ | ||
| 1434 | pdata->xpos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OX) >> 21; | ||
| 1435 | pdata->ypos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OY) >> 10; | ||
| 1436 | pdata->alpha_color = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_A) >> 2; | ||
| 1437 | pdata->alpha_mode = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_AEN) >> 1; | ||
| 1438 | |||
| 1439 | /* Window control register 1 */ | ||
| 1440 | pdata->priority = (lcd->window[plane].winctrl1& LCD_WINCTRL1_PRI) >> 30; | ||
| 1441 | pdata->channel = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PIPE) >> 29; | ||
| 1442 | pdata->buffer_format = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_FRM) >> 25; | ||
| 1443 | pdata->color_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_CCO) >> 24; | ||
| 1444 | pdata->pixel_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PO) >> 22; | ||
| 1445 | pdata->xsize = ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZX) >> 11) + 1; | ||
| 1446 | pdata->ysize = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZY) + 1; | ||
| 1447 | |||
| 1448 | /* Window control register 2 */ | ||
| 1449 | pdata->colorkey_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_CKMODE) >> 24; | ||
| 1450 | pdata->double_buffer_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_DBM) >> 23; | ||
| 1451 | pdata->ram_array_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_RAM) >> 21; | ||
| 1452 | |||
| 1453 | pdata->enable = (lcd->winenable >> plane) & 1; | ||
| 1454 | au_sync(); | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd, | ||
| 1458 | unsigned long arg) | ||
| 1459 | { | ||
| 1460 | int plane; | ||
| 1461 | int val; | ||
| 1462 | |||
| 1463 | #ifdef CONFIG_PM | ||
| 1464 | au1xxx_pm_access(LCD_pm_dev); | ||
| 1465 | #endif | ||
| 1466 | |||
| 1467 | plane = fbinfo2index(info); | ||
| 1468 | print_dbg("au1200fb: ioctl %d on plane %d\n", cmd, plane); | ||
| 1469 | |||
| 1470 | if (cmd == AU1200_LCD_FB_IOCTL) { | ||
| 1471 | struct au1200_lcd_iodata_t iodata; | ||
| 1472 | |||
| 1473 | if (copy_from_user(&iodata, (void __user *) arg, sizeof(iodata))) | ||
| 1474 | return -EFAULT; | ||
| 1475 | |||
| 1476 | print_dbg("FB IOCTL called\n"); | ||
| 1477 | |||
| 1478 | switch (iodata.subcmd) { | ||
| 1479 | case AU1200_LCD_SET_SCREEN: | ||
| 1480 | print_dbg("AU1200_LCD_SET_SCREEN\n"); | ||
| 1481 | set_global(cmd, &iodata.global); | ||
| 1482 | break; | ||
| 1483 | |||
| 1484 | case AU1200_LCD_GET_SCREEN: | ||
| 1485 | print_dbg("AU1200_LCD_GET_SCREEN\n"); | ||
| 1486 | get_global(cmd, &iodata.global); | ||
| 1487 | break; | ||
| 1488 | |||
| 1489 | case AU1200_LCD_SET_WINDOW: | ||
| 1490 | print_dbg("AU1200_LCD_SET_WINDOW\n"); | ||
| 1491 | set_window(plane, &iodata.window); | ||
| 1492 | break; | ||
| 1493 | |||
| 1494 | case AU1200_LCD_GET_WINDOW: | ||
| 1495 | print_dbg("AU1200_LCD_GET_WINDOW\n"); | ||
| 1496 | get_window(plane, &iodata.window); | ||
| 1497 | break; | ||
| 1498 | |||
| 1499 | case AU1200_LCD_SET_PANEL: | ||
| 1500 | print_dbg("AU1200_LCD_SET_PANEL\n"); | ||
| 1501 | if ((iodata.global.panel_choice >= 0) && | ||
| 1502 | (iodata.global.panel_choice < | ||
| 1503 | NUM_PANELS)) | ||
| 1504 | { | ||
| 1505 | struct panel_settings *newpanel; | ||
| 1506 | panel_index = iodata.global.panel_choice; | ||
| 1507 | newpanel = &known_lcd_panels[panel_index]; | ||
| 1508 | au1200_setpanel(newpanel); | ||
| 1509 | } | ||
| 1510 | break; | ||
| 1511 | |||
| 1512 | case AU1200_LCD_GET_PANEL: | ||
| 1513 | print_dbg("AU1200_LCD_GET_PANEL\n"); | ||
| 1514 | iodata.global.panel_choice = panel_index; | ||
| 1515 | break; | ||
| 1516 | |||
| 1517 | default: | ||
| 1518 | return -EINVAL; | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | val = copy_to_user((void __user *) arg, &iodata, sizeof(iodata)); | ||
| 1522 | if (val) { | ||
| 1523 | print_dbg("error: could not copy %d bytes\n", val); | ||
| 1524 | return -EFAULT; | ||
| 1525 | } | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | return 0; | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | |||
| 1532 | static struct fb_ops au1200fb_fb_ops = { | ||
| 1533 | .owner = THIS_MODULE, | ||
| 1534 | .fb_check_var = au1200fb_fb_check_var, | ||
| 1535 | .fb_set_par = au1200fb_fb_set_par, | ||
| 1536 | .fb_setcolreg = au1200fb_fb_setcolreg, | ||
| 1537 | .fb_blank = au1200fb_fb_blank, | ||
| 1538 | .fb_fillrect = cfb_fillrect, | ||
| 1539 | .fb_copyarea = cfb_copyarea, | ||
| 1540 | .fb_imageblit = cfb_imageblit, | ||
| 1541 | .fb_sync = NULL, | ||
| 1542 | .fb_ioctl = au1200fb_ioctl, | ||
| 1543 | .fb_mmap = au1200fb_fb_mmap, | ||
| 1544 | }; | ||
| 1545 | |||
| 1546 | /*-------------------------------------------------------------------------*/ | ||
| 1547 | |||
| 1548 | static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id, struct pt_regs *regs) | ||
| 1549 | { | ||
| 1550 | /* Nothing to do for now, just clear any pending interrupt */ | ||
| 1551 | lcd->intstatus = lcd->intstatus; | ||
| 1552 | au_sync(); | ||
| 1553 | |||
| 1554 | return IRQ_HANDLED; | ||
| 1555 | } | ||
| 1556 | |||
| 1557 | /*-------------------------------------------------------------------------*/ | ||
| 1558 | |||
| 1559 | /* AU1200 LCD device probe helpers */ | ||
| 1560 | |||
| 1561 | static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) | ||
| 1562 | { | ||
| 1563 | struct fb_info *fbi = &fbdev->fb_info; | ||
| 1564 | int bpp; | ||
| 1565 | |||
| 1566 | memset(fbi, 0, sizeof(struct fb_info)); | ||
| 1567 | fbi->fbops = &au1200fb_fb_ops; | ||
| 1568 | |||
| 1569 | bpp = winbpp(win->w[fbdev->plane].mode_winctrl1); | ||
| 1570 | |||
| 1571 | /* Copy monitor specs from panel data */ | ||
| 1572 | /* fixme: we're setting up LCD controller windows, so these dont give a | ||
| 1573 | damn as to what the monitor specs are (the panel itself does, but that | ||
| 1574 | isnt done here...so maybe need a generic catchall monitor setting??? */ | ||
| 1575 | memcpy(&fbi->monspecs, &panel->monspecs, sizeof(struct fb_monspecs)); | ||
| 1576 | |||
| 1577 | /* We first try the user mode passed in argument. If that failed, | ||
| 1578 | * or if no one has been specified, we default to the first mode of the | ||
| 1579 | * panel list. Note that after this call, var data will be set */ | ||
| 1580 | if (!fb_find_mode(&fbi->var, | ||
| 1581 | fbi, | ||
| 1582 | NULL, /* drv_info.opt_mode, */ | ||
| 1583 | fbi->monspecs.modedb, | ||
| 1584 | fbi->monspecs.modedb_len, | ||
| 1585 | fbi->monspecs.modedb, | ||
| 1586 | bpp)) { | ||
| 1587 | |||
| 1588 | print_err("Cannot find valid mode for panel %s", panel->name); | ||
| 1589 | return -EFAULT; | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | fbi->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); | ||
| 1593 | if (!fbi->pseudo_palette) { | ||
| 1594 | return -ENOMEM; | ||
| 1595 | } | ||
| 1596 | memset(fbi->pseudo_palette, 0, sizeof(u32) * 16); | ||
| 1597 | |||
| 1598 | if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { | ||
| 1599 | print_err("Fail to allocate colormap (%d entries)", | ||
| 1600 | AU1200_LCD_NBR_PALETTE_ENTRIES); | ||
| 1601 | kfree(fbi->pseudo_palette); | ||
| 1602 | return -EFAULT; | ||
| 1603 | } | ||
| 1604 | |||
| 1605 | strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id)); | ||
| 1606 | fbi->fix.smem_start = fbdev->fb_phys; | ||
| 1607 | fbi->fix.smem_len = fbdev->fb_len; | ||
| 1608 | fbi->fix.type = FB_TYPE_PACKED_PIXELS; | ||
| 1609 | fbi->fix.xpanstep = 0; | ||
| 1610 | fbi->fix.ypanstep = 0; | ||
| 1611 | fbi->fix.mmio_start = 0; | ||
| 1612 | fbi->fix.mmio_len = 0; | ||
| 1613 | fbi->fix.accel = FB_ACCEL_NONE; | ||
| 1614 | |||
| 1615 | fbi->screen_base = (char __iomem *) fbdev->fb_mem; | ||
| 1616 | |||
| 1617 | au1200fb_update_fbinfo(fbi); | ||
| 1618 | |||
| 1619 | return 0; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | /*-------------------------------------------------------------------------*/ | ||
| 1623 | |||
| 1624 | /* AU1200 LCD controller device driver */ | ||
| 1625 | |||
| 1626 | static int au1200fb_drv_probe(struct device *dev) | ||
| 1627 | { | ||
| 1628 | struct au1200fb_device *fbdev; | ||
| 1629 | unsigned long page; | ||
| 1630 | int bpp, plane, ret; | ||
| 1631 | |||
| 1632 | if (!dev) | ||
| 1633 | return -EINVAL; | ||
| 1634 | |||
| 1635 | for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { | ||
| 1636 | bpp = winbpp(win->w[plane].mode_winctrl1); | ||
| 1637 | if (win->w[plane].xres == 0) | ||
| 1638 | win->w[plane].xres = panel->Xres; | ||
| 1639 | if (win->w[plane].yres == 0) | ||
| 1640 | win->w[plane].yres = panel->Yres; | ||
| 1641 | |||
| 1642 | fbdev = &_au1200fb_devices[plane]; | ||
| 1643 | memset(fbdev, 0, sizeof(struct au1200fb_device)); | ||
| 1644 | fbdev->plane = plane; | ||
| 1645 | |||
| 1646 | /* Allocate the framebuffer to the maximum screen size */ | ||
| 1647 | fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8; | ||
| 1648 | |||
| 1649 | fbdev->fb_mem = dma_alloc_noncoherent(dev, | ||
| 1650 | PAGE_ALIGN(fbdev->fb_len), | ||
| 1651 | &fbdev->fb_phys, GFP_KERNEL); | ||
| 1652 | if (!fbdev->fb_mem) { | ||
| 1653 | print_err("fail to allocate frambuffer (size: %dK))", | ||
| 1654 | fbdev->fb_len / 1024); | ||
| 1655 | return -ENOMEM; | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | /* | ||
| 1659 | * Set page reserved so that mmap will work. This is necessary | ||
| 1660 | * since we'll be remapping normal memory. | ||
| 1661 | */ | ||
| 1662 | for (page = (unsigned long)fbdev->fb_phys; | ||
| 1663 | page < PAGE_ALIGN((unsigned long)fbdev->fb_phys + | ||
| 1664 | fbdev->fb_len); | ||
| 1665 | page += PAGE_SIZE) { | ||
| 1666 | SetPageReserved(pfn_to_page(page >> PAGE_SHIFT)); /* LCD DMA is NOT coherent on Au1200 */ | ||
| 1667 | } | ||
| 1668 | print_dbg("Framebuffer memory map at %p", fbdev->fb_mem); | ||
| 1669 | print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); | ||
| 1670 | |||
| 1671 | /* Init FB data */ | ||
| 1672 | if ((ret = au1200fb_init_fbinfo(fbdev)) < 0) | ||
| 1673 | goto failed; | ||
| 1674 | |||
| 1675 | /* Register new framebuffer */ | ||
| 1676 | if ((ret = register_framebuffer(&fbdev->fb_info)) < 0) { | ||
| 1677 | print_err("cannot register new framebuffer"); | ||
| 1678 | goto failed; | ||
| 1679 | } | ||
| 1680 | |||
| 1681 | au1200fb_fb_set_par(&fbdev->fb_info); | ||
| 1682 | |||
| 1683 | #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) | ||
| 1684 | if (plane == 0) | ||
| 1685 | if (fb_prepare_logo(&fbdev->fb_info, FB_ROTATE_UR)) { | ||
| 1686 | /* Start display and show logo on boot */ | ||
| 1687 | fb_set_cmap(&fbdev->fb_info.cmap, | ||
| 1688 | &fbdev->fb_info); | ||
| 1689 | |||
| 1690 | fb_show_logo(&fbdev->fb_info, FB_ROTATE_UR); | ||
| 1691 | } | ||
| 1692 | #endif | ||
| 1693 | } | ||
| 1694 | |||
| 1695 | /* Now hook interrupt too */ | ||
| 1696 | if ((ret = request_irq(AU1200_LCD_INT, au1200fb_handle_irq, | ||
| 1697 | SA_INTERRUPT | SA_SHIRQ, "lcd", (void *)dev)) < 0) { | ||
| 1698 | print_err("fail to request interrupt line %d (err: %d)", | ||
| 1699 | AU1200_LCD_INT, ret); | ||
| 1700 | goto failed; | ||
| 1701 | } | ||
| 1702 | |||
| 1703 | return 0; | ||
| 1704 | |||
| 1705 | failed: | ||
| 1706 | /* NOTE: This only does the current plane/window that failed; others are still active */ | ||
| 1707 | if (fbdev->fb_mem) | ||
| 1708 | dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), | ||
| 1709 | fbdev->fb_mem, fbdev->fb_phys); | ||
| 1710 | if (fbdev->fb_info.cmap.len != 0) | ||
| 1711 | fb_dealloc_cmap(&fbdev->fb_info.cmap); | ||
| 1712 | if (fbdev->fb_info.pseudo_palette) | ||
| 1713 | kfree(fbdev->fb_info.pseudo_palette); | ||
| 1714 | if (plane == 0) | ||
| 1715 | free_irq(AU1200_LCD_INT, (void*)dev); | ||
| 1716 | return ret; | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | static int au1200fb_drv_remove(struct device *dev) | ||
| 1720 | { | ||
| 1721 | struct au1200fb_device *fbdev; | ||
| 1722 | int plane; | ||
| 1723 | |||
| 1724 | if (!dev) | ||
| 1725 | return -ENODEV; | ||
| 1726 | |||
| 1727 | /* Turn off the panel */ | ||
| 1728 | au1200_setpanel(NULL); | ||
| 1729 | |||
| 1730 | for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) | ||
| 1731 | { | ||
| 1732 | fbdev = &_au1200fb_devices[plane]; | ||
| 1733 | |||
| 1734 | /* Clean up all probe data */ | ||
| 1735 | unregister_framebuffer(&fbdev->fb_info); | ||
| 1736 | if (fbdev->fb_mem) | ||
| 1737 | dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), | ||
| 1738 | fbdev->fb_mem, fbdev->fb_phys); | ||
| 1739 | if (fbdev->fb_info.cmap.len != 0) | ||
| 1740 | fb_dealloc_cmap(&fbdev->fb_info.cmap); | ||
| 1741 | if (fbdev->fb_info.pseudo_palette) | ||
| 1742 | kfree(fbdev->fb_info.pseudo_palette); | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | free_irq(AU1200_LCD_INT, (void *)dev); | ||
| 1746 | |||
| 1747 | return 0; | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | #ifdef CONFIG_PM | ||
| 1751 | static int au1200fb_drv_suspend(struct device *dev, u32 state, u32 level) | ||
| 1752 | { | ||
| 1753 | /* TODO */ | ||
| 1754 | return 0; | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | static int au1200fb_drv_resume(struct device *dev, u32 level) | ||
| 1758 | { | ||
| 1759 | /* TODO */ | ||
| 1760 | return 0; | ||
| 1761 | } | ||
| 1762 | #endif /* CONFIG_PM */ | ||
| 1763 | |||
| 1764 | static struct device_driver au1200fb_driver = { | ||
| 1765 | .name = "au1200-lcd", | ||
| 1766 | .bus = &platform_bus_type, | ||
| 1767 | .probe = au1200fb_drv_probe, | ||
| 1768 | .remove = au1200fb_drv_remove, | ||
| 1769 | #ifdef CONFIG_PM | ||
| 1770 | .suspend = au1200fb_drv_suspend, | ||
| 1771 | .resume = au1200fb_drv_resume, | ||
| 1772 | #endif | ||
| 1773 | }; | ||
| 1774 | |||
| 1775 | /*-------------------------------------------------------------------------*/ | ||
| 1776 | |||
| 1777 | /* Kernel driver */ | ||
| 1778 | |||
| 1779 | static void au1200fb_setup(void) | ||
| 1780 | { | ||
| 1781 | char* options = NULL; | ||
| 1782 | char* this_opt; | ||
| 1783 | int num_panels = ARRAY_SIZE(known_lcd_panels); | ||
| 1784 | int panel_idx = -1; | ||
| 1785 | |||
| 1786 | fb_get_options(DRIVER_NAME, &options); | ||
| 1787 | |||
| 1788 | if (options) { | ||
| 1789 | while ((this_opt = strsep(&options,",")) != NULL) { | ||
| 1790 | /* Panel option - can be panel name, | ||
| 1791 | * "bs" for board-switch, or number/index */ | ||
| 1792 | if (!strncmp(this_opt, "panel:", 6)) { | ||
| 1793 | int i; | ||
| 1794 | long int li; | ||
| 1795 | char *endptr; | ||
| 1796 | this_opt += 6; | ||
| 1797 | /* First check for index, which allows | ||
| 1798 | * to short circuit this mess */ | ||
| 1799 | li = simple_strtol(this_opt, &endptr, 0); | ||
| 1800 | if (*endptr == '\0') { | ||
| 1801 | panel_idx = (int)li; | ||
| 1802 | } | ||
| 1803 | else if (strcmp(this_opt, "bs") == 0) { | ||
| 1804 | extern int board_au1200fb_panel(void); | ||
| 1805 | panel_idx = board_au1200fb_panel(); | ||
| 1806 | } | ||
| 1807 | |||
| 1808 | else | ||
| 1809 | for (i = 0; i < num_panels; i++) { | ||
| 1810 | if (!strcmp(this_opt, known_lcd_panels[i].name)) { | ||
| 1811 | panel_idx = i; | ||
| 1812 | break; | ||
| 1813 | } | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | if ((panel_idx < 0) || (panel_idx >= num_panels)) { | ||
| 1817 | print_warn("Panel %s not supported!", this_opt); | ||
| 1818 | } | ||
| 1819 | else | ||
| 1820 | panel_index = panel_idx; | ||
| 1821 | } | ||
| 1822 | |||
| 1823 | else if (strncmp(this_opt, "nohwcursor", 10) == 0) { | ||
| 1824 | nohwcursor = 1; | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | /* Unsupported option */ | ||
| 1828 | else { | ||
| 1829 | print_warn("Unsupported option \"%s\"", this_opt); | ||
| 1830 | } | ||
| 1831 | } | ||
| 1832 | } | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | #ifdef CONFIG_PM | ||
| 1836 | static int au1200fb_pm_callback(au1xxx_power_dev_t *dev, | ||
| 1837 | au1xxx_request_t request, void *data) { | ||
| 1838 | int retval = -1; | ||
| 1839 | unsigned int d = 0; | ||
| 1840 | unsigned int brightness = 0; | ||
| 1841 | |||
| 1842 | if (request == AU1XXX_PM_SLEEP) { | ||
| 1843 | board_au1200fb_panel_shutdown(); | ||
| 1844 | } | ||
| 1845 | else if (request == AU1XXX_PM_WAKEUP) { | ||
| 1846 | if(dev->prev_state == SLEEP_STATE) | ||
| 1847 | { | ||
| 1848 | int plane; | ||
| 1849 | au1200_setpanel(panel); | ||
| 1850 | for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { | ||
| 1851 | struct au1200fb_device *fbdev; | ||
| 1852 | fbdev = &_au1200fb_devices[plane]; | ||
| 1853 | au1200fb_fb_set_par(&fbdev->fb_info); | ||
| 1854 | } | ||
| 1855 | } | ||
| 1856 | |||
| 1857 | d = *((unsigned int*)data); | ||
| 1858 | if(d <=10) brightness = 26; | ||
| 1859 | else if(d<=20) brightness = 51; | ||
| 1860 | else if(d<=30) brightness = 77; | ||
| 1861 | else if(d<=40) brightness = 102; | ||
| 1862 | else if(d<=50) brightness = 128; | ||
| 1863 | else if(d<=60) brightness = 153; | ||
| 1864 | else if(d<=70) brightness = 179; | ||
| 1865 | else if(d<=80) brightness = 204; | ||
| 1866 | else if(d<=90) brightness = 230; | ||
| 1867 | else brightness = 255; | ||
| 1868 | set_brightness(brightness); | ||
| 1869 | } else if (request == AU1XXX_PM_GETSTATUS) { | ||
| 1870 | return dev->cur_state; | ||
| 1871 | } else if (request == AU1XXX_PM_ACCESS) { | ||
| 1872 | if (dev->cur_state != SLEEP_STATE) | ||
| 1873 | return retval; | ||
| 1874 | else { | ||
| 1875 | au1200_setpanel(panel); | ||
| 1876 | } | ||
| 1877 | } else if (request == AU1XXX_PM_IDLE) { | ||
| 1878 | } else if (request == AU1XXX_PM_CLEANUP) { | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | return retval; | ||
| 1882 | } | ||
| 1883 | #endif | ||
| 1884 | |||
| 1885 | static int __init au1200fb_init(void) | ||
| 1886 | { | ||
| 1887 | print_info("" DRIVER_DESC ""); | ||
| 1888 | |||
| 1889 | /* Setup driver with options */ | ||
| 1890 | au1200fb_setup(); | ||
| 1891 | |||
| 1892 | /* Point to the panel selected */ | ||
| 1893 | panel = &known_lcd_panels[panel_index]; | ||
| 1894 | win = &windows[window_index]; | ||
| 1895 | |||
| 1896 | printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name); | ||
| 1897 | printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name); | ||
| 1898 | |||
| 1899 | /* Kickstart the panel, the framebuffers/windows come soon enough */ | ||
| 1900 | au1200_setpanel(panel); | ||
| 1901 | |||
| 1902 | #ifdef CONFIG_PM | ||
| 1903 | LCD_pm_dev = new_au1xxx_power_device("LCD", &au1200fb_pm_callback, NULL); | ||
| 1904 | if ( LCD_pm_dev == NULL) | ||
| 1905 | printk(KERN_INFO "Unable to create a power management device entry for the au1200fb.\n"); | ||
| 1906 | else | ||
| 1907 | printk(KERN_INFO "Power management device entry for the au1200fb loaded.\n"); | ||
| 1908 | #endif | ||
| 1909 | |||
| 1910 | return driver_register(&au1200fb_driver); | ||
| 1911 | } | ||
| 1912 | |||
| 1913 | static void __exit au1200fb_cleanup(void) | ||
| 1914 | { | ||
| 1915 | driver_unregister(&au1200fb_driver); | ||
| 1916 | } | ||
| 1917 | |||
| 1918 | module_init(au1200fb_init); | ||
| 1919 | module_exit(au1200fb_cleanup); | ||
| 1920 | |||
| 1921 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
| 1922 | MODULE_LICENSE("GPL"); | ||
| 1923 | /* | ||
| 1924 | * BRIEF MODULE DESCRIPTION | ||
| 1925 | * Au1200 LCD Driver. | ||
| 1926 | * | ||
| 1927 | * Copyright 2004-2005 AMD | ||
| 1928 | * Author: AMD | ||
| 1929 | * | ||
| 1930 | * Based on: | ||
| 1931 | * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device | ||
| 1932 | * Created 28 Dec 1997 by Geert Uytterhoeven | ||
| 1933 | * | ||
| 1934 | * This program is free software; you can redistribute it and/or modify it | ||
| 1935 | * under the terms of the GNU General Public License as published by the | ||
| 1936 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 1937 | * option) any later version. | ||
| 1938 | * | ||
| 1939 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 1940 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 1941 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
| 1942 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 1943 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 1944 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 1945 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 1946 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 1947 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 1948 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 1949 | * | ||
| 1950 | * You should have received a copy of the GNU General Public License along | ||
| 1951 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 1952 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 1953 | */ | ||
| 1954 | |||
| 1955 | #include <linux/module.h> | ||
| 1956 | #include <linux/platform_device.h> | ||
| 1957 | #include <linux/kernel.h> | ||
| 1958 | #include <linux/errno.h> | ||
| 1959 | #include <linux/string.h> | ||
| 1960 | #include <linux/mm.h> | ||
| 1961 | #include <linux/fb.h> | ||
| 1962 | #include <linux/init.h> | ||
| 1963 | #include <linux/interrupt.h> | ||
| 1964 | #include <linux/ctype.h> | ||
| 1965 | #include <linux/dma-mapping.h> | ||
| 1966 | |||
| 1967 | #include <asm/mach-au1x00/au1000.h> | ||
| 1968 | #include "au1200fb.h" | ||
| 1969 | |||
| 1970 | #ifdef CONFIG_PM | ||
| 1971 | #include <asm/mach-au1x00/au1xxx_pm.h> | ||
| 1972 | #endif | ||
| 1973 | |||
| 1974 | #ifndef CONFIG_FB_AU1200_DEVS | ||
| 1975 | #define CONFIG_FB_AU1200_DEVS 4 | ||
| 1976 | #endif | ||
| 1977 | |||
| 1978 | #define DRIVER_NAME "au1200fb" | ||
| 1979 | #define DRIVER_DESC "LCD controller driver for AU1200 processors" | ||
| 1980 | |||
| 1981 | #define DEBUG 1 | ||
| 1982 | |||
| 1983 | #define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) | ||
| 1984 | #define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) | ||
| 1985 | #define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) | ||
| 1986 | |||
| 1987 | #if DEBUG | ||
| 1988 | #define print_dbg(f, arg...) printk(KERN_DEBUG __FILE__ ": " f "\n", ## arg) | ||
| 1989 | #else | ||
| 1990 | #define print_dbg(f, arg...) do {} while (0) | ||
| 1991 | #endif | ||
| 1992 | |||
| 1993 | |||
| 1994 | #define AU1200_LCD_FB_IOCTL 0x46FF | ||
| 1995 | |||
| 1996 | #define AU1200_LCD_SET_SCREEN 1 | ||
| 1997 | #define AU1200_LCD_GET_SCREEN 2 | ||
| 1998 | #define AU1200_LCD_SET_WINDOW 3 | ||
| 1999 | #define AU1200_LCD_GET_WINDOW 4 | ||
| 2000 | #define AU1200_LCD_SET_PANEL 5 | ||
| 2001 | #define AU1200_LCD_GET_PANEL 6 | ||
| 2002 | |||
| 2003 | #define SCREEN_SIZE (1<< 1) | ||
| 2004 | #define SCREEN_BACKCOLOR (1<< 2) | ||
| 2005 | #define SCREEN_BRIGHTNESS (1<< 3) | ||
| 2006 | #define SCREEN_COLORKEY (1<< 4) | ||
| 2007 | #define SCREEN_MASK (1<< 5) | ||
| 2008 | |||
| 2009 | struct au1200_lcd_global_regs_t { | ||
| 2010 | unsigned int flags; | ||
| 2011 | unsigned int xsize; | ||
| 2012 | unsigned int ysize; | ||
| 2013 | unsigned int backcolor; | ||
| 2014 | unsigned int brightness; | ||
| 2015 | unsigned int colorkey; | ||
| 2016 | unsigned int mask; | ||
| 2017 | unsigned int panel_choice; | ||
| 2018 | char panel_desc[80]; | ||
| 2019 | |||
| 2020 | }; | ||
| 2021 | |||
| 2022 | #define WIN_POSITION (1<< 0) | ||
| 2023 | #define WIN_ALPHA_COLOR (1<< 1) | ||
| 2024 | #define WIN_ALPHA_MODE (1<< 2) | ||
| 2025 | #define WIN_PRIORITY (1<< 3) | ||
| 2026 | #define WIN_CHANNEL (1<< 4) | ||
| 2027 | #define WIN_BUFFER_FORMAT (1<< 5) | ||
| 2028 | #define WIN_COLOR_ORDER (1<< 6) | ||
| 2029 | #define WIN_PIXEL_ORDER (1<< 7) | ||
| 2030 | #define WIN_SIZE (1<< 8) | ||
| 2031 | #define WIN_COLORKEY_MODE (1<< 9) | ||
| 2032 | #define WIN_DOUBLE_BUFFER_MODE (1<< 10) | ||
| 2033 | #define WIN_RAM_ARRAY_MODE (1<< 11) | ||
| 2034 | #define WIN_BUFFER_SCALE (1<< 12) | ||
| 2035 | #define WIN_ENABLE (1<< 13) | ||
| 2036 | |||
| 2037 | struct au1200_lcd_window_regs_t { | ||
| 2038 | unsigned int flags; | ||
| 2039 | unsigned int xpos; | ||
| 2040 | unsigned int ypos; | ||
| 2041 | unsigned int alpha_color; | ||
| 2042 | unsigned int alpha_mode; | ||
| 2043 | unsigned int priority; | ||
| 2044 | unsigned int channel; | ||
| 2045 | unsigned int buffer_format; | ||
| 2046 | unsigned int color_order; | ||
| 2047 | unsigned int pixel_order; | ||
| 2048 | unsigned int xsize; | ||
| 2049 | unsigned int ysize; | ||
| 2050 | unsigned int colorkey_mode; | ||
| 2051 | unsigned int double_buffer_mode; | ||
| 2052 | unsigned int ram_array_mode; | ||
| 2053 | unsigned int xscale; | ||
| 2054 | unsigned int yscale; | ||
| 2055 | unsigned int enable; | ||
| 2056 | }; | ||
| 2057 | |||
| 2058 | |||
| 2059 | struct au1200_lcd_iodata_t { | ||
| 2060 | unsigned int subcmd; | ||
| 2061 | struct au1200_lcd_global_regs_t global; | ||
| 2062 | struct au1200_lcd_window_regs_t window; | ||
| 2063 | }; | ||
| 2064 | |||
| 2065 | #if defined(__BIG_ENDIAN) | ||
| 2066 | #define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_11 | ||
| 2067 | #else | ||
| 2068 | #define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_00 | ||
| 2069 | #endif | ||
| 2070 | #define LCD_CONTROL_DEFAULT_SBPPF LCD_CONTROL_SBPPF_565 | ||
| 2071 | |||
| 2072 | /* Private, per-framebuffer management information (independent of the panel itself) */ | ||
| 2073 | struct au1200fb_device { | ||
| 2074 | struct fb_info fb_info; /* FB driver info record */ | ||
| 2075 | |||
| 2076 | int plane; | ||
| 2077 | unsigned char* fb_mem; /* FrameBuffer memory map */ | ||
| 2078 | unsigned int fb_len; | ||
| 2079 | dma_addr_t fb_phys; | ||
| 2080 | }; | ||
| 2081 | |||
| 2082 | static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS]; | ||
| 2083 | /********************************************************************/ | ||
| 2084 | |||
| 2085 | /* LCD controller restrictions */ | ||
| 2086 | #define AU1200_LCD_MAX_XRES 1280 | ||
| 2087 | #define AU1200_LCD_MAX_YRES 1024 | ||
| 2088 | #define AU1200_LCD_MAX_BPP 32 | ||
| 2089 | #define AU1200_LCD_MAX_CLK 96000000 /* fixme: this needs to go away ? */ | ||
| 2090 | #define AU1200_LCD_NBR_PALETTE_ENTRIES 256 | ||
| 2091 | |||
| 2092 | /* Default number of visible screen buffer to allocate */ | ||
| 2093 | #define AU1200FB_NBR_VIDEO_BUFFERS 1 | ||
| 2094 | |||
| 2095 | /********************************************************************/ | ||
| 2096 | |||
| 2097 | static struct au1200_lcd *lcd = (struct au1200_lcd *) AU1200_LCD_ADDR; | ||
| 2098 | static int window_index = 2; /* default is zero */ | ||
| 2099 | static int panel_index = 2; /* default is zero */ | ||
| 2100 | static struct window_settings *win; | ||
| 2101 | static struct panel_settings *panel; | ||
| 2102 | static int noblanking = 1; | ||
| 2103 | static int nohwcursor = 0; | ||
| 2104 | |||
| 2105 | struct window_settings { | ||
| 2106 | unsigned char name[64]; | ||
| 2107 | uint32 mode_backcolor; | ||
| 2108 | uint32 mode_colorkey; | ||
| 2109 | uint32 mode_colorkeymsk; | ||
| 2110 | struct { | ||
| 2111 | int xres; | ||
| 2112 | int yres; | ||
| 2113 | int xpos; | ||
| 2114 | int ypos; | ||
| 2115 | uint32 mode_winctrl1; /* winctrl1[FRM,CCO,PO,PIPE] */ | ||
| 2116 | uint32 mode_winenable; | ||
| 2117 | } w[4]; | ||
| 2118 | }; | ||
| 2119 | |||
| 2120 | #if defined(__BIG_ENDIAN) | ||
| 2121 | #define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_00 | ||
| 2122 | #else | ||
| 2123 | #define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_01 | ||
| 2124 | #endif | ||
| 2125 | |||
| 2126 | extern int board_au1200fb_panel_init (void); | ||
| 2127 | extern int board_au1200fb_panel_shutdown (void); | ||
| 2128 | |||
| 2129 | #ifdef CONFIG_PM | ||
| 2130 | int au1200fb_pm_callback(au1xxx_power_dev_t *dev, | ||
| 2131 | au1xxx_request_t request, void *data); | ||
| 2132 | au1xxx_power_dev_t *LCD_pm_dev; | ||
| 2133 | #endif | ||
| 2134 | |||
| 2135 | /* | ||
| 2136 | * Default window configurations | ||
| 2137 | */ | ||
| 2138 | static struct window_settings windows[] = { | ||
| 2139 | { /* Index 0 */ | ||
| 2140 | "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", | ||
| 2141 | /* mode_backcolor */ 0x006600ff, | ||
| 2142 | /* mode_colorkey,msk*/ 0, 0, | ||
| 2143 | { | ||
| 2144 | { | ||
| 2145 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 2146 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2147 | LCD_WINCTRL1_PO_16BPP, | ||
| 2148 | /* mode_winenable*/ LCD_WINENABLE_WEN0, | ||
| 2149 | }, | ||
| 2150 | { | ||
| 2151 | /* xres, yres, xpos, ypos */ 100, 100, 100, 100, | ||
| 2152 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2153 | LCD_WINCTRL1_PO_16BPP | | ||
| 2154 | LCD_WINCTRL1_PIPE, | ||
| 2155 | /* mode_winenable*/ LCD_WINENABLE_WEN1, | ||
| 2156 | }, | ||
| 2157 | { | ||
| 2158 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 2159 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2160 | LCD_WINCTRL1_PO_16BPP, | ||
| 2161 | /* mode_winenable*/ 0, | ||
| 2162 | }, | ||
| 2163 | { | ||
| 2164 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 2165 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2166 | LCD_WINCTRL1_PO_16BPP | | ||
| 2167 | LCD_WINCTRL1_PIPE, | ||
| 2168 | /* mode_winenable*/ 0, | ||
| 2169 | }, | ||
| 2170 | }, | ||
| 2171 | }, | ||
| 2172 | |||
| 2173 | { /* Index 1 */ | ||
| 2174 | "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", | ||
| 2175 | /* mode_backcolor */ 0x006600ff, | ||
| 2176 | /* mode_colorkey,msk*/ 0, 0, | ||
| 2177 | { | ||
| 2178 | { | ||
| 2179 | /* xres, yres, xpos, ypos */ 320, 240, 5, 5, | ||
| 2180 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_24BPP | | ||
| 2181 | LCD_WINCTRL1_PO_00, | ||
| 2182 | /* mode_winenable*/ LCD_WINENABLE_WEN0, | ||
| 2183 | }, | ||
| 2184 | { | ||
| 2185 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 2186 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | ||
| 2187 | | LCD_WINCTRL1_PO_16BPP, | ||
| 2188 | /* mode_winenable*/ 0, | ||
| 2189 | }, | ||
| 2190 | { | ||
| 2191 | /* xres, yres, xpos, ypos */ 100, 100, 0, 0, | ||
| 2192 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2193 | LCD_WINCTRL1_PO_16BPP | | ||
| 2194 | LCD_WINCTRL1_PIPE, | ||
| 2195 | /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, | ||
| 2196 | }, | ||
| 2197 | { | ||
| 2198 | /* xres, yres, xpos, ypos */ 200, 25, 0, 0, | ||
| 2199 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2200 | LCD_WINCTRL1_PO_16BPP | | ||
| 2201 | LCD_WINCTRL1_PIPE, | ||
| 2202 | /* mode_winenable*/ 0, | ||
| 2203 | }, | ||
| 2204 | }, | ||
| 2205 | }, | ||
| 2206 | { /* Index 2 */ | ||
| 2207 | "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", | ||
| 2208 | /* mode_backcolor */ 0x006600ff, | ||
| 2209 | /* mode_colorkey,msk*/ 0, 0, | ||
| 2210 | { | ||
| 2211 | { | ||
| 2212 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 2213 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2214 | LCD_WINCTRL1_PO_16BPP, | ||
| 2215 | /* mode_winenable*/ LCD_WINENABLE_WEN0, | ||
| 2216 | }, | ||
| 2217 | { | ||
| 2218 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 2219 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2220 | LCD_WINCTRL1_PO_16BPP, | ||
| 2221 | /* mode_winenable*/ 0, | ||
| 2222 | }, | ||
| 2223 | { | ||
| 2224 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 2225 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_32BPP | | ||
| 2226 | LCD_WINCTRL1_PO_00|LCD_WINCTRL1_PIPE, | ||
| 2227 | /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, | ||
| 2228 | }, | ||
| 2229 | { | ||
| 2230 | /* xres, yres, xpos, ypos */ 0, 0, 0, 0, | ||
| 2231 | /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | | ||
| 2232 | LCD_WINCTRL1_PO_16BPP | | ||
| 2233 | LCD_WINCTRL1_PIPE, | ||
| 2234 | /* mode_winenable*/ 0, | ||
| 2235 | }, | ||
| 2236 | }, | ||
| 2237 | }, | ||
| 2238 | /* Need VGA 640 @ 24bpp, @ 32bpp */ | ||
| 2239 | /* Need VGA 800 @ 24bpp, @ 32bpp */ | ||
| 2240 | /* Need VGA 1024 @ 24bpp, @ 32bpp */ | ||
| 2241 | }; | ||
| 2242 | |||
| 2243 | /* | ||
| 2244 | * Controller configurations for various panels. | ||
| 2245 | */ | ||
| 2246 | |||
| 2247 | struct panel_settings | ||
| 2248 | { | ||
| 2249 | const char name[25]; /* Full name <vendor>_<model> */ | ||
| 2250 | |||
| 2251 | struct fb_monspecs monspecs; /* FB monitor specs */ | ||
| 2252 | |||
| 2253 | /* panel timings */ | ||
| 2254 | uint32 mode_screen; | ||
| 2255 | uint32 mode_horztiming; | ||
| 2256 | uint32 mode_verttiming; | ||
| 2257 | uint32 mode_clkcontrol; | ||
| 2258 | uint32 mode_pwmdiv; | ||
| 2259 | uint32 mode_pwmhi; | ||
| 2260 | uint32 mode_outmask; | ||
| 2261 | uint32 mode_fifoctrl; | ||
| 2262 | uint32 mode_toyclksrc; | ||
| 2263 | uint32 mode_backlight; | ||
| 2264 | uint32 mode_auxpll; | ||
| 2265 | int (*device_init)(void); | ||
| 2266 | int (*device_shutdown)(void); | ||
| 2267 | #define Xres min_xres | ||
| 2268 | #define Yres min_yres | ||
| 2269 | u32 min_xres; /* Minimum horizontal resolution */ | ||
| 2270 | u32 max_xres; /* Maximum horizontal resolution */ | ||
| 2271 | u32 min_yres; /* Minimum vertical resolution */ | ||
| 2272 | u32 max_yres; /* Maximum vertical resolution */ | ||
| 2273 | }; | ||
| 2274 | |||
| 2275 | /********************************************************************/ | ||
| 2276 | /* fixme: Maybe a modedb for the CRT ? otherwise panels should be as-is */ | ||
| 2277 | |||
| 2278 | /* List of panels known to work with the AU1200 LCD controller. | ||
| 2279 | * To add a new panel, enter the same specifications as the | ||
| 2280 | * Generic_TFT one, and MAKE SURE that it doesn't conflicts | ||
| 2281 | * with the controller restrictions. Restrictions are: | ||
| 2282 | * | ||
| 2283 | * STN color panels: max_bpp <= 12 | ||
| 2284 | * STN mono panels: max_bpp <= 4 | ||
| 2285 | * TFT panels: max_bpp <= 16 | ||
| 2286 | * max_xres <= 800 | ||
| 2287 | * max_yres <= 600 | ||
| 2288 | */ | ||
| 2289 | static struct panel_settings known_lcd_panels[] = | ||
| 2290 | { | ||
| 2291 | [0] = { /* QVGA 320x240 H:33.3kHz V:110Hz */ | ||
| 2292 | .name = "QVGA_320x240", | ||
| 2293 | .monspecs = { | ||
| 2294 | .modedb = NULL, | ||
| 2295 | .modedb_len = 0, | ||
| 2296 | .hfmin = 30000, | ||
| 2297 | .hfmax = 70000, | ||
| 2298 | .vfmin = 60, | ||
| 2299 | .vfmax = 60, | ||
| 2300 | .dclkmin = 6000000, | ||
| 2301 | .dclkmax = 28000000, | ||
| 2302 | .input = FB_DISP_RGB, | ||
| 2303 | }, | ||
| 2304 | .mode_screen = LCD_SCREEN_SX_N(320) | | ||
| 2305 | LCD_SCREEN_SY_N(240), | ||
| 2306 | .mode_horztiming = 0x00c4623b, | ||
| 2307 | .mode_verttiming = 0x00502814, | ||
| 2308 | .mode_clkcontrol = 0x00020002, /* /4=24Mhz */ | ||
| 2309 | .mode_pwmdiv = 0x00000000, | ||
| 2310 | .mode_pwmhi = 0x00000000, | ||
| 2311 | .mode_outmask = 0x00FFFFFF, | ||
| 2312 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2313 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2314 | .mode_backlight = 0x00000000, | ||
| 2315 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 2316 | .device_init = NULL, | ||
| 2317 | .device_shutdown = NULL, | ||
| 2318 | 320, 320, | ||
| 2319 | 240, 240, | ||
| 2320 | }, | ||
| 2321 | |||
| 2322 | [1] = { /* VGA 640x480 H:30.3kHz V:58Hz */ | ||
| 2323 | .name = "VGA_640x480", | ||
| 2324 | .monspecs = { | ||
| 2325 | .modedb = NULL, | ||
| 2326 | .modedb_len = 0, | ||
| 2327 | .hfmin = 30000, | ||
| 2328 | .hfmax = 70000, | ||
| 2329 | .vfmin = 60, | ||
| 2330 | .vfmax = 60, | ||
| 2331 | .dclkmin = 6000000, | ||
| 2332 | .dclkmax = 28000000, | ||
| 2333 | .input = FB_DISP_RGB, | ||
| 2334 | }, | ||
| 2335 | .mode_screen = 0x13f9df80, | ||
| 2336 | .mode_horztiming = 0x003c5859, | ||
| 2337 | .mode_verttiming = 0x00741201, | ||
| 2338 | .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ | ||
| 2339 | .mode_pwmdiv = 0x00000000, | ||
| 2340 | .mode_pwmhi = 0x00000000, | ||
| 2341 | .mode_outmask = 0x00FFFFFF, | ||
| 2342 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2343 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2344 | .mode_backlight = 0x00000000, | ||
| 2345 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 2346 | .device_init = NULL, | ||
| 2347 | .device_shutdown = NULL, | ||
| 2348 | 640, 480, | ||
| 2349 | 640, 480, | ||
| 2350 | }, | ||
| 2351 | |||
| 2352 | [2] = { /* SVGA 800x600 H:46.1kHz V:69Hz */ | ||
| 2353 | .name = "SVGA_800x600", | ||
| 2354 | .monspecs = { | ||
| 2355 | .modedb = NULL, | ||
| 2356 | .modedb_len = 0, | ||
| 2357 | .hfmin = 30000, | ||
| 2358 | .hfmax = 70000, | ||
| 2359 | .vfmin = 60, | ||
| 2360 | .vfmax = 60, | ||
| 2361 | .dclkmin = 6000000, | ||
| 2362 | .dclkmax = 28000000, | ||
| 2363 | .input = FB_DISP_RGB, | ||
| 2364 | }, | ||
| 2365 | .mode_screen = 0x18fa5780, | ||
| 2366 | .mode_horztiming = 0x00dc7e77, | ||
| 2367 | .mode_verttiming = 0x00584805, | ||
| 2368 | .mode_clkcontrol = 0x00020000, /* /2=48Mhz */ | ||
| 2369 | .mode_pwmdiv = 0x00000000, | ||
| 2370 | .mode_pwmhi = 0x00000000, | ||
| 2371 | .mode_outmask = 0x00FFFFFF, | ||
| 2372 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2373 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2374 | .mode_backlight = 0x00000000, | ||
| 2375 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 2376 | .device_init = NULL, | ||
| 2377 | .device_shutdown = NULL, | ||
| 2378 | 800, 800, | ||
| 2379 | 600, 600, | ||
| 2380 | }, | ||
| 2381 | |||
| 2382 | [3] = { /* XVGA 1024x768 H:56.2kHz V:70Hz */ | ||
| 2383 | .name = "XVGA_1024x768", | ||
| 2384 | .monspecs = { | ||
| 2385 | .modedb = NULL, | ||
| 2386 | .modedb_len = 0, | ||
| 2387 | .hfmin = 30000, | ||
| 2388 | .hfmax = 70000, | ||
| 2389 | .vfmin = 60, | ||
| 2390 | .vfmax = 60, | ||
| 2391 | .dclkmin = 6000000, | ||
| 2392 | .dclkmax = 28000000, | ||
| 2393 | .input = FB_DISP_RGB, | ||
| 2394 | }, | ||
| 2395 | .mode_screen = 0x1ffaff80, | ||
| 2396 | .mode_horztiming = 0x007d0e57, | ||
| 2397 | .mode_verttiming = 0x00740a01, | ||
| 2398 | .mode_clkcontrol = 0x000A0000, /* /1 */ | ||
| 2399 | .mode_pwmdiv = 0x00000000, | ||
| 2400 | .mode_pwmhi = 0x00000000, | ||
| 2401 | .mode_outmask = 0x00FFFFFF, | ||
| 2402 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2403 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2404 | .mode_backlight = 0x00000000, | ||
| 2405 | .mode_auxpll = 6, /* 72MHz AUXPLL */ | ||
| 2406 | .device_init = NULL, | ||
| 2407 | .device_shutdown = NULL, | ||
| 2408 | 1024, 1024, | ||
| 2409 | 768, 768, | ||
| 2410 | }, | ||
| 2411 | |||
| 2412 | [4] = { /* XVGA XVGA 1280x1024 H:68.5kHz V:65Hz */ | ||
| 2413 | .name = "XVGA_1280x1024", | ||
| 2414 | .monspecs = { | ||
| 2415 | .modedb = NULL, | ||
| 2416 | .modedb_len = 0, | ||
| 2417 | .hfmin = 30000, | ||
| 2418 | .hfmax = 70000, | ||
| 2419 | .vfmin = 60, | ||
| 2420 | .vfmax = 60, | ||
| 2421 | .dclkmin = 6000000, | ||
| 2422 | .dclkmax = 28000000, | ||
| 2423 | .input = FB_DISP_RGB, | ||
| 2424 | }, | ||
| 2425 | .mode_screen = 0x27fbff80, | ||
| 2426 | .mode_horztiming = 0x00cdb2c7, | ||
| 2427 | .mode_verttiming = 0x00600002, | ||
| 2428 | .mode_clkcontrol = 0x000A0000, /* /1 */ | ||
| 2429 | .mode_pwmdiv = 0x00000000, | ||
| 2430 | .mode_pwmhi = 0x00000000, | ||
| 2431 | .mode_outmask = 0x00FFFFFF, | ||
| 2432 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2433 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2434 | .mode_backlight = 0x00000000, | ||
| 2435 | .mode_auxpll = 10, /* 120MHz AUXPLL */ | ||
| 2436 | .device_init = NULL, | ||
| 2437 | .device_shutdown = NULL, | ||
| 2438 | 1280, 1280, | ||
| 2439 | 1024, 1024, | ||
| 2440 | }, | ||
| 2441 | |||
| 2442 | [5] = { /* Samsung 1024x768 TFT */ | ||
| 2443 | .name = "Samsung_1024x768_TFT", | ||
| 2444 | .monspecs = { | ||
| 2445 | .modedb = NULL, | ||
| 2446 | .modedb_len = 0, | ||
| 2447 | .hfmin = 30000, | ||
| 2448 | .hfmax = 70000, | ||
| 2449 | .vfmin = 60, | ||
| 2450 | .vfmax = 60, | ||
| 2451 | .dclkmin = 6000000, | ||
| 2452 | .dclkmax = 28000000, | ||
| 2453 | .input = FB_DISP_RGB, | ||
| 2454 | }, | ||
| 2455 | .mode_screen = 0x1ffaff80, | ||
| 2456 | .mode_horztiming = 0x018cc677, | ||
| 2457 | .mode_verttiming = 0x00241217, | ||
| 2458 | .mode_clkcontrol = 0x00000000, /* SCB 0x1 /4=24Mhz */ | ||
| 2459 | .mode_pwmdiv = 0x8000063f, /* SCB 0x0 */ | ||
| 2460 | .mode_pwmhi = 0x03400000, /* SCB 0x0 */ | ||
| 2461 | .mode_outmask = 0x00FFFFFF, | ||
| 2462 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2463 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2464 | .mode_backlight = 0x00000000, | ||
| 2465 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 2466 | .device_init = board_au1200fb_panel_init, | ||
| 2467 | .device_shutdown = board_au1200fb_panel_shutdown, | ||
| 2468 | 1024, 1024, | ||
| 2469 | 768, 768, | ||
| 2470 | }, | ||
| 2471 | |||
| 2472 | [6] = { /* Toshiba 640x480 TFT */ | ||
| 2473 | .name = "Toshiba_640x480_TFT", | ||
| 2474 | .monspecs = { | ||
| 2475 | .modedb = NULL, | ||
| 2476 | .modedb_len = 0, | ||
| 2477 | .hfmin = 30000, | ||
| 2478 | .hfmax = 70000, | ||
| 2479 | .vfmin = 60, | ||
| 2480 | .vfmax = 60, | ||
| 2481 | .dclkmin = 6000000, | ||
| 2482 | .dclkmax = 28000000, | ||
| 2483 | .input = FB_DISP_RGB, | ||
| 2484 | }, | ||
| 2485 | .mode_screen = LCD_SCREEN_SX_N(640) | | ||
| 2486 | LCD_SCREEN_SY_N(480), | ||
| 2487 | .mode_horztiming = LCD_HORZTIMING_HPW_N(96) | | ||
| 2488 | LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(51), | ||
| 2489 | .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | | ||
| 2490 | LCD_VERTTIMING_VND1_N(11) | LCD_VERTTIMING_VND2_N(32), | ||
| 2491 | .mode_clkcontrol = 0x00000000, /* /4=24Mhz */ | ||
| 2492 | .mode_pwmdiv = 0x8000063f, | ||
| 2493 | .mode_pwmhi = 0x03400000, | ||
| 2494 | .mode_outmask = 0x00fcfcfc, | ||
| 2495 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2496 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2497 | .mode_backlight = 0x00000000, | ||
| 2498 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 2499 | .device_init = board_au1200fb_panel_init, | ||
| 2500 | .device_shutdown = board_au1200fb_panel_shutdown, | ||
| 2501 | 640, 480, | ||
| 2502 | 640, 480, | ||
| 2503 | }, | ||
| 2504 | |||
| 2505 | [7] = { /* Sharp 320x240 TFT */ | ||
| 2506 | .name = "Sharp_320x240_TFT", | ||
| 2507 | .monspecs = { | ||
| 2508 | .modedb = NULL, | ||
| 2509 | .modedb_len = 0, | ||
| 2510 | .hfmin = 12500, | ||
| 2511 | .hfmax = 20000, | ||
| 2512 | .vfmin = 38, | ||
| 2513 | .vfmax = 81, | ||
| 2514 | .dclkmin = 4500000, | ||
| 2515 | .dclkmax = 6800000, | ||
| 2516 | .input = FB_DISP_RGB, | ||
| 2517 | }, | ||
| 2518 | .mode_screen = LCD_SCREEN_SX_N(320) | | ||
| 2519 | LCD_SCREEN_SY_N(240), | ||
| 2520 | .mode_horztiming = LCD_HORZTIMING_HPW_N(60) | | ||
| 2521 | LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(2), | ||
| 2522 | .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | | ||
| 2523 | LCD_VERTTIMING_VND1_N(2) | LCD_VERTTIMING_VND2_N(5), | ||
| 2524 | .mode_clkcontrol = LCD_CLKCONTROL_PCD_N(7), /*16=6Mhz*/ | ||
| 2525 | .mode_pwmdiv = 0x8000063f, | ||
| 2526 | .mode_pwmhi = 0x03400000, | ||
| 2527 | .mode_outmask = 0x00fcfcfc, | ||
| 2528 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2529 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2530 | .mode_backlight = 0x00000000, | ||
| 2531 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 2532 | .device_init = board_au1200fb_panel_init, | ||
| 2533 | .device_shutdown = board_au1200fb_panel_shutdown, | ||
| 2534 | 320, 320, | ||
| 2535 | 240, 240, | ||
| 2536 | }, | ||
| 2537 | |||
| 2538 | [8] = { /* Toppoly TD070WGCB2 7" 856x480 TFT */ | ||
| 2539 | .name = "Toppoly_TD070WGCB2", | ||
| 2540 | .monspecs = { | ||
| 2541 | .modedb = NULL, | ||
| 2542 | .modedb_len = 0, | ||
| 2543 | .hfmin = 30000, | ||
| 2544 | .hfmax = 70000, | ||
| 2545 | .vfmin = 60, | ||
| 2546 | .vfmax = 60, | ||
| 2547 | .dclkmin = 6000000, | ||
| 2548 | .dclkmax = 28000000, | ||
| 2549 | .input = FB_DISP_RGB, | ||
| 2550 | }, | ||
| 2551 | .mode_screen = LCD_SCREEN_SX_N(856) | | ||
| 2552 | LCD_SCREEN_SY_N(480), | ||
| 2553 | .mode_horztiming = LCD_HORZTIMING_HND2_N(43) | | ||
| 2554 | LCD_HORZTIMING_HND1_N(43) | LCD_HORZTIMING_HPW_N(114), | ||
| 2555 | .mode_verttiming = LCD_VERTTIMING_VND2_N(20) | | ||
| 2556 | LCD_VERTTIMING_VND1_N(21) | LCD_VERTTIMING_VPW_N(4), | ||
| 2557 | .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ | ||
| 2558 | .mode_pwmdiv = 0x8000063f, | ||
| 2559 | .mode_pwmhi = 0x03400000, | ||
| 2560 | .mode_outmask = 0x00fcfcfc, | ||
| 2561 | .mode_fifoctrl = 0x2f2f2f2f, | ||
| 2562 | .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ | ||
| 2563 | .mode_backlight = 0x00000000, | ||
| 2564 | .mode_auxpll = 8, /* 96MHz AUXPLL */ | ||
| 2565 | .device_init = board_au1200fb_panel_init, | ||
| 2566 | .device_shutdown = board_au1200fb_panel_shutdown, | ||
| 2567 | 856, 856, | ||
| 2568 | 480, 480, | ||
| 2569 | }, | ||
| 2570 | }; | ||
| 2571 | |||
| 2572 | #define NUM_PANELS (ARRAY_SIZE(known_lcd_panels)) | ||
| 2573 | |||
| 2574 | /********************************************************************/ | ||
| 2575 | |||
| 2576 | #ifdef CONFIG_PM | ||
| 2577 | static int set_brightness(unsigned int brightness) | ||
| 2578 | { | ||
| 2579 | unsigned int hi1, divider; | ||
| 2580 | |||
| 2581 | /* limit brightness pwm duty to >= 30/1600 */ | ||
| 2582 | if (brightness < 30) { | ||
| 2583 | brightness = 30; | ||
| 2584 | } | ||
| 2585 | divider = (lcd->pwmdiv & 0x3FFFF) + 1; | ||
| 2586 | hi1 = (lcd->pwmhi >> 16) + 1; | ||
| 2587 | hi1 = (((brightness & 0xFF) + 1) * divider >> 8); | ||
| 2588 | lcd->pwmhi &= 0xFFFF; | ||
| 2589 | lcd->pwmhi |= (hi1 << 16); | ||
| 2590 | |||
| 2591 | return brightness; | ||
| 2592 | } | ||
| 2593 | #endif /* CONFIG_PM */ | ||
| 2594 | |||
| 2595 | static int winbpp (unsigned int winctrl1) | ||
| 2596 | { | ||
| 2597 | int bits = 0; | ||
| 2598 | |||
| 2599 | /* how many bits are needed for each pixel format */ | ||
| 2600 | switch (winctrl1 & LCD_WINCTRL1_FRM) { | ||
| 2601 | case LCD_WINCTRL1_FRM_1BPP: | ||
| 2602 | bits = 1; | ||
| 2603 | break; | ||
| 2604 | case LCD_WINCTRL1_FRM_2BPP: | ||
| 2605 | bits = 2; | ||
| 2606 | break; | ||
| 2607 | case LCD_WINCTRL1_FRM_4BPP: | ||
| 2608 | bits = 4; | ||
| 2609 | break; | ||
| 2610 | case LCD_WINCTRL1_FRM_8BPP: | ||
| 2611 | bits = 8; | ||
| 2612 | break; | ||
| 2613 | case LCD_WINCTRL1_FRM_12BPP: | ||
| 2614 | case LCD_WINCTRL1_FRM_16BPP655: | ||
| 2615 | case LCD_WINCTRL1_FRM_16BPP565: | ||
| 2616 | case LCD_WINCTRL1_FRM_16BPP556: | ||
| 2617 | case LCD_WINCTRL1_FRM_16BPPI1555: | ||
| 2618 | case LCD_WINCTRL1_FRM_16BPPI5551: | ||
| 2619 | case LCD_WINCTRL1_FRM_16BPPA1555: | ||
| 2620 | case LCD_WINCTRL1_FRM_16BPPA5551: | ||
| 2621 | bits = 16; | ||
| 2622 | break; | ||
| 2623 | case LCD_WINCTRL1_FRM_24BPP: | ||
| 2624 | case LCD_WINCTRL1_FRM_32BPP: | ||
| 2625 | bits = 32; | ||
| 2626 | break; | ||
| 2627 | } | ||
| 2628 | |||
| 2629 | return bits; | ||
| 2630 | } | ||
| 2631 | |||
| 2632 | static int fbinfo2index (struct fb_info *fb_info) | ||
| 2633 | { | ||
| 2634 | int i; | ||
| 2635 | |||
| 2636 | for (i = 0; i < CONFIG_FB_AU1200_DEVS; ++i) { | ||
| 2637 | if (fb_info == (struct fb_info *)(&_au1200fb_devices[i].fb_info)) | ||
| 2638 | return i; | ||
| 2639 | } | ||
| 2640 | printk("au1200fb: ERROR: fbinfo2index failed!\n"); | ||
| 2641 | return -1; | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | static int au1200_setlocation (struct au1200fb_device *fbdev, int plane, | ||
| 2645 | int xpos, int ypos) | ||
| 2646 | { | ||
| 2647 | uint32 winctrl0, winctrl1, winenable, fb_offset = 0; | ||
| 2648 | int xsz, ysz; | ||
| 2649 | |||
| 2650 | /* FIX!!! NOT CHECKING FOR COMPLETE OFFSCREEN YET */ | ||
| 2651 | |||
| 2652 | winctrl0 = lcd->window[plane].winctrl0; | ||
| 2653 | winctrl1 = lcd->window[plane].winctrl1; | ||
| 2654 | winctrl0 &= (LCD_WINCTRL0_A | LCD_WINCTRL0_AEN); | ||
| 2655 | winctrl1 &= ~(LCD_WINCTRL1_SZX | LCD_WINCTRL1_SZY); | ||
| 2656 | |||
| 2657 | /* Check for off-screen adjustments */ | ||
| 2658 | xsz = win->w[plane].xres; | ||
| 2659 | ysz = win->w[plane].yres; | ||
| 2660 | if ((xpos + win->w[plane].xres) > panel->Xres) { | ||
| 2661 | /* Off-screen to the right */ | ||
| 2662 | xsz = panel->Xres - xpos; /* off by 1 ??? */ | ||
| 2663 | /*printk("off screen right\n");*/ | ||
| 2664 | } | ||
| 2665 | |||
| 2666 | if ((ypos + win->w[plane].yres) > panel->Yres) { | ||
| 2667 | /* Off-screen to the bottom */ | ||
| 2668 | ysz = panel->Yres - ypos; /* off by 1 ??? */ | ||
| 2669 | /*printk("off screen bottom\n");*/ | ||
| 2670 | } | ||
| 2671 | |||
| 2672 | if (xpos < 0) { | ||
| 2673 | /* Off-screen to the left */ | ||
| 2674 | xsz = win->w[plane].xres + xpos; | ||
| 2675 | fb_offset += (((0 - xpos) * winbpp(lcd->window[plane].winctrl1))/8); | ||
| 2676 | xpos = 0; | ||
| 2677 | /*printk("off screen left\n");*/ | ||
| 2678 | } | ||
| 2679 | |||
| 2680 | if (ypos < 0) { | ||
| 2681 | /* Off-screen to the top */ | ||
| 2682 | ysz = win->w[plane].yres + ypos; | ||
| 2683 | /* fixme: fb_offset += ((0-ypos)*fb_pars[plane].line_length); */ | ||
| 2684 | ypos = 0; | ||
| 2685 | /*printk("off screen top\n");*/ | ||
| 2686 | } | ||
| 2687 | |||
| 2688 | /* record settings */ | ||
| 2689 | win->w[plane].xpos = xpos; | ||
| 2690 | win->w[plane].ypos = ypos; | ||
| 2691 | |||
| 2692 | xsz -= 1; | ||
| 2693 | ysz -= 1; | ||
| 2694 | winctrl0 |= (xpos << 21); | ||
| 2695 | winctrl0 |= (ypos << 10); | ||
| 2696 | winctrl1 |= (xsz << 11); | ||
| 2697 | winctrl1 |= (ysz << 0); | ||
| 2698 | |||
| 2699 | /* Disable the window while making changes, then restore WINEN */ | ||
| 2700 | winenable = lcd->winenable & (1 << plane); | ||
| 2701 | au_sync(); | ||
| 2702 | lcd->winenable &= ~(1 << plane); | ||
| 2703 | lcd->window[plane].winctrl0 = winctrl0; | ||
| 2704 | lcd->window[plane].winctrl1 = winctrl1; | ||
| 2705 | lcd->window[plane].winbuf0 = | ||
| 2706 | lcd->window[plane].winbuf1 = fbdev->fb_phys; | ||
| 2707 | lcd->window[plane].winbufctrl = 0; /* select winbuf0 */ | ||
| 2708 | lcd->winenable |= winenable; | ||
| 2709 | au_sync(); | ||
| 2710 | |||
| 2711 | return 0; | ||
| 2712 | } | ||
| 2713 | |||
| 2714 | static void au1200_setpanel (struct panel_settings *newpanel) | ||
| 2715 | { | ||
| 2716 | /* | ||
| 2717 | * Perform global setup/init of LCD controller | ||
| 2718 | */ | ||
| 2719 | uint32 winenable; | ||
| 2720 | |||
| 2721 | /* Make sure all windows disabled */ | ||
| 2722 | winenable = lcd->winenable; | ||
| 2723 | lcd->winenable = 0; | ||
| 2724 | au_sync(); | ||
| 2725 | /* | ||
| 2726 | * Ensure everything is disabled before reconfiguring | ||
| 2727 | */ | ||
| 2728 | if (lcd->screen & LCD_SCREEN_SEN) { | ||
| 2729 | /* Wait for vertical sync period */ | ||
| 2730 | lcd->intstatus = LCD_INT_SS; | ||
| 2731 | while ((lcd->intstatus & LCD_INT_SS) == 0) { | ||
| 2732 | au_sync(); | ||
| 2733 | } | ||
| 2734 | |||
| 2735 | lcd->screen &= ~LCD_SCREEN_SEN; /*disable the controller*/ | ||
| 2736 | |||
| 2737 | do { | ||
| 2738 | lcd->intstatus = lcd->intstatus; /*clear interrupts*/ | ||
| 2739 | au_sync(); | ||
| 2740 | /*wait for controller to shut down*/ | ||
| 2741 | } while ((lcd->intstatus & LCD_INT_SD) == 0); | ||
| 2742 | |||
| 2743 | /* Call shutdown of current panel (if up) */ | ||
| 2744 | /* this must occur last, because if an external clock is driving | ||
| 2745 | the controller, the clock cannot be turned off before first | ||
| 2746 | shutting down the controller. | ||
| 2747 | */ | ||
| 2748 | if (panel->device_shutdown != NULL) | ||
| 2749 | panel->device_shutdown(); | ||
| 2750 | } | ||
| 2751 | |||
| 2752 | /* Newpanel == NULL indicates a shutdown operation only */ | ||
| 2753 | if (newpanel == NULL) | ||
| 2754 | return; | ||
| 2755 | |||
| 2756 | panel = newpanel; | ||
| 2757 | |||
| 2758 | printk("Panel(%s), %dx%d\n", panel->name, panel->Xres, panel->Yres); | ||
| 2759 | |||
| 2760 | /* | ||
| 2761 | * Setup clocking if internal LCD clock source (assumes sys_auxpll valid) | ||
| 2762 | */ | ||
| 2763 | if (!(panel->mode_clkcontrol & LCD_CLKCONTROL_EXT)) | ||
| 2764 | { | ||
| 2765 | uint32 sys_clksrc; | ||
| 2766 | au_writel(panel->mode_auxpll, SYS_AUXPLL); | ||
| 2767 | sys_clksrc = au_readl(SYS_CLKSRC) & ~0x0000001f; | ||
| 2768 | sys_clksrc |= panel->mode_toyclksrc; | ||
| 2769 | au_writel(sys_clksrc, SYS_CLKSRC); | ||
| 2770 | } | ||
| 2771 | |||
| 2772 | /* | ||
| 2773 | * Configure panel timings | ||
| 2774 | */ | ||
| 2775 | lcd->screen = panel->mode_screen; | ||
| 2776 | lcd->horztiming = panel->mode_horztiming; | ||
| 2777 | lcd->verttiming = panel->mode_verttiming; | ||
| 2778 | lcd->clkcontrol = panel->mode_clkcontrol; | ||
| 2779 | lcd->pwmdiv = panel->mode_pwmdiv; | ||
| 2780 | lcd->pwmhi = panel->mode_pwmhi; | ||
| 2781 | lcd->outmask = panel->mode_outmask; | ||
| 2782 | lcd->fifoctrl = panel->mode_fifoctrl; | ||
| 2783 | au_sync(); | ||
| 2784 | |||
| 2785 | /* fixme: Check window settings to make sure still valid | ||
| 2786 | * for new geometry */ | ||
| 2787 | #if 0 | ||
| 2788 | au1200_setlocation(fbdev, 0, win->w[0].xpos, win->w[0].ypos); | ||
| 2789 | au1200_setlocation(fbdev, 1, win->w[1].xpos, win->w[1].ypos); | ||
| 2790 | au1200_setlocation(fbdev, 2, win->w[2].xpos, win->w[2].ypos); | ||
| 2791 | au1200_setlocation(fbdev, 3, win->w[3].xpos, win->w[3].ypos); | ||
| 2792 | #endif | ||
| 2793 | lcd->winenable = winenable; | ||
| 2794 | |||
| 2795 | /* | ||
| 2796 | * Re-enable screen now that it is configured | ||
| 2797 | */ | ||
| 2798 | lcd->screen |= LCD_SCREEN_SEN; | ||
| 2799 | au_sync(); | ||
| 2800 | |||
| 2801 | /* Call init of panel */ | ||
| 2802 | if (panel->device_init != NULL) panel->device_init(); | ||
| 2803 | |||
| 2804 | /* FIX!!!! not appropriate on panel change!!! Global setup/init */ | ||
| 2805 | lcd->intenable = 0; | ||
| 2806 | lcd->intstatus = ~0; | ||
| 2807 | lcd->backcolor = win->mode_backcolor; | ||
| 2808 | |||
| 2809 | /* Setup Color Key - FIX!!! */ | ||
| 2810 | lcd->colorkey = win->mode_colorkey; | ||
| 2811 | lcd->colorkeymsk = win->mode_colorkeymsk; | ||
| 2812 | |||
| 2813 | /* Setup HWCursor - FIX!!! Need to support this eventually */ | ||
| 2814 | lcd->hwc.cursorctrl = 0; | ||
| 2815 | lcd->hwc.cursorpos = 0; | ||
| 2816 | lcd->hwc.cursorcolor0 = 0; | ||
| 2817 | lcd->hwc.cursorcolor1 = 0; | ||
| 2818 | lcd->hwc.cursorcolor2 = 0; | ||
| 2819 | lcd->hwc.cursorcolor3 = 0; | ||
| 2820 | |||
| 2821 | |||
| 2822 | #if 0 | ||
| 2823 | #define D(X) printk("%25s: %08X\n", #X, X) | ||
| 2824 | D(lcd->screen); | ||
| 2825 | D(lcd->horztiming); | ||
| 2826 | D(lcd->verttiming); | ||
| 2827 | D(lcd->clkcontrol); | ||
| 2828 | D(lcd->pwmdiv); | ||
| 2829 | D(lcd->pwmhi); | ||
| 2830 | D(lcd->outmask); | ||
| 2831 | D(lcd->fifoctrl); | ||
| 2832 | D(lcd->window[0].winctrl0); | ||
| 2833 | D(lcd->window[0].winctrl1); | ||
| 2834 | D(lcd->window[0].winctrl2); | ||
| 2835 | D(lcd->window[0].winbuf0); | ||
| 2836 | D(lcd->window[0].winbuf1); | ||
| 2837 | D(lcd->window[0].winbufctrl); | ||
| 2838 | D(lcd->window[1].winctrl0); | ||
| 2839 | D(lcd->window[1].winctrl1); | ||
| 2840 | D(lcd->window[1].winctrl2); | ||
| 2841 | D(lcd->window[1].winbuf0); | ||
| 2842 | D(lcd->window[1].winbuf1); | ||
| 2843 | D(lcd->window[1].winbufctrl); | ||
| 2844 | D(lcd->window[2].winctrl0); | ||
| 2845 | D(lcd->window[2].winctrl1); | ||
| 2846 | D(lcd->window[2].winctrl2); | ||
| 2847 | D(lcd->window[2].winbuf0); | ||
| 2848 | D(lcd->window[2].winbuf1); | ||
| 2849 | D(lcd->window[2].winbufctrl); | ||
| 2850 | D(lcd->window[3].winctrl0); | ||
| 2851 | D(lcd->window[3].winctrl1); | ||
| 2852 | D(lcd->window[3].winctrl2); | ||
| 2853 | D(lcd->window[3].winbuf0); | ||
| 2854 | D(lcd->window[3].winbuf1); | ||
| 2855 | D(lcd->window[3].winbufctrl); | ||
| 2856 | D(lcd->winenable); | ||
| 2857 | D(lcd->intenable); | ||
| 2858 | D(lcd->intstatus); | ||
| 2859 | D(lcd->backcolor); | ||
| 2860 | D(lcd->winenable); | ||
| 2861 | D(lcd->colorkey); | ||
| 2862 | D(lcd->colorkeymsk); | ||
| 2863 | D(lcd->hwc.cursorctrl); | ||
| 2864 | D(lcd->hwc.cursorpos); | ||
| 2865 | D(lcd->hwc.cursorcolor0); | ||
| 2866 | D(lcd->hwc.cursorcolor1); | ||
| 2867 | D(lcd->hwc.cursorcolor2); | ||
| 2868 | D(lcd->hwc.cursorcolor3); | ||
| 2869 | #endif | ||
| 2870 | } | ||
| 2871 | |||
| 2872 | static void au1200_setmode(struct au1200fb_device *fbdev) | ||
| 2873 | { | ||
| 2874 | int plane = fbdev->plane; | ||
| 2875 | /* Window/plane setup */ | ||
| 2876 | lcd->window[plane].winctrl1 = ( 0 | ||
| 2877 | | LCD_WINCTRL1_PRI_N(plane) | ||
| 2878 | | win->w[plane].mode_winctrl1 /* FRM,CCO,PO,PIPE */ | ||
| 2879 | ) ; | ||
| 2880 | |||
| 2881 | au1200_setlocation(fbdev, plane, win->w[plane].xpos, win->w[plane].ypos); | ||
| 2882 | |||
| 2883 | lcd->window[plane].winctrl2 = ( 0 | ||
| 2884 | | LCD_WINCTRL2_CKMODE_00 | ||
| 2885 | | LCD_WINCTRL2_DBM | ||
| 2886 | | LCD_WINCTRL2_BX_N( fbdev->fb_info.fix.line_length) | ||
| 2887 | | LCD_WINCTRL2_SCX_1 | ||
| 2888 | | LCD_WINCTRL2_SCY_1 | ||
| 2889 | ) ; | ||
| 2890 | lcd->winenable |= win->w[plane].mode_winenable; | ||
| 2891 | au_sync(); | ||
| 2892 | } | ||
| 2893 | |||
| 2894 | |||
| 2895 | /* Inline helpers */ | ||
| 2896 | |||
| 2897 | /*#define panel_is_dual(panel) ((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ | ||
| 2898 | /*#define panel_is_active(panel)((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ | ||
| 2899 | |||
| 2900 | #define panel_is_color(panel) ((panel->mode_screen & LCD_SCREEN_PT) <= LCD_SCREEN_PT_CDSTN) | ||
| 2901 | |||
| 2902 | /* Bitfields format supported by the controller. */ | ||
| 2903 | static struct fb_bitfield rgb_bitfields[][4] = { | ||
| 2904 | /* Red, Green, Blue, Transp */ | ||
| 2905 | [LCD_WINCTRL1_FRM_16BPP655 >> 25] = | ||
| 2906 | { { 10, 6, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, | ||
| 2907 | |||
| 2908 | [LCD_WINCTRL1_FRM_16BPP565 >> 25] = | ||
| 2909 | { { 11, 5, 0 }, { 5, 6, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, | ||
| 2910 | |||
| 2911 | [LCD_WINCTRL1_FRM_16BPP556 >> 25] = | ||
| 2912 | { { 11, 5, 0 }, { 6, 5, 0 }, { 0, 6, 0 }, { 0, 0, 0 } }, | ||
| 2913 | |||
| 2914 | [LCD_WINCTRL1_FRM_16BPPI1555 >> 25] = | ||
| 2915 | { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, | ||
| 2916 | |||
| 2917 | [LCD_WINCTRL1_FRM_16BPPI5551 >> 25] = | ||
| 2918 | { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 0, 0 } }, | ||
| 2919 | |||
| 2920 | [LCD_WINCTRL1_FRM_16BPPA1555 >> 25] = | ||
| 2921 | { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 15, 1, 0 } }, | ||
| 2922 | |||
| 2923 | [LCD_WINCTRL1_FRM_16BPPA5551 >> 25] = | ||
| 2924 | { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 1, 0 } }, | ||
| 2925 | |||
| 2926 | [LCD_WINCTRL1_FRM_24BPP >> 25] = | ||
| 2927 | { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 } }, | ||
| 2928 | |||
| 2929 | [LCD_WINCTRL1_FRM_32BPP >> 25] = | ||
| 2930 | { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 24, 0, 0 } }, | ||
| 2931 | }; | ||
| 2932 | |||
| 2933 | /*-------------------------------------------------------------------------*/ | ||
| 2934 | |||
| 2935 | /* Helpers */ | ||
| 2936 | |||
| 2937 | static void au1200fb_update_fbinfo(struct fb_info *fbi) | ||
| 2938 | { | ||
| 2939 | /* FIX!!!! This also needs to take the window pixel format into account!!! */ | ||
| 2940 | |||
| 2941 | /* Update var-dependent FB info */ | ||
| 2942 | if (panel_is_color(panel)) { | ||
| 2943 | if (fbi->var.bits_per_pixel <= 8) { | ||
| 2944 | /* palettized */ | ||
| 2945 | fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
| 2946 | fbi->fix.line_length = fbi->var.xres_virtual / | ||
| 2947 | (8/fbi->var.bits_per_pixel); | ||
| 2948 | } else { | ||
| 2949 | /* non-palettized */ | ||
| 2950 | fbi->fix.visual = FB_VISUAL_TRUECOLOR; | ||
| 2951 | fbi->fix.line_length = fbi->var.xres_virtual * (fbi->var.bits_per_pixel / 8); | ||
| 2952 | } | ||
| 2953 | } else { | ||
| 2954 | /* mono FIX!!! mono 8 and 4 bits */ | ||
| 2955 | fbi->fix.visual = FB_VISUAL_MONO10; | ||
| 2956 | fbi->fix.line_length = fbi->var.xres_virtual / 8; | ||
| 2957 | } | ||
| 2958 | |||
| 2959 | fbi->screen_size = fbi->fix.line_length * fbi->var.yres_virtual; | ||
| 2960 | print_dbg("line length: %d\n", fbi->fix.line_length); | ||
| 2961 | print_dbg("bits_per_pixel: %d\n", fbi->var.bits_per_pixel); | ||
| 2962 | } | ||
| 2963 | |||
| 2964 | /*-------------------------------------------------------------------------*/ | ||
| 2965 | |||
| 2966 | /* AU1200 framebuffer driver */ | ||
| 2967 | |||
| 2968 | /* fb_check_var | ||
| 2969 | * Validate var settings with hardware restrictions and modify it if necessary | ||
| 2970 | */ | ||
| 2971 | static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, | ||
| 2972 | struct fb_info *fbi) | ||
| 2973 | { | ||
| 2974 | struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; | ||
| 2975 | u32 pixclock; | ||
| 2976 | int screen_size, plane; | ||
| 2977 | |||
| 2978 | plane = fbdev->plane; | ||
| 2979 | |||
| 2980 | /* Make sure that the mode respect all LCD controller and | ||
| 2981 | * panel restrictions. */ | ||
| 2982 | var->xres = win->w[plane].xres; | ||
| 2983 | var->yres = win->w[plane].yres; | ||
| 2984 | |||
| 2985 | /* No need for virtual resolution support */ | ||
| 2986 | var->xres_virtual = var->xres; | ||
| 2987 | var->yres_virtual = var->yres; | ||
| 2988 | |||
| 2989 | var->bits_per_pixel = winbpp(win->w[plane].mode_winctrl1); | ||
| 2990 | |||
| 2991 | screen_size = var->xres_virtual * var->yres_virtual; | ||
| 2992 | if (var->bits_per_pixel > 8) screen_size *= (var->bits_per_pixel / 8); | ||
| 2993 | else screen_size /= (8/var->bits_per_pixel); | ||
| 2994 | |||
| 2995 | if (fbdev->fb_len < screen_size) | ||
| 2996 | return -EINVAL; /* Virtual screen is to big, abort */ | ||
| 2997 | |||
| 2998 | /* FIX!!!! what are the implicaitons of ignoring this for windows ??? */ | ||
| 2999 | /* The max LCD clock is fixed to 48MHz (value of AUX_CLK). The pixel | ||
| 3000 | * clock can only be obtain by dividing this value by an even integer. | ||
| 3001 | * Fallback to a slower pixel clock if necessary. */ | ||
| 3002 | pixclock = max((u32)(PICOS2KHZ(var->pixclock) * 1000), fbi->monspecs.dclkmin); | ||
| 3003 | pixclock = min(pixclock, min(fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2)); | ||
| 3004 | |||
| 3005 | if (AU1200_LCD_MAX_CLK % pixclock) { | ||
| 3006 | int diff = AU1200_LCD_MAX_CLK % pixclock; | ||
| 3007 | pixclock -= diff; | ||
| 3008 | } | ||
| 3009 | |||
| 3010 | var->pixclock = KHZ2PICOS(pixclock/1000); | ||
| 3011 | #if 0 | ||
| 3012 | if (!panel_is_active(panel)) { | ||
| 3013 | int pcd = AU1200_LCD_MAX_CLK / (pixclock * 2) - 1; | ||
| 3014 | |||
| 3015 | if (!panel_is_color(panel) | ||
| 3016 | && (panel->control_base & LCD_CONTROL_MPI) && (pcd < 3)) { | ||
| 3017 | /* STN 8bit mono panel support is up to 6MHz pixclock */ | ||
| 3018 | var->pixclock = KHZ2PICOS(6000); | ||
| 3019 | } else if (!pcd) { | ||
| 3020 | /* Other STN panel support is up to 12MHz */ | ||
| 3021 | var->pixclock = KHZ2PICOS(12000); | ||
| 3022 | } | ||
| 3023 | } | ||
| 3024 | #endif | ||
| 3025 | /* Set bitfield accordingly */ | ||
| 3026 | switch (var->bits_per_pixel) { | ||
| 3027 | case 16: | ||
| 3028 | { | ||
| 3029 | /* 16bpp True color. | ||
| 3030 | * These must be set to MATCH WINCTRL[FORM] */ | ||
| 3031 | int idx; | ||
| 3032 | idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; | ||
| 3033 | var->red = rgb_bitfields[idx][0]; | ||
| 3034 | var->green = rgb_bitfields[idx][1]; | ||
| 3035 | var->blue = rgb_bitfields[idx][2]; | ||
| 3036 | var->transp = rgb_bitfields[idx][3]; | ||
| 3037 | break; | ||
| 3038 | } | ||
| 3039 | |||
| 3040 | case 32: | ||
| 3041 | { | ||
| 3042 | /* 32bpp True color. | ||
| 3043 | * These must be set to MATCH WINCTRL[FORM] */ | ||
| 3044 | int idx; | ||
| 3045 | idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; | ||
| 3046 | var->red = rgb_bitfields[idx][0]; | ||
| 3047 | var->green = rgb_bitfields[idx][1]; | ||
| 3048 | var->blue = rgb_bitfields[idx][2]; | ||
| 3049 | var->transp = rgb_bitfields[idx][3]; | ||
| 3050 | break; | ||
| 3051 | } | ||
| 3052 | default: | ||
| 3053 | print_dbg("Unsupported depth %dbpp", var->bits_per_pixel); | ||
| 3054 | return -EINVAL; | ||
| 3055 | } | ||
| 3056 | |||
| 3057 | return 0; | ||
| 3058 | } | ||
| 3059 | |||
| 3060 | /* fb_set_par | ||
| 3061 | * Set hardware with var settings. This will enable the controller with a | ||
| 3062 | * specific mode, normally validated with the fb_check_var method | ||
| 3063 | */ | ||
| 3064 | static int au1200fb_fb_set_par(struct fb_info *fbi) | ||
| 3065 | { | ||
| 3066 | struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; | ||
| 3067 | |||
| 3068 | au1200fb_update_fbinfo(fbi); | ||
| 3069 | au1200_setmode(fbdev); | ||
| 3070 | |||
| 3071 | return 0; | ||
| 3072 | } | ||
| 3073 | |||
| 3074 | /* fb_setcolreg | ||
| 3075 | * Set color in LCD palette. | ||
| 3076 | */ | ||
| 3077 | static int au1200fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
| 3078 | unsigned blue, unsigned transp, struct fb_info *fbi) | ||
| 3079 | { | ||
| 3080 | volatile u32 *palette = lcd->palette; | ||
| 3081 | u32 value; | ||
| 3082 | |||
| 3083 | if (regno > (AU1200_LCD_NBR_PALETTE_ENTRIES - 1)) | ||
| 3084 | return -EINVAL; | ||
| 3085 | |||
| 3086 | if (fbi->var.grayscale) { | ||
| 3087 | /* Convert color to grayscale */ | ||
| 3088 | red = green = blue = | ||
| 3089 | (19595 * red + 38470 * green + 7471 * blue) >> 16; | ||
| 3090 | } | ||
| 3091 | |||
| 3092 | if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) { | ||
| 3093 | /* Place color in the pseudopalette */ | ||
| 3094 | if (regno > 16) | ||
| 3095 | return -EINVAL; | ||
| 3096 | |||
| 3097 | palette = (u32*) fbi->pseudo_palette; | ||
| 3098 | |||
| 3099 | red >>= (16 - fbi->var.red.length); | ||
| 3100 | green >>= (16 - fbi->var.green.length); | ||
| 3101 | blue >>= (16 - fbi->var.blue.length); | ||
| 3102 | |||
| 3103 | value = (red << fbi->var.red.offset) | | ||
| 3104 | (green << fbi->var.green.offset)| | ||
| 3105 | (blue << fbi->var.blue.offset); | ||
| 3106 | value &= 0xFFFF; | ||
| 3107 | |||
| 3108 | } else if (1 /*FIX!!! panel_is_active(fbdev->panel)*/) { | ||
| 3109 | /* COLOR TFT PALLETTIZED (use RGB 565) */ | ||
| 3110 | value = (red & 0xF800)|((green >> 5) & | ||
| 3111 | 0x07E0)|((blue >> 11) & 0x001F); | ||
| 3112 | value &= 0xFFFF; | ||
| 3113 | |||
| 3114 | } else if (0 /*panel_is_color(fbdev->panel)*/) { | ||
| 3115 | /* COLOR STN MODE */ | ||
| 3116 | value = 0x1234; | ||
| 3117 | value &= 0xFFF; | ||
| 3118 | } else { | ||
| 3119 | /* MONOCHROME MODE */ | ||
| 3120 | value = (green >> 12) & 0x000F; | ||
| 3121 | value &= 0xF; | ||
| 3122 | } | ||
| 3123 | |||
| 3124 | palette[regno] = value; | ||
| 3125 | |||
| 3126 | return 0; | ||
| 3127 | } | ||
| 3128 | |||
| 3129 | /* fb_blank | ||
| 3130 | * Blank the screen. Depending on the mode, the screen will be | ||
| 3131 | * activated with the backlight color, or desactivated | ||
| 3132 | */ | ||
| 3133 | static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi) | ||
| 3134 | { | ||
| 3135 | /* Short-circuit screen blanking */ | ||
| 3136 | if (noblanking) | ||
| 3137 | return 0; | ||
| 3138 | |||
| 3139 | switch (blank_mode) { | ||
| 3140 | |||
| 3141 | case FB_BLANK_UNBLANK: | ||
| 3142 | case FB_BLANK_NORMAL: | ||
| 3143 | /* printk("turn on panel\n"); */ | ||
| 3144 | au1200_setpanel(panel); | ||
| 3145 | break; | ||
| 3146 | case FB_BLANK_VSYNC_SUSPEND: | ||
| 3147 | case FB_BLANK_HSYNC_SUSPEND: | ||
| 3148 | case FB_BLANK_POWERDOWN: | ||
| 3149 | /* printk("turn off panel\n"); */ | ||
| 3150 | au1200_setpanel(NULL); | ||
| 3151 | break; | ||
| 3152 | default: | ||
| 3153 | break; | ||
| 3154 | |||
| 3155 | } | ||
| 3156 | |||
| 3157 | /* FB_BLANK_NORMAL is a soft blank */ | ||
| 3158 | return (blank_mode == FB_BLANK_NORMAL) ? -EINVAL : 0; | ||
| 3159 | } | ||
| 3160 | |||
| 3161 | /* fb_mmap | ||
| 3162 | * Map video memory in user space. We don't use the generic fb_mmap | ||
| 3163 | * method mainly to allow the use of the TLB streaming flag (CCA=6) | ||
| 3164 | */ | ||
| 3165 | static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) | ||
| 3166 | |||
| 3167 | { | ||
| 3168 | unsigned int len; | ||
| 3169 | unsigned long start=0, off; | ||
| 3170 | struct au1200fb_device *fbdev = (struct au1200fb_device *) info; | ||
| 3171 | |||
| 3172 | #ifdef CONFIG_PM | ||
| 3173 | au1xxx_pm_access(LCD_pm_dev); | ||
| 3174 | #endif | ||
| 3175 | |||
| 3176 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { | ||
| 3177 | return -EINVAL; | ||
| 3178 | } | ||
| 3179 | |||
| 3180 | start = fbdev->fb_phys & PAGE_MASK; | ||
| 3181 | len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len); | ||
| 3182 | |||
| 3183 | off = vma->vm_pgoff << PAGE_SHIFT; | ||
| 3184 | |||
| 3185 | if ((vma->vm_end - vma->vm_start + off) > len) { | ||
| 3186 | return -EINVAL; | ||
| 3187 | } | ||
| 3188 | |||
| 3189 | off += start; | ||
| 3190 | vma->vm_pgoff = off >> PAGE_SHIFT; | ||
| 3191 | |||
| 3192 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
| 3193 | pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */ | ||
| 3194 | |||
| 3195 | vma->vm_flags |= VM_IO; | ||
| 3196 | |||
| 3197 | return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, | ||
| 3198 | vma->vm_end - vma->vm_start, | ||
| 3199 | vma->vm_page_prot); | ||
| 3200 | |||
| 3201 | return 0; | ||
| 3202 | } | ||
| 3203 | |||
| 3204 | static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) | ||
| 3205 | { | ||
| 3206 | |||
| 3207 | unsigned int hi1, divider; | ||
| 3208 | |||
| 3209 | /* SCREEN_SIZE: user cannot reset size, must switch panel choice */ | ||
| 3210 | |||
| 3211 | if (pdata->flags & SCREEN_BACKCOLOR) | ||
| 3212 | lcd->backcolor = pdata->backcolor; | ||
| 3213 | |||
| 3214 | if (pdata->flags & SCREEN_BRIGHTNESS) { | ||
| 3215 | |||
| 3216 | // limit brightness pwm duty to >= 30/1600 | ||
| 3217 | if (pdata->brightness < 30) { | ||
| 3218 | pdata->brightness = 30; | ||
| 3219 | } | ||
| 3220 | divider = (lcd->pwmdiv & 0x3FFFF) + 1; | ||
| 3221 | hi1 = (lcd->pwmhi >> 16) + 1; | ||
| 3222 | hi1 = (((pdata->brightness & 0xFF)+1) * divider >> 8); | ||
| 3223 | lcd->pwmhi &= 0xFFFF; | ||
| 3224 | lcd->pwmhi |= (hi1 << 16); | ||
| 3225 | } | ||
| 3226 | |||
| 3227 | if (pdata->flags & SCREEN_COLORKEY) | ||
| 3228 | lcd->colorkey = pdata->colorkey; | ||
| 3229 | |||
| 3230 | if (pdata->flags & SCREEN_MASK) | ||
| 3231 | lcd->colorkeymsk = pdata->mask; | ||
| 3232 | au_sync(); | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | static void get_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) | ||
| 3236 | { | ||
| 3237 | unsigned int hi1, divider; | ||
| 3238 | |||
| 3239 | pdata->xsize = ((lcd->screen & LCD_SCREEN_SX) >> 19) + 1; | ||
| 3240 | pdata->ysize = ((lcd->screen & LCD_SCREEN_SY) >> 8) + 1; | ||
| 3241 | |||
| 3242 | pdata->backcolor = lcd->backcolor; | ||
| 3243 | pdata->colorkey = lcd->colorkey; | ||
| 3244 | pdata->mask = lcd->colorkeymsk; | ||
| 3245 | |||
| 3246 | // brightness | ||
| 3247 | hi1 = (lcd->pwmhi >> 16) + 1; | ||
| 3248 | divider = (lcd->pwmdiv & 0x3FFFF) + 1; | ||
| 3249 | pdata->brightness = ((hi1 << 8) / divider) - 1; | ||
| 3250 | au_sync(); | ||
| 3251 | } | ||
| 3252 | |||
| 3253 | static void set_window(unsigned int plane, | ||
| 3254 | struct au1200_lcd_window_regs_t *pdata) | ||
| 3255 | { | ||
| 3256 | unsigned int val, bpp; | ||
| 3257 | |||
| 3258 | /* Window control register 0 */ | ||
| 3259 | if (pdata->flags & WIN_POSITION) { | ||
| 3260 | val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_OX | | ||
| 3261 | LCD_WINCTRL0_OY); | ||
| 3262 | val |= ((pdata->xpos << 21) & LCD_WINCTRL0_OX); | ||
| 3263 | val |= ((pdata->ypos << 10) & LCD_WINCTRL0_OY); | ||
| 3264 | lcd->window[plane].winctrl0 = val; | ||
| 3265 | } | ||
| 3266 | if (pdata->flags & WIN_ALPHA_COLOR) { | ||
| 3267 | val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_A); | ||
| 3268 | val |= ((pdata->alpha_color << 2) & LCD_WINCTRL0_A); | ||
| 3269 | lcd->window[plane].winctrl0 = val; | ||
| 3270 | } | ||
| 3271 | if (pdata->flags & WIN_ALPHA_MODE) { | ||
| 3272 | val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_AEN); | ||
| 3273 | val |= ((pdata->alpha_mode << 1) & LCD_WINCTRL0_AEN); | ||
| 3274 | lcd->window[plane].winctrl0 = val; | ||
| 3275 | } | ||
| 3276 | |||
| 3277 | /* Window control register 1 */ | ||
| 3278 | if (pdata->flags & WIN_PRIORITY) { | ||
| 3279 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PRI); | ||
| 3280 | val |= ((pdata->priority << 30) & LCD_WINCTRL1_PRI); | ||
| 3281 | lcd->window[plane].winctrl1 = val; | ||
| 3282 | } | ||
| 3283 | if (pdata->flags & WIN_CHANNEL) { | ||
| 3284 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PIPE); | ||
| 3285 | val |= ((pdata->channel << 29) & LCD_WINCTRL1_PIPE); | ||
| 3286 | lcd->window[plane].winctrl1 = val; | ||
| 3287 | } | ||
| 3288 | if (pdata->flags & WIN_BUFFER_FORMAT) { | ||
| 3289 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_FRM); | ||
| 3290 | val |= ((pdata->buffer_format << 25) & LCD_WINCTRL1_FRM); | ||
| 3291 | lcd->window[plane].winctrl1 = val; | ||
| 3292 | } | ||
| 3293 | if (pdata->flags & WIN_COLOR_ORDER) { | ||
| 3294 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_CCO); | ||
| 3295 | val |= ((pdata->color_order << 24) & LCD_WINCTRL1_CCO); | ||
| 3296 | lcd->window[plane].winctrl1 = val; | ||
| 3297 | } | ||
| 3298 | if (pdata->flags & WIN_PIXEL_ORDER) { | ||
| 3299 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PO); | ||
| 3300 | val |= ((pdata->pixel_order << 22) & LCD_WINCTRL1_PO); | ||
| 3301 | lcd->window[plane].winctrl1 = val; | ||
| 3302 | } | ||
| 3303 | if (pdata->flags & WIN_SIZE) { | ||
| 3304 | val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_SZX | | ||
| 3305 | LCD_WINCTRL1_SZY); | ||
| 3306 | val |= (((pdata->xsize << 11) - 1) & LCD_WINCTRL1_SZX); | ||
| 3307 | val |= (((pdata->ysize) - 1) & LCD_WINCTRL1_SZY); | ||
| 3308 | lcd->window[plane].winctrl1 = val; | ||
| 3309 | /* program buffer line width */ | ||
| 3310 | bpp = winbpp(val) / 8; | ||
| 3311 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_BX); | ||
| 3312 | val |= (((pdata->xsize * bpp) << 8) & LCD_WINCTRL2_BX); | ||
| 3313 | lcd->window[plane].winctrl2 = val; | ||
| 3314 | } | ||
| 3315 | |||
| 3316 | /* Window control register 2 */ | ||
| 3317 | if (pdata->flags & WIN_COLORKEY_MODE) { | ||
| 3318 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_CKMODE); | ||
| 3319 | val |= ((pdata->colorkey_mode << 24) & LCD_WINCTRL2_CKMODE); | ||
| 3320 | lcd->window[plane].winctrl2 = val; | ||
| 3321 | } | ||
| 3322 | if (pdata->flags & WIN_DOUBLE_BUFFER_MODE) { | ||
| 3323 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_DBM); | ||
| 3324 | val |= ((pdata->double_buffer_mode << 23) & LCD_WINCTRL2_DBM); | ||
| 3325 | lcd->window[plane].winctrl2 = val; | ||
| 3326 | } | ||
| 3327 | if (pdata->flags & WIN_RAM_ARRAY_MODE) { | ||
| 3328 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_RAM); | ||
| 3329 | val |= ((pdata->ram_array_mode << 21) & LCD_WINCTRL2_RAM); | ||
| 3330 | lcd->window[plane].winctrl2 = val; | ||
| 3331 | } | ||
| 3332 | |||
| 3333 | /* Buffer line width programmed with WIN_SIZE */ | ||
| 3334 | |||
| 3335 | if (pdata->flags & WIN_BUFFER_SCALE) { | ||
| 3336 | val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_SCX | | ||
| 3337 | LCD_WINCTRL2_SCY); | ||
| 3338 | val |= ((pdata->xsize << 11) & LCD_WINCTRL2_SCX); | ||
| 3339 | val |= ((pdata->ysize) & LCD_WINCTRL2_SCY); | ||
| 3340 | lcd->window[plane].winctrl2 = val; | ||
| 3341 | } | ||
| 3342 | |||
| 3343 | if (pdata->flags & WIN_ENABLE) { | ||
| 3344 | val = lcd->winenable; | ||
| 3345 | val &= ~(1<<plane); | ||
| 3346 | val |= (pdata->enable & 1) << plane; | ||
| 3347 | lcd->winenable = val; | ||
| 3348 | } | ||
| 3349 | au_sync(); | ||
| 3350 | } | ||
| 3351 | |||
| 3352 | static void get_window(unsigned int plane, | ||
| 3353 | struct au1200_lcd_window_regs_t *pdata) | ||
| 3354 | { | ||
| 3355 | /* Window control register 0 */ | ||
| 3356 | pdata->xpos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OX) >> 21; | ||
| 3357 | pdata->ypos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OY) >> 10; | ||
| 3358 | pdata->alpha_color = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_A) >> 2; | ||
| 3359 | pdata->alpha_mode = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_AEN) >> 1; | ||
| 3360 | |||
| 3361 | /* Window control register 1 */ | ||
| 3362 | pdata->priority = (lcd->window[plane].winctrl1& LCD_WINCTRL1_PRI) >> 30; | ||
| 3363 | pdata->channel = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PIPE) >> 29; | ||
| 3364 | pdata->buffer_format = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_FRM) >> 25; | ||
| 3365 | pdata->color_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_CCO) >> 24; | ||
| 3366 | pdata->pixel_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PO) >> 22; | ||
| 3367 | pdata->xsize = ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZX) >> 11) + 1; | ||
| 3368 | pdata->ysize = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZY) + 1; | ||
| 3369 | |||
| 3370 | /* Window control register 2 */ | ||
| 3371 | pdata->colorkey_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_CKMODE) >> 24; | ||
| 3372 | pdata->double_buffer_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_DBM) >> 23; | ||
| 3373 | pdata->ram_array_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_RAM) >> 21; | ||
| 3374 | |||
| 3375 | pdata->enable = (lcd->winenable >> plane) & 1; | ||
| 3376 | au_sync(); | ||
| 3377 | } | ||
| 3378 | |||
| 3379 | static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd, | ||
| 3380 | unsigned long arg) | ||
| 3381 | { | ||
| 3382 | int plane; | ||
| 3383 | int val; | ||
| 3384 | |||
| 3385 | #ifdef CONFIG_PM | ||
| 3386 | au1xxx_pm_access(LCD_pm_dev); | ||
| 3387 | #endif | ||
| 3388 | |||
| 3389 | plane = fbinfo2index(info); | ||
| 3390 | print_dbg("au1200fb: ioctl %d on plane %d\n", cmd, plane); | ||
| 3391 | |||
| 3392 | if (cmd == AU1200_LCD_FB_IOCTL) { | ||
| 3393 | struct au1200_lcd_iodata_t iodata; | ||
| 3394 | |||
| 3395 | if (copy_from_user(&iodata, (void __user *) arg, sizeof(iodata))) | ||
| 3396 | return -EFAULT; | ||
| 3397 | |||
| 3398 | print_dbg("FB IOCTL called\n"); | ||
| 3399 | |||
| 3400 | switch (iodata.subcmd) { | ||
| 3401 | case AU1200_LCD_SET_SCREEN: | ||
| 3402 | print_dbg("AU1200_LCD_SET_SCREEN\n"); | ||
| 3403 | set_global(cmd, &iodata.global); | ||
| 3404 | break; | ||
| 3405 | |||
| 3406 | case AU1200_LCD_GET_SCREEN: | ||
| 3407 | print_dbg("AU1200_LCD_GET_SCREEN\n"); | ||
| 3408 | get_global(cmd, &iodata.global); | ||
| 3409 | break; | ||
| 3410 | |||
| 3411 | case AU1200_LCD_SET_WINDOW: | ||
| 3412 | print_dbg("AU1200_LCD_SET_WINDOW\n"); | ||
| 3413 | set_window(plane, &iodata.window); | ||
| 3414 | break; | ||
| 3415 | |||
| 3416 | case AU1200_LCD_GET_WINDOW: | ||
| 3417 | print_dbg("AU1200_LCD_GET_WINDOW\n"); | ||
| 3418 | get_window(plane, &iodata.window); | ||
| 3419 | break; | ||
| 3420 | |||
| 3421 | case AU1200_LCD_SET_PANEL: | ||
| 3422 | print_dbg("AU1200_LCD_SET_PANEL\n"); | ||
| 3423 | if ((iodata.global.panel_choice >= 0) && | ||
| 3424 | (iodata.global.panel_choice < | ||
| 3425 | NUM_PANELS)) | ||
| 3426 | { | ||
| 3427 | struct panel_settings *newpanel; | ||
| 3428 | panel_index = iodata.global.panel_choice; | ||
| 3429 | newpanel = &known_lcd_panels[panel_index]; | ||
| 3430 | au1200_setpanel(newpanel); | ||
| 3431 | } | ||
| 3432 | break; | ||
| 3433 | |||
| 3434 | case AU1200_LCD_GET_PANEL: | ||
| 3435 | print_dbg("AU1200_LCD_GET_PANEL\n"); | ||
| 3436 | iodata.global.panel_choice = panel_index; | ||
| 3437 | break; | ||
| 3438 | |||
| 3439 | default: | ||
| 3440 | return -EINVAL; | ||
| 3441 | } | ||
| 3442 | |||
| 3443 | val = copy_to_user((void __user *) arg, &iodata, sizeof(iodata)); | ||
| 3444 | if (val) { | ||
| 3445 | print_dbg("error: could not copy %d bytes\n", val); | ||
| 3446 | return -EFAULT; | ||
| 3447 | } | ||
| 3448 | } | ||
| 3449 | |||
| 3450 | return 0; | ||
| 3451 | } | ||
| 3452 | |||
| 3453 | |||
| 3454 | static struct fb_ops au1200fb_fb_ops = { | ||
| 3455 | .owner = THIS_MODULE, | ||
| 3456 | .fb_check_var = au1200fb_fb_check_var, | ||
| 3457 | .fb_set_par = au1200fb_fb_set_par, | ||
| 3458 | .fb_setcolreg = au1200fb_fb_setcolreg, | ||
| 3459 | .fb_blank = au1200fb_fb_blank, | ||
| 3460 | .fb_fillrect = cfb_fillrect, | ||
| 3461 | .fb_copyarea = cfb_copyarea, | ||
| 3462 | .fb_imageblit = cfb_imageblit, | ||
| 3463 | .fb_sync = NULL, | ||
| 3464 | .fb_ioctl = au1200fb_ioctl, | ||
| 3465 | .fb_mmap = au1200fb_fb_mmap, | ||
| 3466 | }; | ||
| 3467 | |||
| 3468 | /*-------------------------------------------------------------------------*/ | ||
| 3469 | |||
| 3470 | static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id, struct pt_regs *regs) | ||
| 3471 | { | ||
| 3472 | /* Nothing to do for now, just clear any pending interrupt */ | ||
| 3473 | lcd->intstatus = lcd->intstatus; | ||
| 3474 | au_sync(); | ||
| 3475 | |||
| 3476 | return IRQ_HANDLED; | ||
| 3477 | } | ||
| 3478 | |||
| 3479 | /*-------------------------------------------------------------------------*/ | ||
| 3480 | |||
| 3481 | /* AU1200 LCD device probe helpers */ | ||
| 3482 | |||
| 3483 | static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) | ||
| 3484 | { | ||
| 3485 | struct fb_info *fbi = &fbdev->fb_info; | ||
| 3486 | int bpp; | ||
| 3487 | |||
| 3488 | memset(fbi, 0, sizeof(struct fb_info)); | ||
| 3489 | fbi->fbops = &au1200fb_fb_ops; | ||
| 3490 | |||
| 3491 | bpp = winbpp(win->w[fbdev->plane].mode_winctrl1); | ||
| 3492 | |||
| 3493 | /* Copy monitor specs from panel data */ | ||
| 3494 | /* fixme: we're setting up LCD controller windows, so these dont give a | ||
| 3495 | damn as to what the monitor specs are (the panel itself does, but that | ||
| 3496 | isnt done here...so maybe need a generic catchall monitor setting??? */ | ||
| 3497 | memcpy(&fbi->monspecs, &panel->monspecs, sizeof(struct fb_monspecs)); | ||
| 3498 | |||
| 3499 | /* We first try the user mode passed in argument. If that failed, | ||
| 3500 | * or if no one has been specified, we default to the first mode of the | ||
| 3501 | * panel list. Note that after this call, var data will be set */ | ||
| 3502 | if (!fb_find_mode(&fbi->var, | ||
| 3503 | fbi, | ||
| 3504 | NULL, /* drv_info.opt_mode, */ | ||
| 3505 | fbi->monspecs.modedb, | ||
| 3506 | fbi->monspecs.modedb_len, | ||
| 3507 | fbi->monspecs.modedb, | ||
| 3508 | bpp)) { | ||
| 3509 | |||
| 3510 | print_err("Cannot find valid mode for panel %s", panel->name); | ||
| 3511 | return -EFAULT; | ||
| 3512 | } | ||
| 3513 | |||
| 3514 | fbi->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); | ||
| 3515 | if (!fbi->pseudo_palette) { | ||
| 3516 | return -ENOMEM; | ||
| 3517 | } | ||
| 3518 | memset(fbi->pseudo_palette, 0, sizeof(u32) * 16); | ||
| 3519 | |||
| 3520 | if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { | ||
| 3521 | print_err("Fail to allocate colormap (%d entries)", | ||
| 3522 | AU1200_LCD_NBR_PALETTE_ENTRIES); | ||
| 3523 | kfree(fbi->pseudo_palette); | ||
| 3524 | return -EFAULT; | ||
| 3525 | } | ||
| 3526 | |||
| 3527 | strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id)); | ||
| 3528 | fbi->fix.smem_start = fbdev->fb_phys; | ||
| 3529 | fbi->fix.smem_len = fbdev->fb_len; | ||
| 3530 | fbi->fix.type = FB_TYPE_PACKED_PIXELS; | ||
| 3531 | fbi->fix.xpanstep = 0; | ||
| 3532 | fbi->fix.ypanstep = 0; | ||
| 3533 | fbi->fix.mmio_start = 0; | ||
| 3534 | fbi->fix.mmio_len = 0; | ||
| 3535 | fbi->fix.accel = FB_ACCEL_NONE; | ||
| 3536 | |||
| 3537 | fbi->screen_base = (char __iomem *) fbdev->fb_mem; | ||
| 3538 | |||
| 3539 | au1200fb_update_fbinfo(fbi); | ||
| 3540 | |||
| 3541 | return 0; | ||
| 3542 | } | ||
| 3543 | |||
| 3544 | /*-------------------------------------------------------------------------*/ | ||
| 3545 | |||
| 3546 | /* AU1200 LCD controller device driver */ | ||
| 3547 | |||
| 3548 | static int au1200fb_drv_probe(struct device *dev) | ||
| 3549 | { | ||
| 3550 | struct au1200fb_device *fbdev; | ||
| 3551 | unsigned long page; | ||
| 3552 | int bpp, plane, ret; | ||
| 3553 | |||
| 3554 | if (!dev) | ||
| 3555 | return -EINVAL; | ||
| 3556 | |||
| 3557 | for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { | ||
| 3558 | bpp = winbpp(win->w[plane].mode_winctrl1); | ||
| 3559 | if (win->w[plane].xres == 0) | ||
| 3560 | win->w[plane].xres = panel->Xres; | ||
| 3561 | if (win->w[plane].yres == 0) | ||
| 3562 | win->w[plane].yres = panel->Yres; | ||
| 3563 | |||
| 3564 | fbdev = &_au1200fb_devices[plane]; | ||
| 3565 | memset(fbdev, 0, sizeof(struct au1200fb_device)); | ||
| 3566 | fbdev->plane = plane; | ||
| 3567 | |||
| 3568 | /* Allocate the framebuffer to the maximum screen size */ | ||
| 3569 | fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8; | ||
| 3570 | |||
| 3571 | fbdev->fb_mem = dma_alloc_noncoherent(dev, | ||
| 3572 | PAGE_ALIGN(fbdev->fb_len), | ||
| 3573 | &fbdev->fb_phys, GFP_KERNEL); | ||
| 3574 | if (!fbdev->fb_mem) { | ||
| 3575 | print_err("fail to allocate frambuffer (size: %dK))", | ||
| 3576 | fbdev->fb_len / 1024); | ||
| 3577 | return -ENOMEM; | ||
| 3578 | } | ||
| 3579 | |||
| 3580 | /* | ||
| 3581 | * Set page reserved so that mmap will work. This is necessary | ||
| 3582 | * since we'll be remapping normal memory. | ||
| 3583 | */ | ||
| 3584 | for (page = (unsigned long)fbdev->fb_phys; | ||
| 3585 | page < PAGE_ALIGN((unsigned long)fbdev->fb_phys + | ||
| 3586 | fbdev->fb_len); | ||
| 3587 | page += PAGE_SIZE) { | ||
| 3588 | SetPageReserved(pfn_to_page(page >> PAGE_SHIFT)); /* LCD DMA is NOT coherent on Au1200 */ | ||
| 3589 | } | ||
| 3590 | print_dbg("Framebuffer memory map at %p", fbdev->fb_mem); | ||
| 3591 | print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); | ||
| 3592 | |||
| 3593 | /* Init FB data */ | ||
| 3594 | if ((ret = au1200fb_init_fbinfo(fbdev)) < 0) | ||
| 3595 | goto failed; | ||
| 3596 | |||
| 3597 | /* Register new framebuffer */ | ||
| 3598 | if ((ret = register_framebuffer(&fbdev->fb_info)) < 0) { | ||
| 3599 | print_err("cannot register new framebuffer"); | ||
| 3600 | goto failed; | ||
| 3601 | } | ||
| 3602 | |||
| 3603 | au1200fb_fb_set_par(&fbdev->fb_info); | ||
| 3604 | |||
| 3605 | #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) | ||
| 3606 | if (plane == 0) | ||
| 3607 | if (fb_prepare_logo(&fbdev->fb_info, FB_ROTATE_UR)) { | ||
| 3608 | /* Start display and show logo on boot */ | ||
| 3609 | fb_set_cmap(&fbdev->fb_info.cmap, | ||
| 3610 | &fbdev->fb_info); | ||
| 3611 | |||
| 3612 | fb_show_logo(&fbdev->fb_info, FB_ROTATE_UR); | ||
| 3613 | } | ||
| 3614 | #endif | ||
| 3615 | } | ||
| 3616 | |||
| 3617 | /* Now hook interrupt too */ | ||
| 3618 | if ((ret = request_irq(AU1200_LCD_INT, au1200fb_handle_irq, | ||
| 3619 | SA_INTERRUPT | SA_SHIRQ, "lcd", (void *)dev)) < 0) { | ||
| 3620 | print_err("fail to request interrupt line %d (err: %d)", | ||
| 3621 | AU1200_LCD_INT, ret); | ||
| 3622 | goto failed; | ||
| 3623 | } | ||
| 3624 | |||
| 3625 | return 0; | ||
| 3626 | |||
| 3627 | failed: | ||
| 3628 | /* NOTE: This only does the current plane/window that failed; others are still active */ | ||
| 3629 | if (fbdev->fb_mem) | ||
| 3630 | dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), | ||
| 3631 | fbdev->fb_mem, fbdev->fb_phys); | ||
| 3632 | if (fbdev->fb_info.cmap.len != 0) | ||
| 3633 | fb_dealloc_cmap(&fbdev->fb_info.cmap); | ||
| 3634 | if (fbdev->fb_info.pseudo_palette) | ||
| 3635 | kfree(fbdev->fb_info.pseudo_palette); | ||
| 3636 | if (plane == 0) | ||
| 3637 | free_irq(AU1200_LCD_INT, (void*)dev); | ||
| 3638 | return ret; | ||
| 3639 | } | ||
| 3640 | |||
| 3641 | static int au1200fb_drv_remove(struct device *dev) | ||
| 3642 | { | ||
| 3643 | struct au1200fb_device *fbdev; | ||
| 3644 | int plane; | ||
| 3645 | |||
| 3646 | if (!dev) | ||
| 3647 | return -ENODEV; | ||
| 3648 | |||
| 3649 | /* Turn off the panel */ | ||
| 3650 | au1200_setpanel(NULL); | ||
| 3651 | |||
| 3652 | for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) | ||
| 3653 | { | ||
| 3654 | fbdev = &_au1200fb_devices[plane]; | ||
| 3655 | |||
| 3656 | /* Clean up all probe data */ | ||
| 3657 | unregister_framebuffer(&fbdev->fb_info); | ||
| 3658 | if (fbdev->fb_mem) | ||
| 3659 | dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), | ||
| 3660 | fbdev->fb_mem, fbdev->fb_phys); | ||
| 3661 | if (fbdev->fb_info.cmap.len != 0) | ||
| 3662 | fb_dealloc_cmap(&fbdev->fb_info.cmap); | ||
| 3663 | if (fbdev->fb_info.pseudo_palette) | ||
| 3664 | kfree(fbdev->fb_info.pseudo_palette); | ||
| 3665 | } | ||
| 3666 | |||
| 3667 | free_irq(AU1200_LCD_INT, (void *)dev); | ||
| 3668 | |||
| 3669 | return 0; | ||
| 3670 | } | ||
| 3671 | |||
| 3672 | #ifdef CONFIG_PM | ||
| 3673 | static int au1200fb_drv_suspend(struct device *dev, u32 state, u32 level) | ||
| 3674 | { | ||
| 3675 | /* TODO */ | ||
| 3676 | return 0; | ||
| 3677 | } | ||
| 3678 | |||
| 3679 | static int au1200fb_drv_resume(struct device *dev, u32 level) | ||
| 3680 | { | ||
| 3681 | /* TODO */ | ||
| 3682 | return 0; | ||
| 3683 | } | ||
| 3684 | #endif /* CONFIG_PM */ | ||
| 3685 | |||
| 3686 | static struct device_driver au1200fb_driver = { | ||
| 3687 | .name = "au1200-lcd", | ||
| 3688 | .bus = &platform_bus_type, | ||
| 3689 | .probe = au1200fb_drv_probe, | ||
| 3690 | .remove = au1200fb_drv_remove, | ||
| 3691 | #ifdef CONFIG_PM | ||
| 3692 | .suspend = au1200fb_drv_suspend, | ||
| 3693 | .resume = au1200fb_drv_resume, | ||
| 3694 | #endif | ||
| 3695 | }; | ||
| 3696 | |||
| 3697 | /*-------------------------------------------------------------------------*/ | ||
| 3698 | |||
| 3699 | /* Kernel driver */ | ||
| 3700 | |||
| 3701 | static void au1200fb_setup(void) | ||
| 3702 | { | ||
| 3703 | char* options = NULL; | ||
| 3704 | char* this_opt; | ||
| 3705 | int num_panels = ARRAY_SIZE(known_lcd_panels); | ||
| 3706 | int panel_idx = -1; | ||
| 3707 | |||
| 3708 | fb_get_options(DRIVER_NAME, &options); | ||
| 3709 | |||
| 3710 | if (options) { | ||
| 3711 | while ((this_opt = strsep(&options,",")) != NULL) { | ||
| 3712 | /* Panel option - can be panel name, | ||
| 3713 | * "bs" for board-switch, or number/index */ | ||
| 3714 | if (!strncmp(this_opt, "panel:", 6)) { | ||
| 3715 | int i; | ||
| 3716 | long int li; | ||
| 3717 | char *endptr; | ||
| 3718 | this_opt += 6; | ||
| 3719 | /* First check for index, which allows | ||
| 3720 | * to short circuit this mess */ | ||
| 3721 | li = simple_strtol(this_opt, &endptr, 0); | ||
| 3722 | if (*endptr == '\0') { | ||
| 3723 | panel_idx = (int)li; | ||
| 3724 | } | ||
| 3725 | else if (strcmp(this_opt, "bs") == 0) { | ||
| 3726 | extern int board_au1200fb_panel(void); | ||
| 3727 | panel_idx = board_au1200fb_panel(); | ||
| 3728 | } | ||
| 3729 | |||
| 3730 | else | ||
| 3731 | for (i = 0; i < num_panels; i++) { | ||
| 3732 | if (!strcmp(this_opt, known_lcd_panels[i].name)) { | ||
| 3733 | panel_idx = i; | ||
| 3734 | break; | ||
| 3735 | } | ||
| 3736 | } | ||
| 3737 | |||
| 3738 | if ((panel_idx < 0) || (panel_idx >= num_panels)) { | ||
| 3739 | print_warn("Panel %s not supported!", this_opt); | ||
| 3740 | } | ||
| 3741 | else | ||
| 3742 | panel_index = panel_idx; | ||
| 3743 | } | ||
| 3744 | |||
| 3745 | else if (strncmp(this_opt, "nohwcursor", 10) == 0) { | ||
| 3746 | nohwcursor = 1; | ||
| 3747 | } | ||
| 3748 | |||
| 3749 | /* Unsupported option */ | ||
| 3750 | else { | ||
| 3751 | print_warn("Unsupported option \"%s\"", this_opt); | ||
| 3752 | } | ||
| 3753 | } | ||
| 3754 | } | ||
| 3755 | } | ||
| 3756 | |||
| 3757 | #ifdef CONFIG_PM | ||
| 3758 | static int au1200fb_pm_callback(au1xxx_power_dev_t *dev, | ||
| 3759 | au1xxx_request_t request, void *data) { | ||
| 3760 | int retval = -1; | ||
| 3761 | unsigned int d = 0; | ||
| 3762 | unsigned int brightness = 0; | ||
| 3763 | |||
| 3764 | if (request == AU1XXX_PM_SLEEP) { | ||
| 3765 | board_au1200fb_panel_shutdown(); | ||
| 3766 | } | ||
| 3767 | else if (request == AU1XXX_PM_WAKEUP) { | ||
| 3768 | if(dev->prev_state == SLEEP_STATE) | ||
| 3769 | { | ||
| 3770 | int plane; | ||
| 3771 | au1200_setpanel(panel); | ||
| 3772 | for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { | ||
| 3773 | struct au1200fb_device *fbdev; | ||
| 3774 | fbdev = &_au1200fb_devices[plane]; | ||
| 3775 | au1200fb_fb_set_par(&fbdev->fb_info); | ||
| 3776 | } | ||
| 3777 | } | ||
| 3778 | |||
| 3779 | d = *((unsigned int*)data); | ||
| 3780 | if(d <=10) brightness = 26; | ||
| 3781 | else if(d<=20) brightness = 51; | ||
| 3782 | else if(d<=30) brightness = 77; | ||
| 3783 | else if(d<=40) brightness = 102; | ||
| 3784 | else if(d<=50) brightness = 128; | ||
| 3785 | else if(d<=60) brightness = 153; | ||
| 3786 | else if(d<=70) brightness = 179; | ||
| 3787 | else if(d<=80) brightness = 204; | ||
| 3788 | else if(d<=90) brightness = 230; | ||
| 3789 | else brightness = 255; | ||
| 3790 | set_brightness(brightness); | ||
| 3791 | } else if (request == AU1XXX_PM_GETSTATUS) { | ||
| 3792 | return dev->cur_state; | ||
| 3793 | } else if (request == AU1XXX_PM_ACCESS) { | ||
| 3794 | if (dev->cur_state != SLEEP_STATE) | ||
| 3795 | return retval; | ||
| 3796 | else { | ||
| 3797 | au1200_setpanel(panel); | ||
| 3798 | } | ||
| 3799 | } else if (request == AU1XXX_PM_IDLE) { | ||
| 3800 | } else if (request == AU1XXX_PM_CLEANUP) { | ||
| 3801 | } | ||
| 3802 | |||
| 3803 | return retval; | ||
| 3804 | } | ||
| 3805 | #endif | ||
| 3806 | |||
| 3807 | static int __init au1200fb_init(void) | ||
| 3808 | { | ||
| 3809 | print_info("" DRIVER_DESC ""); | ||
| 3810 | |||
| 3811 | /* Setup driver with options */ | ||
| 3812 | au1200fb_setup(); | ||
| 3813 | |||
| 3814 | /* Point to the panel selected */ | ||
| 3815 | panel = &known_lcd_panels[panel_index]; | ||
| 3816 | win = &windows[window_index]; | ||
| 3817 | |||
| 3818 | printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name); | ||
| 3819 | printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name); | ||
| 3820 | |||
| 3821 | /* Kickstart the panel, the framebuffers/windows come soon enough */ | ||
| 3822 | au1200_setpanel(panel); | ||
| 3823 | |||
| 3824 | #ifdef CONFIG_PM | ||
| 3825 | LCD_pm_dev = new_au1xxx_power_device("LCD", &au1200fb_pm_callback, NULL); | ||
| 3826 | if ( LCD_pm_dev == NULL) | ||
| 3827 | printk(KERN_INFO "Unable to create a power management device entry for the au1200fb.\n"); | ||
| 3828 | else | ||
| 3829 | printk(KERN_INFO "Power management device entry for the au1200fb loaded.\n"); | ||
| 3830 | #endif | ||
| 3831 | |||
| 3832 | return driver_register(&au1200fb_driver); | ||
| 3833 | } | ||
| 3834 | |||
| 3835 | static void __exit au1200fb_cleanup(void) | ||
| 3836 | { | ||
| 3837 | driver_unregister(&au1200fb_driver); | ||
| 3838 | } | ||
| 3839 | |||
| 3840 | module_init(au1200fb_init); | ||
| 3841 | module_exit(au1200fb_cleanup); | ||
| 3842 | |||
| 3843 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
| 3844 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/au1200fb.h b/drivers/video/au1200fb.h new file mode 100644 index 000000000000..e2672714d8d4 --- /dev/null +++ b/drivers/video/au1200fb.h | |||
| @@ -0,0 +1,572 @@ | |||
| 1 | /* | ||
| 2 | * BRIEF MODULE DESCRIPTION | ||
| 3 | * Hardware definitions for the Au1200 LCD controller | ||
| 4 | * | ||
| 5 | * Copyright 2004 AMD | ||
| 6 | * Author: AMD | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the | ||
| 10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 11 | * option) any later version. | ||
| 12 | * | ||
| 13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 14 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
| 16 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 19 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 23 | * | ||
| 24 | * You should have received a copy of the GNU General Public License along | ||
| 25 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 26 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 27 | */ | ||
| 28 | |||
| 29 | #ifndef _AU1200LCD_H | ||
| 30 | #define _AU1200LCD_H | ||
| 31 | |||
| 32 | /********************************************************************/ | ||
| 33 | #define AU1200_LCD_ADDR 0xB5000000 | ||
| 34 | |||
| 35 | #define uint8 unsigned char | ||
| 36 | #define uint32 unsigned int | ||
| 37 | |||
| 38 | struct au1200_lcd { | ||
| 39 | volatile uint32 reserved0; | ||
| 40 | volatile uint32 screen; | ||
| 41 | volatile uint32 backcolor; | ||
| 42 | volatile uint32 horztiming; | ||
| 43 | volatile uint32 verttiming; | ||
| 44 | volatile uint32 clkcontrol; | ||
| 45 | volatile uint32 pwmdiv; | ||
| 46 | volatile uint32 pwmhi; | ||
| 47 | volatile uint32 reserved1; | ||
| 48 | volatile uint32 winenable; | ||
| 49 | volatile uint32 colorkey; | ||
| 50 | volatile uint32 colorkeymsk; | ||
| 51 | struct | ||
| 52 | { | ||
| 53 | volatile uint32 cursorctrl; | ||
| 54 | volatile uint32 cursorpos; | ||
| 55 | volatile uint32 cursorcolor0; | ||
| 56 | volatile uint32 cursorcolor1; | ||
| 57 | volatile uint32 cursorcolor2; | ||
| 58 | uint32 cursorcolor3; | ||
| 59 | } hwc; | ||
| 60 | volatile uint32 intstatus; | ||
| 61 | volatile uint32 intenable; | ||
| 62 | volatile uint32 outmask; | ||
| 63 | volatile uint32 fifoctrl; | ||
| 64 | uint32 reserved2[(0x0100-0x0058)/4]; | ||
| 65 | struct | ||
| 66 | { | ||
| 67 | volatile uint32 winctrl0; | ||
| 68 | volatile uint32 winctrl1; | ||
| 69 | volatile uint32 winctrl2; | ||
| 70 | volatile uint32 winbuf0; | ||
| 71 | volatile uint32 winbuf1; | ||
| 72 | volatile uint32 winbufctrl; | ||
| 73 | uint32 winreserved0; | ||
| 74 | uint32 winreserved1; | ||
| 75 | } window[4]; | ||
| 76 | |||
| 77 | uint32 reserved3[(0x0400-0x0180)/4]; | ||
| 78 | |||
| 79 | volatile uint32 palette[(0x0800-0x0400)/4]; | ||
| 80 | |||
| 81 | volatile uint8 cursorpattern[256]; | ||
| 82 | }; | ||
| 83 | |||
| 84 | /* lcd_screen */ | ||
| 85 | #define LCD_SCREEN_SEN (1<<31) | ||
| 86 | #define LCD_SCREEN_SX (0x07FF<<19) | ||
| 87 | #define LCD_SCREEN_SY (0x07FF<< 8) | ||
| 88 | #define LCD_SCREEN_SWP (1<<7) | ||
| 89 | #define LCD_SCREEN_SWD (1<<6) | ||
| 90 | #define LCD_SCREEN_PT (7<<0) | ||
| 91 | #define LCD_SCREEN_PT_TFT (0<<0) | ||
| 92 | #define LCD_SCREEN_SX_N(WIDTH) ((WIDTH-1)<<19) | ||
| 93 | #define LCD_SCREEN_SY_N(HEIGHT) ((HEIGHT-1)<<8) | ||
| 94 | #define LCD_SCREEN_PT_CSTN (1<<0) | ||
| 95 | #define LCD_SCREEN_PT_CDSTN (2<<0) | ||
| 96 | #define LCD_SCREEN_PT_M8STN (3<<0) | ||
| 97 | #define LCD_SCREEN_PT_M4STN (4<<0) | ||
| 98 | |||
| 99 | /* lcd_backcolor */ | ||
| 100 | #define LCD_BACKCOLOR_SBGR (0xFF<<16) | ||
| 101 | #define LCD_BACKCOLOR_SBGG (0xFF<<8) | ||
| 102 | #define LCD_BACKCOLOR_SBGB (0xFF<<0) | ||
| 103 | #define LCD_BACKCOLOR_SBGR_N(N) ((N)<<16) | ||
| 104 | #define LCD_BACKCOLOR_SBGG_N(N) ((N)<<8) | ||
| 105 | #define LCD_BACKCOLOR_SBGB_N(N) ((N)<<0) | ||
| 106 | |||
| 107 | /* lcd_winenable */ | ||
| 108 | #define LCD_WINENABLE_WEN3 (1<<3) | ||
| 109 | #define LCD_WINENABLE_WEN2 (1<<2) | ||
| 110 | #define LCD_WINENABLE_WEN1 (1<<1) | ||
| 111 | #define LCD_WINENABLE_WEN0 (1<<0) | ||
| 112 | |||
| 113 | /* lcd_colorkey */ | ||
| 114 | #define LCD_COLORKEY_CKR (0xFF<<16) | ||
| 115 | #define LCD_COLORKEY_CKG (0xFF<<8) | ||
| 116 | #define LCD_COLORKEY_CKB (0xFF<<0) | ||
| 117 | #define LCD_COLORKEY_CKR_N(N) ((N)<<16) | ||
| 118 | #define LCD_COLORKEY_CKG_N(N) ((N)<<8) | ||
| 119 | #define LCD_COLORKEY_CKB_N(N) ((N)<<0) | ||
| 120 | |||
| 121 | /* lcd_colorkeymsk */ | ||
| 122 | #define LCD_COLORKEYMSK_CKMR (0xFF<<16) | ||
| 123 | #define LCD_COLORKEYMSK_CKMG (0xFF<<8) | ||
| 124 | #define LCD_COLORKEYMSK_CKMB (0xFF<<0) | ||
| 125 | #define LCD_COLORKEYMSK_CKMR_N(N) ((N)<<16) | ||
| 126 | #define LCD_COLORKEYMSK_CKMG_N(N) ((N)<<8) | ||
| 127 | #define LCD_COLORKEYMSK_CKMB_N(N) ((N)<<0) | ||
| 128 | |||
| 129 | /* lcd windows control 0 */ | ||
| 130 | #define LCD_WINCTRL0_OX (0x07FF<<21) | ||
| 131 | #define LCD_WINCTRL0_OY (0x07FF<<10) | ||
| 132 | #define LCD_WINCTRL0_A (0x00FF<<2) | ||
| 133 | #define LCD_WINCTRL0_AEN (1<<1) | ||
| 134 | #define LCD_WINCTRL0_OX_N(N) ((N)<<21) | ||
| 135 | #define LCD_WINCTRL0_OY_N(N) ((N)<<10) | ||
| 136 | #define LCD_WINCTRL0_A_N(N) ((N)<<2) | ||
| 137 | |||
| 138 | /* lcd windows control 1 */ | ||
| 139 | #define LCD_WINCTRL1_PRI (3<<30) | ||
| 140 | #define LCD_WINCTRL1_PIPE (1<<29) | ||
| 141 | #define LCD_WINCTRL1_FRM (0xF<<25) | ||
| 142 | #define LCD_WINCTRL1_CCO (1<<24) | ||
| 143 | #define LCD_WINCTRL1_PO (3<<22) | ||
| 144 | #define LCD_WINCTRL1_SZX (0x07FF<<11) | ||
| 145 | #define LCD_WINCTRL1_SZY (0x07FF<<0) | ||
| 146 | #define LCD_WINCTRL1_FRM_1BPP (0<<25) | ||
| 147 | #define LCD_WINCTRL1_FRM_2BPP (1<<25) | ||
| 148 | #define LCD_WINCTRL1_FRM_4BPP (2<<25) | ||
| 149 | #define LCD_WINCTRL1_FRM_8BPP (3<<25) | ||
| 150 | #define LCD_WINCTRL1_FRM_12BPP (4<<25) | ||
| 151 | #define LCD_WINCTRL1_FRM_16BPP655 (5<<25) | ||
| 152 | #define LCD_WINCTRL1_FRM_16BPP565 (6<<25) | ||
| 153 | #define LCD_WINCTRL1_FRM_16BPP556 (7<<25) | ||
| 154 | #define LCD_WINCTRL1_FRM_16BPPI1555 (8<<25) | ||
| 155 | #define LCD_WINCTRL1_FRM_16BPPI5551 (9<<25) | ||
| 156 | #define LCD_WINCTRL1_FRM_16BPPA1555 (10<<25) | ||
| 157 | #define LCD_WINCTRL1_FRM_16BPPA5551 (11<<25) | ||
| 158 | #define LCD_WINCTRL1_FRM_24BPP (12<<25) | ||
| 159 | #define LCD_WINCTRL1_FRM_32BPP (13<<25) | ||
| 160 | #define LCD_WINCTRL1_PRI_N(N) ((N)<<30) | ||
| 161 | #define LCD_WINCTRL1_PO_00 (0<<22) | ||
| 162 | #define LCD_WINCTRL1_PO_01 (1<<22) | ||
| 163 | #define LCD_WINCTRL1_PO_10 (2<<22) | ||
| 164 | #define LCD_WINCTRL1_PO_11 (3<<22) | ||
| 165 | #define LCD_WINCTRL1_SZX_N(N) ((N-1)<<11) | ||
| 166 | #define LCD_WINCTRL1_SZY_N(N) ((N-1)<<0) | ||
| 167 | |||
| 168 | /* lcd windows control 2 */ | ||
| 169 | #define LCD_WINCTRL2_CKMODE (3<<24) | ||
| 170 | #define LCD_WINCTRL2_DBM (1<<23) | ||
| 171 | #define LCD_WINCTRL2_RAM (3<<21) | ||
| 172 | #define LCD_WINCTRL2_BX (0x1FFF<<8) | ||
| 173 | #define LCD_WINCTRL2_SCX (0xF<<4) | ||
| 174 | #define LCD_WINCTRL2_SCY (0xF<<0) | ||
| 175 | #define LCD_WINCTRL2_CKMODE_00 (0<<24) | ||
| 176 | #define LCD_WINCTRL2_CKMODE_01 (1<<24) | ||
| 177 | #define LCD_WINCTRL2_CKMODE_10 (2<<24) | ||
| 178 | #define LCD_WINCTRL2_CKMODE_11 (3<<24) | ||
| 179 | #define LCD_WINCTRL2_RAM_NONE (0<<21) | ||
| 180 | #define LCD_WINCTRL2_RAM_PALETTE (1<<21) | ||
| 181 | #define LCD_WINCTRL2_RAM_GAMMA (2<<21) | ||
| 182 | #define LCD_WINCTRL2_RAM_BUFFER (3<<21) | ||
| 183 | #define LCD_WINCTRL2_BX_N(N) ((N)<<8) | ||
| 184 | #define LCD_WINCTRL2_SCX_1 (0<<4) | ||
| 185 | #define LCD_WINCTRL2_SCX_2 (1<<4) | ||
| 186 | #define LCD_WINCTRL2_SCX_4 (2<<4) | ||
| 187 | #define LCD_WINCTRL2_SCY_1 (0<<0) | ||
| 188 | #define LCD_WINCTRL2_SCY_2 (1<<0) | ||
| 189 | #define LCD_WINCTRL2_SCY_4 (2<<0) | ||
| 190 | |||
| 191 | /* lcd windows buffer control */ | ||
| 192 | #define LCD_WINBUFCTRL_DB (1<<1) | ||
| 193 | #define LCD_WINBUFCTRL_DBN (1<<0) | ||
| 194 | |||
| 195 | /* lcd_intstatus, lcd_intenable */ | ||
| 196 | #define LCD_INT_IFO (0xF<<14) | ||
| 197 | #define LCD_INT_IFU (0xF<<10) | ||
| 198 | #define LCD_INT_OFO (1<<9) | ||
| 199 | #define LCD_INT_OFU (1<<8) | ||
| 200 | #define LCD_INT_WAIT (1<<3) | ||
| 201 | #define LCD_INT_SD (1<<2) | ||
| 202 | #define LCD_INT_SA (1<<1) | ||
| 203 | #define LCD_INT_SS (1<<0) | ||
| 204 | |||
| 205 | /* lcd_horztiming */ | ||
| 206 | #define LCD_HORZTIMING_HND2 (0x1FF<<18) | ||
| 207 | #define LCD_HORZTIMING_HND1 (0x1FF<<9) | ||
| 208 | #define LCD_HORZTIMING_HPW (0x1FF<<0) | ||
| 209 | #define LCD_HORZTIMING_HND2_N(N)(((N)-1)<<18) | ||
| 210 | #define LCD_HORZTIMING_HND1_N(N)(((N)-1)<<9) | ||
| 211 | #define LCD_HORZTIMING_HPW_N(N) (((N)-1)<<0) | ||
| 212 | |||
| 213 | /* lcd_verttiming */ | ||
| 214 | #define LCD_VERTTIMING_VND2 (0x1FF<<18) | ||
| 215 | #define LCD_VERTTIMING_VND1 (0x1FF<<9) | ||
| 216 | #define LCD_VERTTIMING_VPW (0x1FF<<0) | ||
| 217 | #define LCD_VERTTIMING_VND2_N(N)(((N)-1)<<18) | ||
| 218 | #define LCD_VERTTIMING_VND1_N(N)(((N)-1)<<9) | ||
| 219 | #define LCD_VERTTIMING_VPW_N(N) (((N)-1)<<0) | ||
| 220 | |||
| 221 | /* lcd_clkcontrol */ | ||
| 222 | #define LCD_CLKCONTROL_EXT (1<<22) | ||
| 223 | #define LCD_CLKCONTROL_DELAY (3<<20) | ||
| 224 | #define LCD_CLKCONTROL_CDD (1<<19) | ||
| 225 | #define LCD_CLKCONTROL_IB (1<<18) | ||
| 226 | #define LCD_CLKCONTROL_IC (1<<17) | ||
| 227 | #define LCD_CLKCONTROL_IH (1<<16) | ||
| 228 | #define LCD_CLKCONTROL_IV (1<<15) | ||
| 229 | #define LCD_CLKCONTROL_BF (0x1F<<10) | ||
| 230 | #define LCD_CLKCONTROL_PCD (0x3FF<<0) | ||
| 231 | #define LCD_CLKCONTROL_BF_N(N) (((N)-1)<<10) | ||
| 232 | #define LCD_CLKCONTROL_PCD_N(N) ((N)<<0) | ||
| 233 | |||
| 234 | /* lcd_pwmdiv */ | ||
| 235 | #define LCD_PWMDIV_EN (1<<31) | ||
| 236 | #define LCD_PWMDIV_PWMDIV (0x1FFFF<<0) | ||
| 237 | #define LCD_PWMDIV_PWMDIV_N(N) ((N)<<0) | ||
| 238 | |||
| 239 | /* lcd_pwmhi */ | ||
| 240 | #define LCD_PWMHI_PWMHI1 (0xFFFF<<16) | ||
| 241 | #define LCD_PWMHI_PWMHI0 (0xFFFF<<0) | ||
| 242 | #define LCD_PWMHI_PWMHI1_N(N) ((N)<<16) | ||
| 243 | #define LCD_PWMHI_PWMHI0_N(N) ((N)<<0) | ||
| 244 | |||
| 245 | /* lcd_hwccon */ | ||
| 246 | #define LCD_HWCCON_EN (1<<0) | ||
| 247 | |||
| 248 | /* lcd_cursorpos */ | ||
| 249 | #define LCD_CURSORPOS_HWCXOFF (0x1F<<27) | ||
| 250 | #define LCD_CURSORPOS_HWCXPOS (0x07FF<<16) | ||
| 251 | #define LCD_CURSORPOS_HWCYOFF (0x1F<<11) | ||
| 252 | #define LCD_CURSORPOS_HWCYPOS (0x07FF<<0) | ||
| 253 | #define LCD_CURSORPOS_HWCXOFF_N(N) ((N)<<27) | ||
| 254 | #define LCD_CURSORPOS_HWCXPOS_N(N) ((N)<<16) | ||
| 255 | #define LCD_CURSORPOS_HWCYOFF_N(N) ((N)<<11) | ||
| 256 | #define LCD_CURSORPOS_HWCYPOS_N(N) ((N)<<0) | ||
| 257 | |||
| 258 | /* lcd_cursorcolor */ | ||
| 259 | #define LCD_CURSORCOLOR_HWCA (0xFF<<24) | ||
| 260 | #define LCD_CURSORCOLOR_HWCR (0xFF<<16) | ||
| 261 | #define LCD_CURSORCOLOR_HWCG (0xFF<<8) | ||
| 262 | #define LCD_CURSORCOLOR_HWCB (0xFF<<0) | ||
| 263 | #define LCD_CURSORCOLOR_HWCA_N(N) ((N)<<24) | ||
| 264 | #define LCD_CURSORCOLOR_HWCR_N(N) ((N)<<16) | ||
| 265 | #define LCD_CURSORCOLOR_HWCG_N(N) ((N)<<8) | ||
| 266 | #define LCD_CURSORCOLOR_HWCB_N(N) ((N)<<0) | ||
| 267 | |||
| 268 | /* lcd_fifoctrl */ | ||
| 269 | #define LCD_FIFOCTRL_F3IF (1<<29) | ||
| 270 | #define LCD_FIFOCTRL_F3REQ (0x1F<<24) | ||
| 271 | #define LCD_FIFOCTRL_F2IF (1<<29) | ||
| 272 | #define LCD_FIFOCTRL_F2REQ (0x1F<<16) | ||
| 273 | #define LCD_FIFOCTRL_F1IF (1<<29) | ||
| 274 | #define LCD_FIFOCTRL_F1REQ (0x1F<<8) | ||
| 275 | #define LCD_FIFOCTRL_F0IF (1<<29) | ||
| 276 | #define LCD_FIFOCTRL_F0REQ (0x1F<<0) | ||
| 277 | #define LCD_FIFOCTRL_F3REQ_N(N) ((N-1)<<24) | ||
| 278 | #define LCD_FIFOCTRL_F2REQ_N(N) ((N-1)<<16) | ||
| 279 | #define LCD_FIFOCTRL_F1REQ_N(N) ((N-1)<<8) | ||
| 280 | #define LCD_FIFOCTRL_F0REQ_N(N) ((N-1)<<0) | ||
| 281 | |||
| 282 | /* lcd_outmask */ | ||
| 283 | #define LCD_OUTMASK_MASK (0x00FFFFFF) | ||
| 284 | |||
| 285 | /********************************************************************/ | ||
| 286 | #endif /* _AU1200LCD_H */ | ||
| 287 | /* | ||
| 288 | * BRIEF MODULE DESCRIPTION | ||
| 289 | * Hardware definitions for the Au1200 LCD controller | ||
| 290 | * | ||
| 291 | * Copyright 2004 AMD | ||
| 292 | * Author: AMD | ||
| 293 | * | ||
| 294 | * This program is free software; you can redistribute it and/or modify it | ||
| 295 | * under the terms of the GNU General Public License as published by the | ||
| 296 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 297 | * option) any later version. | ||
| 298 | * | ||
| 299 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 300 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 301 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
| 302 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 303 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 304 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 305 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 306 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 307 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 308 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 309 | * | ||
| 310 | * You should have received a copy of the GNU General Public License along | ||
| 311 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 312 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 313 | */ | ||
| 314 | |||
| 315 | #ifndef _AU1200LCD_H | ||
| 316 | #define _AU1200LCD_H | ||
| 317 | |||
| 318 | /********************************************************************/ | ||
| 319 | #define AU1200_LCD_ADDR 0xB5000000 | ||
| 320 | |||
| 321 | #define uint8 unsigned char | ||
| 322 | #define uint32 unsigned int | ||
| 323 | |||
| 324 | struct au1200_lcd { | ||
| 325 | volatile uint32 reserved0; | ||
| 326 | volatile uint32 screen; | ||
| 327 | volatile uint32 backcolor; | ||
| 328 | volatile uint32 horztiming; | ||
| 329 | volatile uint32 verttiming; | ||
| 330 | volatile uint32 clkcontrol; | ||
| 331 | volatile uint32 pwmdiv; | ||
| 332 | volatile uint32 pwmhi; | ||
| 333 | volatile uint32 reserved1; | ||
| 334 | volatile uint32 winenable; | ||
| 335 | volatile uint32 colorkey; | ||
| 336 | volatile uint32 colorkeymsk; | ||
| 337 | struct | ||
| 338 | { | ||
| 339 | volatile uint32 cursorctrl; | ||
| 340 | volatile uint32 cursorpos; | ||
| 341 | volatile uint32 cursorcolor0; | ||
| 342 | volatile uint32 cursorcolor1; | ||
| 343 | volatile uint32 cursorcolor2; | ||
| 344 | uint32 cursorcolor3; | ||
| 345 | } hwc; | ||
| 346 | volatile uint32 intstatus; | ||
| 347 | volatile uint32 intenable; | ||
| 348 | volatile uint32 outmask; | ||
| 349 | volatile uint32 fifoctrl; | ||
| 350 | uint32 reserved2[(0x0100-0x0058)/4]; | ||
| 351 | struct | ||
| 352 | { | ||
| 353 | volatile uint32 winctrl0; | ||
| 354 | volatile uint32 winctrl1; | ||
| 355 | volatile uint32 winctrl2; | ||
| 356 | volatile uint32 winbuf0; | ||
| 357 | volatile uint32 winbuf1; | ||
| 358 | volatile uint32 winbufctrl; | ||
| 359 | uint32 winreserved0; | ||
| 360 | uint32 winreserved1; | ||
| 361 | } window[4]; | ||
| 362 | |||
| 363 | uint32 reserved3[(0x0400-0x0180)/4]; | ||
| 364 | |||
| 365 | volatile uint32 palette[(0x0800-0x0400)/4]; | ||
| 366 | |||
| 367 | volatile uint8 cursorpattern[256]; | ||
| 368 | }; | ||
| 369 | |||
| 370 | /* lcd_screen */ | ||
| 371 | #define LCD_SCREEN_SEN (1<<31) | ||
| 372 | #define LCD_SCREEN_SX (0x07FF<<19) | ||
| 373 | #define LCD_SCREEN_SY (0x07FF<< 8) | ||
| 374 | #define LCD_SCREEN_SWP (1<<7) | ||
| 375 | #define LCD_SCREEN_SWD (1<<6) | ||
| 376 | #define LCD_SCREEN_PT (7<<0) | ||
| 377 | #define LCD_SCREEN_PT_TFT (0<<0) | ||
| 378 | #define LCD_SCREEN_SX_N(WIDTH) ((WIDTH-1)<<19) | ||
| 379 | #define LCD_SCREEN_SY_N(HEIGHT) ((HEIGHT-1)<<8) | ||
| 380 | #define LCD_SCREEN_PT_CSTN (1<<0) | ||
| 381 | #define LCD_SCREEN_PT_CDSTN (2<<0) | ||
| 382 | #define LCD_SCREEN_PT_M8STN (3<<0) | ||
| 383 | #define LCD_SCREEN_PT_M4STN (4<<0) | ||
| 384 | |||
| 385 | /* lcd_backcolor */ | ||
| 386 | #define LCD_BACKCOLOR_SBGR (0xFF<<16) | ||
| 387 | #define LCD_BACKCOLOR_SBGG (0xFF<<8) | ||
| 388 | #define LCD_BACKCOLOR_SBGB (0xFF<<0) | ||
| 389 | #define LCD_BACKCOLOR_SBGR_N(N) ((N)<<16) | ||
| 390 | #define LCD_BACKCOLOR_SBGG_N(N) ((N)<<8) | ||
| 391 | #define LCD_BACKCOLOR_SBGB_N(N) ((N)<<0) | ||
| 392 | |||
| 393 | /* lcd_winenable */ | ||
| 394 | #define LCD_WINENABLE_WEN3 (1<<3) | ||
| 395 | #define LCD_WINENABLE_WEN2 (1<<2) | ||
| 396 | #define LCD_WINENABLE_WEN1 (1<<1) | ||
| 397 | #define LCD_WINENABLE_WEN0 (1<<0) | ||
| 398 | |||
| 399 | /* lcd_colorkey */ | ||
| 400 | #define LCD_COLORKEY_CKR (0xFF<<16) | ||
| 401 | #define LCD_COLORKEY_CKG (0xFF<<8) | ||
| 402 | #define LCD_COLORKEY_CKB (0xFF<<0) | ||
| 403 | #define LCD_COLORKEY_CKR_N(N) ((N)<<16) | ||
| 404 | #define LCD_COLORKEY_CKG_N(N) ((N)<<8) | ||
| 405 | #define LCD_COLORKEY_CKB_N(N) ((N)<<0) | ||
| 406 | |||
| 407 | /* lcd_colorkeymsk */ | ||
| 408 | #define LCD_COLORKEYMSK_CKMR (0xFF<<16) | ||
| 409 | #define LCD_COLORKEYMSK_CKMG (0xFF<<8) | ||
| 410 | #define LCD_COLORKEYMSK_CKMB (0xFF<<0) | ||
| 411 | #define LCD_COLORKEYMSK_CKMR_N(N) ((N)<<16) | ||
| 412 | #define LCD_COLORKEYMSK_CKMG_N(N) ((N)<<8) | ||
| 413 | #define LCD_COLORKEYMSK_CKMB_N(N) ((N)<<0) | ||
| 414 | |||
| 415 | /* lcd windows control 0 */ | ||
| 416 | #define LCD_WINCTRL0_OX (0x07FF<<21) | ||
| 417 | #define LCD_WINCTRL0_OY (0x07FF<<10) | ||
| 418 | #define LCD_WINCTRL0_A (0x00FF<<2) | ||
| 419 | #define LCD_WINCTRL0_AEN (1<<1) | ||
| 420 | #define LCD_WINCTRL0_OX_N(N) ((N)<<21) | ||
| 421 | #define LCD_WINCTRL0_OY_N(N) ((N)<<10) | ||
| 422 | #define LCD_WINCTRL0_A_N(N) ((N)<<2) | ||
| 423 | |||
| 424 | /* lcd windows control 1 */ | ||
| 425 | #define LCD_WINCTRL1_PRI (3<<30) | ||
| 426 | #define LCD_WINCTRL1_PIPE (1<<29) | ||
| 427 | #define LCD_WINCTRL1_FRM (0xF<<25) | ||
| 428 | #define LCD_WINCTRL1_CCO (1<<24) | ||
| 429 | #define LCD_WINCTRL1_PO (3<<22) | ||
| 430 | #define LCD_WINCTRL1_SZX (0x07FF<<11) | ||
| 431 | #define LCD_WINCTRL1_SZY (0x07FF<<0) | ||
| 432 | #define LCD_WINCTRL1_FRM_1BPP (0<<25) | ||
| 433 | #define LCD_WINCTRL1_FRM_2BPP (1<<25) | ||
| 434 | #define LCD_WINCTRL1_FRM_4BPP (2<<25) | ||
| 435 | #define LCD_WINCTRL1_FRM_8BPP (3<<25) | ||
| 436 | #define LCD_WINCTRL1_FRM_12BPP (4<<25) | ||
| 437 | #define LCD_WINCTRL1_FRM_16BPP655 (5<<25) | ||
| 438 | #define LCD_WINCTRL1_FRM_16BPP565 (6<<25) | ||
| 439 | #define LCD_WINCTRL1_FRM_16BPP556 (7<<25) | ||
| 440 | #define LCD_WINCTRL1_FRM_16BPPI1555 (8<<25) | ||
| 441 | #define LCD_WINCTRL1_FRM_16BPPI5551 (9<<25) | ||
| 442 | #define LCD_WINCTRL1_FRM_16BPPA1555 (10<<25) | ||
| 443 | #define LCD_WINCTRL1_FRM_16BPPA5551 (11<<25) | ||
| 444 | #define LCD_WINCTRL1_FRM_24BPP (12<<25) | ||
| 445 | #define LCD_WINCTRL1_FRM_32BPP (13<<25) | ||
| 446 | #define LCD_WINCTRL1_PRI_N(N) ((N)<<30) | ||
| 447 | #define LCD_WINCTRL1_PO_00 (0<<22) | ||
| 448 | #define LCD_WINCTRL1_PO_01 (1<<22) | ||
| 449 | #define LCD_WINCTRL1_PO_10 (2<<22) | ||
| 450 | #define LCD_WINCTRL1_PO_11 (3<<22) | ||
| 451 | #define LCD_WINCTRL1_SZX_N(N) ((N-1)<<11) | ||
| 452 | #define LCD_WINCTRL1_SZY_N(N) ((N-1)<<0) | ||
| 453 | |||
| 454 | /* lcd windows control 2 */ | ||
| 455 | #define LCD_WINCTRL2_CKMODE (3<<24) | ||
| 456 | #define LCD_WINCTRL2_DBM (1<<23) | ||
| 457 | #define LCD_WINCTRL2_RAM (3<<21) | ||
| 458 | #define LCD_WINCTRL2_BX (0x1FFF<<8) | ||
| 459 | #define LCD_WINCTRL2_SCX (0xF<<4) | ||
| 460 | #define LCD_WINCTRL2_SCY (0xF<<0) | ||
| 461 | #define LCD_WINCTRL2_CKMODE_00 (0<<24) | ||
| 462 | #define LCD_WINCTRL2_CKMODE_01 (1<<24) | ||
| 463 | #define LCD_WINCTRL2_CKMODE_10 (2<<24) | ||
| 464 | #define LCD_WINCTRL2_CKMODE_11 (3<<24) | ||
| 465 | #define LCD_WINCTRL2_RAM_NONE (0<<21) | ||
| 466 | #define LCD_WINCTRL2_RAM_PALETTE (1<<21) | ||
| 467 | #define LCD_WINCTRL2_RAM_GAMMA (2<<21) | ||
| 468 | #define LCD_WINCTRL2_RAM_BUFFER (3<<21) | ||
| 469 | #define LCD_WINCTRL2_BX_N(N) ((N)<<8) | ||
| 470 | #define LCD_WINCTRL2_SCX_1 (0<<4) | ||
| 471 | #define LCD_WINCTRL2_SCX_2 (1<<4) | ||
| 472 | #define LCD_WINCTRL2_SCX_4 (2<<4) | ||
| 473 | #define LCD_WINCTRL2_SCY_1 (0<<0) | ||
| 474 | #define LCD_WINCTRL2_SCY_2 (1<<0) | ||
| 475 | #define LCD_WINCTRL2_SCY_4 (2<<0) | ||
| 476 | |||
| 477 | /* lcd windows buffer control */ | ||
| 478 | #define LCD_WINBUFCTRL_DB (1<<1) | ||
| 479 | #define LCD_WINBUFCTRL_DBN (1<<0) | ||
| 480 | |||
| 481 | /* lcd_intstatus, lcd_intenable */ | ||
| 482 | #define LCD_INT_IFO (0xF<<14) | ||
| 483 | #define LCD_INT_IFU (0xF<<10) | ||
| 484 | #define LCD_INT_OFO (1<<9) | ||
| 485 | #define LCD_INT_OFU (1<<8) | ||
| 486 | #define LCD_INT_WAIT (1<<3) | ||
| 487 | #define LCD_INT_SD (1<<2) | ||
| 488 | #define LCD_INT_SA (1<<1) | ||
| 489 | #define LCD_INT_SS (1<<0) | ||
| 490 | |||
| 491 | /* lcd_horztiming */ | ||
| 492 | #define LCD_HORZTIMING_HND2 (0x1FF<<18) | ||
| 493 | #define LCD_HORZTIMING_HND1 (0x1FF<<9) | ||
| 494 | #define LCD_HORZTIMING_HPW (0x1FF<<0) | ||
| 495 | #define LCD_HORZTIMING_HND2_N(N)(((N)-1)<<18) | ||
| 496 | #define LCD_HORZTIMING_HND1_N(N)(((N)-1)<<9) | ||
| 497 | #define LCD_HORZTIMING_HPW_N(N) (((N)-1)<<0) | ||
| 498 | |||
| 499 | /* lcd_verttiming */ | ||
| 500 | #define LCD_VERTTIMING_VND2 (0x1FF<<18) | ||
| 501 | #define LCD_VERTTIMING_VND1 (0x1FF<<9) | ||
| 502 | #define LCD_VERTTIMING_VPW (0x1FF<<0) | ||
| 503 | #define LCD_VERTTIMING_VND2_N(N)(((N)-1)<<18) | ||
| 504 | #define LCD_VERTTIMING_VND1_N(N)(((N)-1)<<9) | ||
| 505 | #define LCD_VERTTIMING_VPW_N(N) (((N)-1)<<0) | ||
| 506 | |||
| 507 | /* lcd_clkcontrol */ | ||
| 508 | #define LCD_CLKCONTROL_EXT (1<<22) | ||
| 509 | #define LCD_CLKCONTROL_DELAY (3<<20) | ||
| 510 | #define LCD_CLKCONTROL_CDD (1<<19) | ||
| 511 | #define LCD_CLKCONTROL_IB (1<<18) | ||
| 512 | #define LCD_CLKCONTROL_IC (1<<17) | ||
| 513 | #define LCD_CLKCONTROL_IH (1<<16) | ||
| 514 | #define LCD_CLKCONTROL_IV (1<<15) | ||
| 515 | #define LCD_CLKCONTROL_BF (0x1F<<10) | ||
| 516 | #define LCD_CLKCONTROL_PCD (0x3FF<<0) | ||
| 517 | #define LCD_CLKCONTROL_BF_N(N) (((N)-1)<<10) | ||
| 518 | #define LCD_CLKCONTROL_PCD_N(N) ((N)<<0) | ||
| 519 | |||
| 520 | /* lcd_pwmdiv */ | ||
| 521 | #define LCD_PWMDIV_EN (1<<31) | ||
| 522 | #define LCD_PWMDIV_PWMDIV (0x1FFFF<<0) | ||
| 523 | #define LCD_PWMDIV_PWMDIV_N(N) ((N)<<0) | ||
| 524 | |||
| 525 | /* lcd_pwmhi */ | ||
| 526 | #define LCD_PWMHI_PWMHI1 (0xFFFF<<16) | ||
| 527 | #define LCD_PWMHI_PWMHI0 (0xFFFF<<0) | ||
| 528 | #define LCD_PWMHI_PWMHI1_N(N) ((N)<<16) | ||
| 529 | #define LCD_PWMHI_PWMHI0_N(N) ((N)<<0) | ||
| 530 | |||
| 531 | /* lcd_hwccon */ | ||
| 532 | #define LCD_HWCCON_EN (1<<0) | ||
| 533 | |||
| 534 | /* lcd_cursorpos */ | ||
| 535 | #define LCD_CURSORPOS_HWCXOFF (0x1F<<27) | ||
| 536 | #define LCD_CURSORPOS_HWCXPOS (0x07FF<<16) | ||
| 537 | #define LCD_CURSORPOS_HWCYOFF (0x1F<<11) | ||
| 538 | #define LCD_CURSORPOS_HWCYPOS (0x07FF<<0) | ||
| 539 | #define LCD_CURSORPOS_HWCXOFF_N(N) ((N)<<27) | ||
| 540 | #define LCD_CURSORPOS_HWCXPOS_N(N) ((N)<<16) | ||
| 541 | #define LCD_CURSORPOS_HWCYOFF_N(N) ((N)<<11) | ||
| 542 | #define LCD_CURSORPOS_HWCYPOS_N(N) ((N)<<0) | ||
| 543 | |||
| 544 | /* lcd_cursorcolor */ | ||
| 545 | #define LCD_CURSORCOLOR_HWCA (0xFF<<24) | ||
| 546 | #define LCD_CURSORCOLOR_HWCR (0xFF<<16) | ||
| 547 | #define LCD_CURSORCOLOR_HWCG (0xFF<<8) | ||
| 548 | #define LCD_CURSORCOLOR_HWCB (0xFF<<0) | ||
| 549 | #define LCD_CURSORCOLOR_HWCA_N(N) ((N)<<24) | ||
| 550 | #define LCD_CURSORCOLOR_HWCR_N(N) ((N)<<16) | ||
| 551 | #define LCD_CURSORCOLOR_HWCG_N(N) ((N)<<8) | ||
| 552 | #define LCD_CURSORCOLOR_HWCB_N(N) ((N)<<0) | ||
| 553 | |||
| 554 | /* lcd_fifoctrl */ | ||
| 555 | #define LCD_FIFOCTRL_F3IF (1<<29) | ||
| 556 | #define LCD_FIFOCTRL_F3REQ (0x1F<<24) | ||
| 557 | #define LCD_FIFOCTRL_F2IF (1<<29) | ||
| 558 | #define LCD_FIFOCTRL_F2REQ (0x1F<<16) | ||
| 559 | #define LCD_FIFOCTRL_F1IF (1<<29) | ||
| 560 | #define LCD_FIFOCTRL_F1REQ (0x1F<<8) | ||
| 561 | #define LCD_FIFOCTRL_F0IF (1<<29) | ||
| 562 | #define LCD_FIFOCTRL_F0REQ (0x1F<<0) | ||
| 563 | #define LCD_FIFOCTRL_F3REQ_N(N) ((N-1)<<24) | ||
| 564 | #define LCD_FIFOCTRL_F2REQ_N(N) ((N-1)<<16) | ||
| 565 | #define LCD_FIFOCTRL_F1REQ_N(N) ((N-1)<<8) | ||
| 566 | #define LCD_FIFOCTRL_F0REQ_N(N) ((N-1)<<0) | ||
| 567 | |||
| 568 | /* lcd_outmask */ | ||
| 569 | #define LCD_OUTMASK_MASK (0x00FFFFFF) | ||
| 570 | |||
| 571 | /********************************************************************/ | ||
| 572 | #endif /* _AU1200LCD_H */ | ||
diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index bc061d4ec786..72ff6bf75e5e 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c | |||
| @@ -178,8 +178,6 @@ struct chips_init_reg { | |||
| 178 | unsigned char data; | 178 | unsigned char data; |
| 179 | }; | 179 | }; |
| 180 | 180 | ||
| 181 | #define N_ELTS(x) (sizeof(x) / sizeof(x[0])) | ||
| 182 | |||
| 183 | static struct chips_init_reg chips_init_sr[] = { | 181 | static struct chips_init_reg chips_init_sr[] = { |
| 184 | { 0x00, 0x03 }, | 182 | { 0x00, 0x03 }, |
| 185 | { 0x01, 0x01 }, | 183 | { 0x01, 0x01 }, |
| @@ -287,18 +285,18 @@ static void __init chips_hw_init(void) | |||
| 287 | { | 285 | { |
| 288 | int i; | 286 | int i; |
| 289 | 287 | ||
| 290 | for (i = 0; i < N_ELTS(chips_init_xr); ++i) | 288 | for (i = 0; i < ARRAY_SIZE(chips_init_xr); ++i) |
| 291 | write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); | 289 | write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); |
| 292 | outb(0x29, 0x3c2); /* set misc output reg */ | 290 | outb(0x29, 0x3c2); /* set misc output reg */ |
| 293 | for (i = 0; i < N_ELTS(chips_init_sr); ++i) | 291 | for (i = 0; i < ARRAY_SIZE(chips_init_sr); ++i) |
| 294 | write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); | 292 | write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); |
| 295 | for (i = 0; i < N_ELTS(chips_init_gr); ++i) | 293 | for (i = 0; i < ARRAY_SIZE(chips_init_gr); ++i) |
| 296 | write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); | 294 | write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); |
| 297 | for (i = 0; i < N_ELTS(chips_init_ar); ++i) | 295 | for (i = 0; i < ARRAY_SIZE(chips_init_ar); ++i) |
| 298 | write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); | 296 | write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); |
| 299 | for (i = 0; i < N_ELTS(chips_init_cr); ++i) | 297 | for (i = 0; i < ARRAY_SIZE(chips_init_cr); ++i) |
| 300 | write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); | 298 | write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); |
| 301 | for (i = 0; i < N_ELTS(chips_init_fr); ++i) | 299 | for (i = 0; i < ARRAY_SIZE(chips_init_fr); ++i) |
| 302 | write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); | 300 | write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); |
| 303 | } | 301 | } |
| 304 | 302 | ||
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 6ee449858a5c..4444bef68fba 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig | |||
| @@ -26,6 +26,30 @@ config VGA_CONSOLE | |||
| 26 | # fi | 26 | # fi |
| 27 | # fi | 27 | # fi |
| 28 | 28 | ||
| 29 | config VGACON_SOFT_SCROLLBACK | ||
| 30 | bool "Enable Scrollback Buffer in System RAM" | ||
| 31 | depends on VGA_CONSOLE | ||
| 32 | default n | ||
| 33 | help | ||
| 34 | The scrollback buffer of the standard VGA console is located in | ||
| 35 | the VGA RAM. The size of this RAM is fixed and is quite small. | ||
| 36 | If you require a larger scrollback buffer, this can be placed in | ||
| 37 | System RAM which is dynamically allocated during intialization. | ||
| 38 | Placing the scrollback buffer in System RAM will slightly slow | ||
| 39 | down the console. | ||
| 40 | |||
| 41 | If you want this feature, say 'Y' here and enter the amount of | ||
| 42 | RAM to allocate for this buffer. If unsure, say 'N'. | ||
| 43 | |||
| 44 | config VGACON_SOFT_SCROLLBACK_SIZE | ||
| 45 | int "Scrollback Buffer Size (in KB)" | ||
| 46 | depends on VGACON_SOFT_SCROLLBACK | ||
| 47 | default "64" | ||
| 48 | help | ||
| 49 | Enter the amount of System RAM to allocate for the scrollback | ||
| 50 | buffer. Each 64KB will give you approximately 16 80x25 | ||
| 51 | screenfuls of scrollback buffer | ||
| 52 | |||
| 29 | config VIDEO_SELECT | 53 | config VIDEO_SELECT |
| 30 | bool "Video mode selection support" | 54 | bool "Video mode selection support" |
| 31 | depends on X86 && VGA_CONSOLE | 55 | depends on X86 && VGA_CONSOLE |
diff --git a/drivers/video/console/fonts.c b/drivers/video/console/fonts.c index 4fd07d9eca03..0cc1bfda76a6 100644 --- a/drivers/video/console/fonts.c +++ b/drivers/video/console/fonts.c | |||
| @@ -66,7 +66,7 @@ static const struct font_desc *fonts[] = { | |||
| 66 | #endif | 66 | #endif |
| 67 | }; | 67 | }; |
| 68 | 68 | ||
| 69 | #define num_fonts (sizeof(fonts)/sizeof(*fonts)) | 69 | #define num_fonts ARRAY_SIZE(fonts) |
| 70 | 70 | ||
| 71 | #ifdef NO_FONTS | 71 | #ifdef NO_FONTS |
| 72 | #error No fonts configured. | 72 | #error No fonts configured. |
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 762c7a593141..e99fe30e568c 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c | |||
| @@ -149,7 +149,7 @@ static inline void newport_clear_lines(int ystart, int yend, int ci) | |||
| 149 | newport_clear_screen(0, ystart, 1280 + 63, yend, ci); | 149 | newport_clear_screen(0, ystart, 1280 + 63, yend, ci); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | void newport_reset(void) | 152 | static void newport_reset(void) |
| 153 | { | 153 | { |
| 154 | unsigned short treg; | 154 | unsigned short treg; |
| 155 | int i; | 155 | int i; |
| @@ -193,7 +193,7 @@ void newport_reset(void) | |||
| 193 | * calculate the actual screen size by reading | 193 | * calculate the actual screen size by reading |
| 194 | * the video timing out of the VC2 | 194 | * the video timing out of the VC2 |
| 195 | */ | 195 | */ |
| 196 | void newport_get_screensize(void) | 196 | static void newport_get_screensize(void) |
| 197 | { | 197 | { |
| 198 | int i, cols; | 198 | int i, cols; |
| 199 | unsigned short ventry, treg; | 199 | unsigned short ventry, treg; |
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 5a86978537d2..d5a04b68c4d4 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c | |||
| @@ -93,7 +93,6 @@ static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity, | |||
| 93 | static void vgacon_invert_region(struct vc_data *c, u16 * p, int count); | 93 | static void vgacon_invert_region(struct vc_data *c, u16 * p, int count); |
| 94 | static unsigned long vgacon_uni_pagedir[2]; | 94 | static unsigned long vgacon_uni_pagedir[2]; |
| 95 | 95 | ||
| 96 | |||
| 97 | /* Description of the hardware situation */ | 96 | /* Description of the hardware situation */ |
| 98 | static unsigned long vga_vram_base; /* Base of video memory */ | 97 | static unsigned long vga_vram_base; /* Base of video memory */ |
| 99 | static unsigned long vga_vram_end; /* End of video memory */ | 98 | static unsigned long vga_vram_end; /* End of video memory */ |
| @@ -161,6 +160,201 @@ static inline void write_vga(unsigned char reg, unsigned int val) | |||
| 161 | spin_unlock_irqrestore(&vga_lock, flags); | 160 | spin_unlock_irqrestore(&vga_lock, flags); |
| 162 | } | 161 | } |
| 163 | 162 | ||
| 163 | static inline void vga_set_mem_top(struct vc_data *c) | ||
| 164 | { | ||
| 165 | write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2); | ||
| 166 | } | ||
| 167 | |||
| 168 | #ifdef CONFIG_VGACON_SOFT_SCROLLBACK | ||
| 169 | #include <linux/bootmem.h> | ||
| 170 | /* software scrollback */ | ||
| 171 | static void *vgacon_scrollback; | ||
| 172 | static int vgacon_scrollback_tail; | ||
| 173 | static int vgacon_scrollback_size; | ||
| 174 | static int vgacon_scrollback_rows; | ||
| 175 | static int vgacon_scrollback_cnt; | ||
| 176 | static int vgacon_scrollback_cur; | ||
| 177 | static int vgacon_scrollback_save; | ||
| 178 | static int vgacon_scrollback_restore; | ||
| 179 | |||
| 180 | static void vgacon_scrollback_init(int pitch) | ||
| 181 | { | ||
| 182 | int rows = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024/pitch; | ||
| 183 | |||
| 184 | if (vgacon_scrollback) { | ||
| 185 | vgacon_scrollback_cnt = 0; | ||
| 186 | vgacon_scrollback_tail = 0; | ||
| 187 | vgacon_scrollback_cur = 0; | ||
| 188 | vgacon_scrollback_rows = rows - 1; | ||
| 189 | vgacon_scrollback_size = rows * pitch; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | static void __init vgacon_scrollback_startup(void) | ||
| 194 | { | ||
| 195 | vgacon_scrollback = alloc_bootmem(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE | ||
| 196 | * 1024); | ||
| 197 | vgacon_scrollback_init(vga_video_num_columns * 2); | ||
| 198 | } | ||
| 199 | |||
| 200 | static void vgacon_scrollback_update(struct vc_data *c, int t, int count) | ||
| 201 | { | ||
| 202 | void *p; | ||
| 203 | |||
| 204 | if (!vgacon_scrollback_size || c->vc_num != fg_console) | ||
| 205 | return; | ||
| 206 | |||
| 207 | p = (void *) (c->vc_origin + t * c->vc_size_row); | ||
| 208 | |||
| 209 | while (count--) { | ||
| 210 | scr_memcpyw(vgacon_scrollback + vgacon_scrollback_tail, | ||
| 211 | p, c->vc_size_row); | ||
| 212 | vgacon_scrollback_cnt++; | ||
| 213 | p += c->vc_size_row; | ||
| 214 | vgacon_scrollback_tail += c->vc_size_row; | ||
| 215 | |||
| 216 | if (vgacon_scrollback_tail >= vgacon_scrollback_size) | ||
| 217 | vgacon_scrollback_tail = 0; | ||
| 218 | |||
| 219 | if (vgacon_scrollback_cnt > vgacon_scrollback_rows) | ||
| 220 | vgacon_scrollback_cnt = vgacon_scrollback_rows; | ||
| 221 | |||
| 222 | vgacon_scrollback_cur = vgacon_scrollback_cnt; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | static void vgacon_restore_screen(struct vc_data *c) | ||
| 227 | { | ||
| 228 | vgacon_scrollback_save = 0; | ||
| 229 | |||
| 230 | if (!vga_is_gfx && !vgacon_scrollback_restore) { | ||
| 231 | scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, | ||
| 232 | c->vc_screenbuf_size > vga_vram_size ? | ||
| 233 | vga_vram_size : c->vc_screenbuf_size); | ||
| 234 | vgacon_scrollback_restore = 1; | ||
| 235 | vgacon_scrollback_cur = vgacon_scrollback_cnt; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | static int vgacon_scrolldelta(struct vc_data *c, int lines) | ||
| 240 | { | ||
| 241 | int start, end, count, soff, diff; | ||
| 242 | void *d, *s; | ||
| 243 | |||
| 244 | if (!lines) { | ||
| 245 | c->vc_visible_origin = c->vc_origin; | ||
| 246 | vga_set_mem_top(c); | ||
| 247 | return 1; | ||
| 248 | } | ||
| 249 | |||
| 250 | if (!vgacon_scrollback) | ||
| 251 | return 1; | ||
| 252 | |||
| 253 | if (!vgacon_scrollback_save) { | ||
| 254 | vgacon_cursor(c, CM_ERASE); | ||
| 255 | vgacon_save_screen(c); | ||
| 256 | vgacon_scrollback_save = 1; | ||
| 257 | } | ||
| 258 | |||
| 259 | vgacon_scrollback_restore = 0; | ||
| 260 | start = vgacon_scrollback_cur + lines; | ||
| 261 | end = start + abs(lines); | ||
| 262 | |||
| 263 | if (start < 0) | ||
| 264 | start = 0; | ||
| 265 | |||
| 266 | if (start > vgacon_scrollback_cnt) | ||
| 267 | start = vgacon_scrollback_cnt; | ||
| 268 | |||
| 269 | if (end < 0) | ||
| 270 | end = 0; | ||
| 271 | |||
| 272 | if (end > vgacon_scrollback_cnt) | ||
| 273 | end = vgacon_scrollback_cnt; | ||
| 274 | |||
| 275 | vgacon_scrollback_cur = start; | ||
| 276 | count = end - start; | ||
| 277 | soff = vgacon_scrollback_tail - ((vgacon_scrollback_cnt - end) * | ||
| 278 | c->vc_size_row); | ||
| 279 | soff -= count * c->vc_size_row; | ||
| 280 | |||
| 281 | if (soff < 0) | ||
| 282 | soff += vgacon_scrollback_size; | ||
| 283 | |||
| 284 | count = vgacon_scrollback_cnt - start; | ||
| 285 | |||
| 286 | if (count > c->vc_rows) | ||
| 287 | count = c->vc_rows; | ||
| 288 | |||
| 289 | diff = c->vc_rows - count; | ||
| 290 | |||
| 291 | d = (void *) c->vc_origin; | ||
| 292 | s = (void *) c->vc_screenbuf; | ||
| 293 | |||
| 294 | while (count--) { | ||
| 295 | scr_memcpyw(d, vgacon_scrollback + soff, c->vc_size_row); | ||
| 296 | d += c->vc_size_row; | ||
| 297 | soff += c->vc_size_row; | ||
| 298 | |||
| 299 | if (soff >= vgacon_scrollback_size) | ||
| 300 | soff = 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | if (diff == c->vc_rows) { | ||
| 304 | vgacon_cursor(c, CM_MOVE); | ||
| 305 | } else { | ||
| 306 | while (diff--) { | ||
| 307 | scr_memcpyw(d, s, c->vc_size_row); | ||
| 308 | d += c->vc_size_row; | ||
| 309 | s += c->vc_size_row; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | return 1; | ||
| 314 | } | ||
| 315 | #else | ||
| 316 | #define vgacon_scrollback_startup(...) do { } while (0) | ||
| 317 | #define vgacon_scrollback_init(...) do { } while (0) | ||
| 318 | #define vgacon_scrollback_update(...) do { } while (0) | ||
| 319 | |||
| 320 | static void vgacon_restore_screen(struct vc_data *c) | ||
| 321 | { | ||
| 322 | if (c->vc_origin != c->vc_visible_origin) | ||
| 323 | vgacon_scrolldelta(c, 0); | ||
| 324 | } | ||
| 325 | |||
| 326 | static int vgacon_scrolldelta(struct vc_data *c, int lines) | ||
| 327 | { | ||
| 328 | if (!lines) /* Turn scrollback off */ | ||
| 329 | c->vc_visible_origin = c->vc_origin; | ||
| 330 | else { | ||
| 331 | int margin = c->vc_size_row * 4; | ||
| 332 | int ul, we, p, st; | ||
| 333 | |||
| 334 | if (vga_rolled_over > | ||
| 335 | (c->vc_scr_end - vga_vram_base) + margin) { | ||
| 336 | ul = c->vc_scr_end - vga_vram_base; | ||
| 337 | we = vga_rolled_over + c->vc_size_row; | ||
| 338 | } else { | ||
| 339 | ul = 0; | ||
| 340 | we = vga_vram_size; | ||
| 341 | } | ||
| 342 | p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + | ||
| 343 | lines * c->vc_size_row; | ||
| 344 | st = (c->vc_origin - vga_vram_base - ul + we) % we; | ||
| 345 | if (st < 2 * margin) | ||
| 346 | margin = 0; | ||
| 347 | if (p < margin) | ||
| 348 | p = 0; | ||
| 349 | if (p > st - margin) | ||
| 350 | p = st; | ||
| 351 | c->vc_visible_origin = vga_vram_base + (p + ul) % we; | ||
| 352 | } | ||
| 353 | vga_set_mem_top(c); | ||
| 354 | return 1; | ||
| 355 | } | ||
| 356 | #endif /* CONFIG_VGACON_SOFT_SCROLLBACK */ | ||
| 357 | |||
| 164 | static const char __init *vgacon_startup(void) | 358 | static const char __init *vgacon_startup(void) |
| 165 | { | 359 | { |
| 166 | const char *display_desc = NULL; | 360 | const char *display_desc = NULL; |
| @@ -330,7 +524,7 @@ static const char __init *vgacon_startup(void) | |||
| 330 | 524 | ||
| 331 | vgacon_xres = ORIG_VIDEO_COLS * VGA_FONTWIDTH; | 525 | vgacon_xres = ORIG_VIDEO_COLS * VGA_FONTWIDTH; |
| 332 | vgacon_yres = vga_scan_lines; | 526 | vgacon_yres = vga_scan_lines; |
| 333 | 527 | vgacon_scrollback_startup(); | |
| 334 | return display_desc; | 528 | return display_desc; |
| 335 | } | 529 | } |
| 336 | 530 | ||
| @@ -357,11 +551,6 @@ static void vgacon_init(struct vc_data *c, int init) | |||
| 357 | con_set_default_unimap(c); | 551 | con_set_default_unimap(c); |
| 358 | } | 552 | } |
| 359 | 553 | ||
| 360 | static inline void vga_set_mem_top(struct vc_data *c) | ||
| 361 | { | ||
| 362 | write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2); | ||
| 363 | } | ||
| 364 | |||
| 365 | static void vgacon_deinit(struct vc_data *c) | 554 | static void vgacon_deinit(struct vc_data *c) |
| 366 | { | 555 | { |
| 367 | /* When closing the last console, reset video origin */ | 556 | /* When closing the last console, reset video origin */ |
| @@ -433,29 +622,37 @@ static void vgacon_set_cursor_size(int xpos, int from, int to) | |||
| 433 | cursor_size_lastto = to; | 622 | cursor_size_lastto = to; |
| 434 | 623 | ||
| 435 | spin_lock_irqsave(&vga_lock, flags); | 624 | spin_lock_irqsave(&vga_lock, flags); |
| 436 | outb_p(0x0a, vga_video_port_reg); /* Cursor start */ | 625 | if (vga_video_type >= VIDEO_TYPE_VGAC) { |
| 437 | curs = inb_p(vga_video_port_val); | 626 | outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg); |
| 438 | outb_p(0x0b, vga_video_port_reg); /* Cursor end */ | 627 | curs = inb_p(vga_video_port_val); |
| 439 | cure = inb_p(vga_video_port_val); | 628 | outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg); |
| 629 | cure = inb_p(vga_video_port_val); | ||
| 630 | } else { | ||
| 631 | curs = 0; | ||
| 632 | cure = 0; | ||
| 633 | } | ||
| 440 | 634 | ||
| 441 | curs = (curs & 0xc0) | from; | 635 | curs = (curs & 0xc0) | from; |
| 442 | cure = (cure & 0xe0) | to; | 636 | cure = (cure & 0xe0) | to; |
| 443 | 637 | ||
| 444 | outb_p(0x0a, vga_video_port_reg); /* Cursor start */ | 638 | outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg); |
| 445 | outb_p(curs, vga_video_port_val); | 639 | outb_p(curs, vga_video_port_val); |
| 446 | outb_p(0x0b, vga_video_port_reg); /* Cursor end */ | 640 | outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg); |
| 447 | outb_p(cure, vga_video_port_val); | 641 | outb_p(cure, vga_video_port_val); |
| 448 | spin_unlock_irqrestore(&vga_lock, flags); | 642 | spin_unlock_irqrestore(&vga_lock, flags); |
| 449 | } | 643 | } |
| 450 | 644 | ||
| 451 | static void vgacon_cursor(struct vc_data *c, int mode) | 645 | static void vgacon_cursor(struct vc_data *c, int mode) |
| 452 | { | 646 | { |
| 453 | if (c->vc_origin != c->vc_visible_origin) | 647 | vgacon_restore_screen(c); |
| 454 | vgacon_scrolldelta(c, 0); | 648 | |
| 455 | switch (mode) { | 649 | switch (mode) { |
| 456 | case CM_ERASE: | 650 | case CM_ERASE: |
| 457 | write_vga(14, (c->vc_pos - vga_vram_base) / 2); | 651 | write_vga(14, (c->vc_pos - vga_vram_base) / 2); |
| 458 | vgacon_set_cursor_size(c->vc_x, 31, 30); | 652 | if (vga_video_type >= VIDEO_TYPE_VGAC) |
| 653 | vgacon_set_cursor_size(c->vc_x, 31, 30); | ||
| 654 | else | ||
| 655 | vgacon_set_cursor_size(c->vc_x, 31, 31); | ||
| 459 | break; | 656 | break; |
| 460 | 657 | ||
| 461 | case CM_MOVE: | 658 | case CM_MOVE: |
| @@ -493,7 +690,10 @@ static void vgacon_cursor(struct vc_data *c, int mode) | |||
| 493 | 10 ? 1 : 2)); | 690 | 10 ? 1 : 2)); |
| 494 | break; | 691 | break; |
| 495 | case CUR_NONE: | 692 | case CUR_NONE: |
| 496 | vgacon_set_cursor_size(c->vc_x, 31, 30); | 693 | if (vga_video_type >= VIDEO_TYPE_VGAC) |
| 694 | vgacon_set_cursor_size(c->vc_x, 31, 30); | ||
| 695 | else | ||
| 696 | vgacon_set_cursor_size(c->vc_x, 31, 31); | ||
| 497 | break; | 697 | break; |
| 498 | default: | 698 | default: |
| 499 | vgacon_set_cursor_size(c->vc_x, 1, | 699 | vgacon_set_cursor_size(c->vc_x, 1, |
| @@ -595,6 +795,7 @@ static int vgacon_switch(struct vc_data *c) | |||
| 595 | vgacon_doresize(c, c->vc_cols, c->vc_rows); | 795 | vgacon_doresize(c, c->vc_cols, c->vc_rows); |
| 596 | } | 796 | } |
| 597 | 797 | ||
| 798 | vgacon_scrollback_init(c->vc_size_row); | ||
| 598 | return 0; /* Redrawing not needed */ | 799 | return 0; /* Redrawing not needed */ |
| 599 | } | 800 | } |
| 600 | 801 | ||
| @@ -1062,37 +1263,6 @@ static int vgacon_resize(struct vc_data *c, unsigned int width, | |||
| 1062 | return 0; | 1263 | return 0; |
| 1063 | } | 1264 | } |
| 1064 | 1265 | ||
| 1065 | static int vgacon_scrolldelta(struct vc_data *c, int lines) | ||
| 1066 | { | ||
| 1067 | if (!lines) /* Turn scrollback off */ | ||
| 1068 | c->vc_visible_origin = c->vc_origin; | ||
| 1069 | else { | ||
| 1070 | int margin = c->vc_size_row * 4; | ||
| 1071 | int ul, we, p, st; | ||
| 1072 | |||
| 1073 | if (vga_rolled_over > | ||
| 1074 | (c->vc_scr_end - vga_vram_base) + margin) { | ||
| 1075 | ul = c->vc_scr_end - vga_vram_base; | ||
| 1076 | we = vga_rolled_over + c->vc_size_row; | ||
| 1077 | } else { | ||
| 1078 | ul = 0; | ||
| 1079 | we = vga_vram_size; | ||
| 1080 | } | ||
| 1081 | p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + | ||
| 1082 | lines * c->vc_size_row; | ||
| 1083 | st = (c->vc_origin - vga_vram_base - ul + we) % we; | ||
| 1084 | if (st < 2 * margin) | ||
| 1085 | margin = 0; | ||
| 1086 | if (p < margin) | ||
| 1087 | p = 0; | ||
| 1088 | if (p > st - margin) | ||
| 1089 | p = st; | ||
| 1090 | c->vc_visible_origin = vga_vram_base + (p + ul) % we; | ||
| 1091 | } | ||
| 1092 | vga_set_mem_top(c); | ||
| 1093 | return 1; | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | static int vgacon_set_origin(struct vc_data *c) | 1266 | static int vgacon_set_origin(struct vc_data *c) |
| 1097 | { | 1267 | { |
| 1098 | if (vga_is_gfx || /* We don't play origin tricks in graphic modes */ | 1268 | if (vga_is_gfx || /* We don't play origin tricks in graphic modes */ |
| @@ -1135,15 +1305,14 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, | |||
| 1135 | if (t || b != c->vc_rows || vga_is_gfx) | 1305 | if (t || b != c->vc_rows || vga_is_gfx) |
| 1136 | return 0; | 1306 | return 0; |
| 1137 | 1307 | ||
| 1138 | if (c->vc_origin != c->vc_visible_origin) | ||
| 1139 | vgacon_scrolldelta(c, 0); | ||
| 1140 | |||
| 1141 | if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2) | 1308 | if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2) |
| 1142 | return 0; | 1309 | return 0; |
| 1143 | 1310 | ||
| 1311 | vgacon_restore_screen(c); | ||
| 1144 | oldo = c->vc_origin; | 1312 | oldo = c->vc_origin; |
| 1145 | delta = lines * c->vc_size_row; | 1313 | delta = lines * c->vc_size_row; |
| 1146 | if (dir == SM_UP) { | 1314 | if (dir == SM_UP) { |
| 1315 | vgacon_scrollback_update(c, t, lines); | ||
| 1147 | if (c->vc_scr_end + delta >= vga_vram_end) { | 1316 | if (c->vc_scr_end + delta >= vga_vram_end) { |
| 1148 | scr_memcpyw((u16 *) vga_vram_base, | 1317 | scr_memcpyw((u16 *) vga_vram_base, |
| 1149 | (u16 *) (oldo + delta), | 1318 | (u16 *) (oldo + delta), |
diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index c32a2a50bfa2..1f98392a43b3 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c | |||
| @@ -85,7 +85,7 @@ static struct fb_cmap default_16_colors = { | |||
| 85 | * Allocates memory for a colormap @cmap. @len is the | 85 | * Allocates memory for a colormap @cmap. @len is the |
| 86 | * number of entries in the palette. | 86 | * number of entries in the palette. |
| 87 | * | 87 | * |
| 88 | * Returns -1 errno on error, or zero on success. | 88 | * Returns negative errno on error, or zero on success. |
| 89 | * | 89 | * |
| 90 | */ | 90 | */ |
| 91 | 91 | ||
| @@ -116,7 +116,7 @@ int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) | |||
| 116 | 116 | ||
| 117 | fail: | 117 | fail: |
| 118 | fb_dealloc_cmap(cmap); | 118 | fb_dealloc_cmap(cmap); |
| 119 | return -1; | 119 | return -ENOMEM; |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | /** | 122 | /** |
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 07d882b14396..b1a8dca76430 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c | |||
| @@ -55,7 +55,7 @@ | |||
| 55 | 55 | ||
| 56 | #define FBPIXMAPSIZE (1024 * 8) | 56 | #define FBPIXMAPSIZE (1024 * 8) |
| 57 | 57 | ||
| 58 | static struct notifier_block *fb_notifier_list; | 58 | static BLOCKING_NOTIFIER_HEAD(fb_notifier_list); |
| 59 | struct fb_info *registered_fb[FB_MAX]; | 59 | struct fb_info *registered_fb[FB_MAX]; |
| 60 | int num_registered_fb; | 60 | int num_registered_fb; |
| 61 | 61 | ||
| @@ -784,7 +784,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) | |||
| 784 | 784 | ||
| 785 | event.info = info; | 785 | event.info = info; |
| 786 | event.data = &mode1; | 786 | event.data = &mode1; |
| 787 | ret = notifier_call_chain(&fb_notifier_list, | 787 | ret = blocking_notifier_call_chain(&fb_notifier_list, |
| 788 | FB_EVENT_MODE_DELETE, &event); | 788 | FB_EVENT_MODE_DELETE, &event); |
| 789 | } | 789 | } |
| 790 | 790 | ||
| @@ -830,8 +830,8 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) | |||
| 830 | 830 | ||
| 831 | info->flags &= ~FBINFO_MISC_USEREVENT; | 831 | info->flags &= ~FBINFO_MISC_USEREVENT; |
| 832 | event.info = info; | 832 | event.info = info; |
| 833 | notifier_call_chain(&fb_notifier_list, evnt, | 833 | blocking_notifier_call_chain(&fb_notifier_list, |
| 834 | &event); | 834 | evnt, &event); |
| 835 | } | 835 | } |
| 836 | } | 836 | } |
| 837 | } | 837 | } |
| @@ -854,7 +854,8 @@ fb_blank(struct fb_info *info, int blank) | |||
| 854 | 854 | ||
| 855 | event.info = info; | 855 | event.info = info; |
| 856 | event.data = ␣ | 856 | event.data = ␣ |
| 857 | notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event); | 857 | blocking_notifier_call_chain(&fb_notifier_list, |
| 858 | FB_EVENT_BLANK, &event); | ||
| 858 | } | 859 | } |
| 859 | 860 | ||
| 860 | return ret; | 861 | return ret; |
| @@ -925,7 +926,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
| 925 | con2fb.framebuffer = -1; | 926 | con2fb.framebuffer = -1; |
| 926 | event.info = info; | 927 | event.info = info; |
| 927 | event.data = &con2fb; | 928 | event.data = &con2fb; |
| 928 | notifier_call_chain(&fb_notifier_list, | 929 | blocking_notifier_call_chain(&fb_notifier_list, |
| 929 | FB_EVENT_GET_CONSOLE_MAP, &event); | 930 | FB_EVENT_GET_CONSOLE_MAP, &event); |
| 930 | return copy_to_user(argp, &con2fb, | 931 | return copy_to_user(argp, &con2fb, |
| 931 | sizeof(con2fb)) ? -EFAULT : 0; | 932 | sizeof(con2fb)) ? -EFAULT : 0; |
| @@ -944,7 +945,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
| 944 | return -EINVAL; | 945 | return -EINVAL; |
| 945 | event.info = info; | 946 | event.info = info; |
| 946 | event.data = &con2fb; | 947 | event.data = &con2fb; |
| 947 | return notifier_call_chain(&fb_notifier_list, | 948 | return blocking_notifier_call_chain(&fb_notifier_list, |
| 948 | FB_EVENT_SET_CONSOLE_MAP, | 949 | FB_EVENT_SET_CONSOLE_MAP, |
| 949 | &event); | 950 | &event); |
| 950 | case FBIOBLANK: | 951 | case FBIOBLANK: |
| @@ -1324,7 +1325,7 @@ register_framebuffer(struct fb_info *fb_info) | |||
| 1324 | devfs_mk_cdev(MKDEV(FB_MAJOR, i), | 1325 | devfs_mk_cdev(MKDEV(FB_MAJOR, i), |
| 1325 | S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); | 1326 | S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); |
| 1326 | event.info = fb_info; | 1327 | event.info = fb_info; |
| 1327 | notifier_call_chain(&fb_notifier_list, | 1328 | blocking_notifier_call_chain(&fb_notifier_list, |
| 1328 | FB_EVENT_FB_REGISTERED, &event); | 1329 | FB_EVENT_FB_REGISTERED, &event); |
| 1329 | return 0; | 1330 | return 0; |
| 1330 | } | 1331 | } |
| @@ -1366,7 +1367,7 @@ unregister_framebuffer(struct fb_info *fb_info) | |||
| 1366 | */ | 1367 | */ |
| 1367 | int fb_register_client(struct notifier_block *nb) | 1368 | int fb_register_client(struct notifier_block *nb) |
| 1368 | { | 1369 | { |
| 1369 | return notifier_chain_register(&fb_notifier_list, nb); | 1370 | return blocking_notifier_chain_register(&fb_notifier_list, nb); |
| 1370 | } | 1371 | } |
| 1371 | 1372 | ||
| 1372 | /** | 1373 | /** |
| @@ -1375,7 +1376,7 @@ int fb_register_client(struct notifier_block *nb) | |||
| 1375 | */ | 1376 | */ |
| 1376 | int fb_unregister_client(struct notifier_block *nb) | 1377 | int fb_unregister_client(struct notifier_block *nb) |
| 1377 | { | 1378 | { |
| 1378 | return notifier_chain_unregister(&fb_notifier_list, nb); | 1379 | return blocking_notifier_chain_unregister(&fb_notifier_list, nb); |
| 1379 | } | 1380 | } |
| 1380 | 1381 | ||
| 1381 | /** | 1382 | /** |
| @@ -1393,11 +1394,13 @@ void fb_set_suspend(struct fb_info *info, int state) | |||
| 1393 | 1394 | ||
| 1394 | event.info = info; | 1395 | event.info = info; |
| 1395 | if (state) { | 1396 | if (state) { |
| 1396 | notifier_call_chain(&fb_notifier_list, FB_EVENT_SUSPEND, &event); | 1397 | blocking_notifier_call_chain(&fb_notifier_list, |
| 1398 | FB_EVENT_SUSPEND, &event); | ||
| 1397 | info->state = FBINFO_STATE_SUSPENDED; | 1399 | info->state = FBINFO_STATE_SUSPENDED; |
| 1398 | } else { | 1400 | } else { |
| 1399 | info->state = FBINFO_STATE_RUNNING; | 1401 | info->state = FBINFO_STATE_RUNNING; |
| 1400 | notifier_call_chain(&fb_notifier_list, FB_EVENT_RESUME, &event); | 1402 | blocking_notifier_call_chain(&fb_notifier_list, |
| 1403 | FB_EVENT_RESUME, &event); | ||
| 1401 | } | 1404 | } |
| 1402 | } | 1405 | } |
| 1403 | 1406 | ||
| @@ -1469,7 +1472,7 @@ int fb_new_modelist(struct fb_info *info) | |||
| 1469 | 1472 | ||
| 1470 | if (!list_empty(&info->modelist)) { | 1473 | if (!list_empty(&info->modelist)) { |
| 1471 | event.info = info; | 1474 | event.info = info; |
| 1472 | err = notifier_call_chain(&fb_notifier_list, | 1475 | err = blocking_notifier_call_chain(&fb_notifier_list, |
| 1473 | FB_EVENT_NEW_MODELIST, | 1476 | FB_EVENT_NEW_MODELIST, |
| 1474 | &event); | 1477 | &event); |
| 1475 | } | 1478 | } |
| @@ -1495,7 +1498,7 @@ int fb_con_duit(struct fb_info *info, int event, void *data) | |||
| 1495 | evnt.info = info; | 1498 | evnt.info = info; |
| 1496 | evnt.data = data; | 1499 | evnt.data = data; |
| 1497 | 1500 | ||
| 1498 | return notifier_call_chain(&fb_notifier_list, event, &evnt); | 1501 | return blocking_notifier_call_chain(&fb_notifier_list, event, &evnt); |
| 1499 | } | 1502 | } |
| 1500 | EXPORT_SYMBOL(fb_con_duit); | 1503 | EXPORT_SYMBOL(fb_con_duit); |
| 1501 | 1504 | ||
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 7c74e7325d95..53beeb4a9998 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c | |||
| @@ -1281,7 +1281,7 @@ int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) | |||
| 1281 | -EINVAL : 0; | 1281 | -EINVAL : 0; |
| 1282 | } | 1282 | } |
| 1283 | 1283 | ||
| 1284 | #if defined(__i386__) | 1284 | #if defined(CONFIG_FB_FIRMWARE_EDID) && defined(__i386__) |
| 1285 | #include <linux/pci.h> | 1285 | #include <linux/pci.h> |
| 1286 | 1286 | ||
| 1287 | /* | 1287 | /* |
| @@ -1311,11 +1311,11 @@ const unsigned char *fb_firmware_edid(struct device *device) | |||
| 1311 | { | 1311 | { |
| 1312 | return NULL; | 1312 | return NULL; |
| 1313 | } | 1313 | } |
| 1314 | #endif /* _i386_ */ | 1314 | #endif |
| 1315 | EXPORT_SYMBOL(fb_firmware_edid); | ||
| 1315 | 1316 | ||
| 1316 | EXPORT_SYMBOL(fb_parse_edid); | 1317 | EXPORT_SYMBOL(fb_parse_edid); |
| 1317 | EXPORT_SYMBOL(fb_edid_to_monspecs); | 1318 | EXPORT_SYMBOL(fb_edid_to_monspecs); |
| 1318 | EXPORT_SYMBOL(fb_firmware_edid); | ||
| 1319 | EXPORT_SYMBOL(fb_get_mode); | 1319 | EXPORT_SYMBOL(fb_get_mode); |
| 1320 | EXPORT_SYMBOL(fb_validate_mode); | 1320 | EXPORT_SYMBOL(fb_validate_mode); |
| 1321 | EXPORT_SYMBOL(fb_destroy_modedb); | 1321 | EXPORT_SYMBOL(fb_destroy_modedb); |
diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 6d26057337e2..b72b05250a9d 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c | |||
| @@ -348,7 +348,7 @@ static ssize_t store_cmap(struct class_device *class_device, const char *buf, | |||
| 348 | fb_copy_cmap(&umap, &fb_info->cmap); | 348 | fb_copy_cmap(&umap, &fb_info->cmap); |
| 349 | fb_dealloc_cmap(&umap); | 349 | fb_dealloc_cmap(&umap); |
| 350 | 350 | ||
| 351 | return rc; | 351 | return rc ?: count; |
| 352 | } | 352 | } |
| 353 | for (i = 0; i < length; i++) { | 353 | for (i = 0; i < length; i++) { |
| 354 | u16 red, blue, green, tsp; | 354 | u16 red, blue, green, tsp; |
| @@ -367,7 +367,7 @@ static ssize_t store_cmap(struct class_device *class_device, const char *buf, | |||
| 367 | if (transp) | 367 | if (transp) |
| 368 | fb_info->cmap.transp[i] = tsp; | 368 | fb_info->cmap.transp[i] = tsp; |
| 369 | } | 369 | } |
| 370 | return 0; | 370 | return count; |
| 371 | } | 371 | } |
| 372 | 372 | ||
| 373 | static ssize_t show_cmap(struct class_device *class_device, char *buf) | 373 | static ssize_t show_cmap(struct class_device *class_device, char *buf) |
diff --git a/drivers/video/geode/Kconfig b/drivers/video/geode/Kconfig index 42fb9a89a792..4e173ef20a7d 100644 --- a/drivers/video/geode/Kconfig +++ b/drivers/video/geode/Kconfig | |||
| @@ -8,9 +8,24 @@ config FB_GEODE | |||
| 8 | Say 'Y' here to allow you to select framebuffer drivers for | 8 | Say 'Y' here to allow you to select framebuffer drivers for |
| 9 | the AMD Geode family of processors. | 9 | the AMD Geode family of processors. |
| 10 | 10 | ||
| 11 | config FB_GEODE_GX | ||
| 12 | tristate "AMD Geode GX framebuffer support (EXPERIMENTAL)" | ||
| 13 | depends on FB && FB_GEODE && EXPERIMENTAL | ||
| 14 | select FB_CFB_FILLRECT | ||
| 15 | select FB_CFB_COPYAREA | ||
| 16 | select FB_CFB_IMAGEBLIT | ||
| 17 | ---help--- | ||
| 18 | Framebuffer driver for the display controller integrated into the | ||
| 19 | AMD Geode GX processors. | ||
| 20 | |||
| 21 | To compile this driver as a module, choose M here: the module will be | ||
| 22 | called gxfb. | ||
| 23 | |||
| 24 | If unsure, say N. | ||
| 25 | |||
| 11 | config FB_GEODE_GX1 | 26 | config FB_GEODE_GX1 |
| 12 | tristate "AMD Geode GX1 framebuffer support (EXPERIMENTAL)" | 27 | tristate "AMD Geode GX1 framebuffer support (EXPERIMENTAL)" |
| 13 | depends on FB_GEODE && EXPERIMENTAL | 28 | depends on FB && FB_GEODE && EXPERIMENTAL |
| 14 | select FB_CFB_FILLRECT | 29 | select FB_CFB_FILLRECT |
| 15 | select FB_CFB_COPYAREA | 30 | select FB_CFB_COPYAREA |
| 16 | select FB_CFB_IMAGEBLIT | 31 | select FB_CFB_IMAGEBLIT |
diff --git a/drivers/video/geode/Makefile b/drivers/video/geode/Makefile index 13ad501ea990..f896565bc312 100644 --- a/drivers/video/geode/Makefile +++ b/drivers/video/geode/Makefile | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | # Makefile for the Geode family framebuffer drivers | 1 | # Makefile for the Geode family framebuffer drivers |
| 2 | 2 | ||
| 3 | obj-$(CONFIG_FB_GEODE_GX1) += gx1fb.o | 3 | obj-$(CONFIG_FB_GEODE_GX1) += gx1fb.o |
| 4 | obj-$(CONFIG_FB_GEODE_GX) += gxfb.o | ||
| 4 | 5 | ||
| 5 | gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o | 6 | gx1fb-objs := gx1fb_core.o display_gx1.o video_cs5530.o |
| 7 | gxfb-objs := gxfb_core.o display_gx.o video_gx.o | ||
diff --git a/drivers/video/geode/display_gx.c b/drivers/video/geode/display_gx.c new file mode 100644 index 000000000000..825c3405f5c2 --- /dev/null +++ b/drivers/video/geode/display_gx.c | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | /* | ||
| 2 | * Geode GX display controller. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Arcom Control Systems Ltd. | ||
| 5 | * | ||
| 6 | * Portions from AMD's original 2.4 driver: | ||
| 7 | * Copyright (C) 2004 Advanced Micro Devices, Inc. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms of the GNU General Public License as published by * the | ||
| 11 | * Free Software Foundation; either version 2 of the License, or * (at your | ||
| 12 | * option) any later version. | ||
| 13 | */ | ||
| 14 | #include <linux/spinlock.h> | ||
| 15 | #include <linux/fb.h> | ||
| 16 | #include <linux/delay.h> | ||
| 17 | #include <asm/io.h> | ||
| 18 | #include <asm/div64.h> | ||
| 19 | #include <asm/delay.h> | ||
| 20 | |||
| 21 | #include "geodefb.h" | ||
| 22 | #include "display_gx.h" | ||
| 23 | |||
| 24 | int gx_frame_buffer_size(void) | ||
| 25 | { | ||
| 26 | /* Assuming 16 MiB. */ | ||
| 27 | return 16*1024*1024; | ||
| 28 | } | ||
| 29 | |||
| 30 | int gx_line_delta(int xres, int bpp) | ||
| 31 | { | ||
| 32 | /* Must be a multiple of 8 bytes. */ | ||
| 33 | return (xres * (bpp >> 3) + 7) & ~0x7; | ||
| 34 | } | ||
| 35 | |||
| 36 | static void gx_set_mode(struct fb_info *info) | ||
| 37 | { | ||
| 38 | struct geodefb_par *par = info->par; | ||
| 39 | u32 gcfg, dcfg; | ||
| 40 | int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal; | ||
| 41 | int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal; | ||
| 42 | |||
| 43 | /* Unlock the display controller registers. */ | ||
| 44 | readl(par->dc_regs + DC_UNLOCK); | ||
| 45 | writel(DC_UNLOCK_CODE, par->dc_regs + DC_UNLOCK); | ||
| 46 | |||
| 47 | gcfg = readl(par->dc_regs + DC_GENERAL_CFG); | ||
| 48 | dcfg = readl(par->dc_regs + DC_DISPLAY_CFG); | ||
| 49 | |||
| 50 | /* Disable the timing generator. */ | ||
| 51 | dcfg &= ~(DC_DCFG_TGEN); | ||
| 52 | writel(dcfg, par->dc_regs + DC_DISPLAY_CFG); | ||
| 53 | |||
| 54 | /* Wait for pending memory requests before disabling the FIFO load. */ | ||
| 55 | udelay(100); | ||
| 56 | |||
| 57 | /* Disable FIFO load and compression. */ | ||
| 58 | gcfg &= ~(DC_GCFG_DFLE | DC_GCFG_CMPE | DC_GCFG_DECE); | ||
| 59 | writel(gcfg, par->dc_regs + DC_GENERAL_CFG); | ||
| 60 | |||
| 61 | /* Setup DCLK and its divisor. */ | ||
| 62 | par->vid_ops->set_dclk(info); | ||
| 63 | |||
| 64 | /* | ||
| 65 | * Setup new mode. | ||
| 66 | */ | ||
| 67 | |||
| 68 | /* Clear all unused feature bits. */ | ||
| 69 | gcfg &= DC_GCFG_YUVM | DC_GCFG_VDSE; | ||
| 70 | dcfg = 0; | ||
| 71 | |||
| 72 | /* Set FIFO priority (default 6/5) and enable. */ | ||
| 73 | /* FIXME: increase fifo priority for 1280x1024 and higher modes? */ | ||
| 74 | gcfg |= (6 << DC_GCFG_DFHPEL_POS) | (5 << DC_GCFG_DFHPSL_POS) | DC_GCFG_DFLE; | ||
| 75 | |||
| 76 | /* Framebuffer start offset. */ | ||
| 77 | writel(0, par->dc_regs + DC_FB_ST_OFFSET); | ||
| 78 | |||
| 79 | /* Line delta and line buffer length. */ | ||
| 80 | writel(info->fix.line_length >> 3, par->dc_regs + DC_GFX_PITCH); | ||
| 81 | writel(((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2, | ||
| 82 | par->dc_regs + DC_LINE_SIZE); | ||
| 83 | |||
| 84 | /* Enable graphics and video data and unmask address lines. */ | ||
| 85 | dcfg |= DC_DCFG_GDEN | DC_DCFG_VDEN | DC_DCFG_A20M | DC_DCFG_A18M; | ||
| 86 | |||
| 87 | /* Set pixel format. */ | ||
| 88 | switch (info->var.bits_per_pixel) { | ||
| 89 | case 8: | ||
| 90 | dcfg |= DC_DCFG_DISP_MODE_8BPP; | ||
| 91 | break; | ||
| 92 | case 16: | ||
| 93 | dcfg |= DC_DCFG_DISP_MODE_16BPP; | ||
| 94 | dcfg |= DC_DCFG_16BPP_MODE_565; | ||
| 95 | break; | ||
| 96 | case 32: | ||
| 97 | dcfg |= DC_DCFG_DISP_MODE_24BPP; | ||
| 98 | dcfg |= DC_DCFG_PALB; | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Enable timing generator. */ | ||
| 103 | dcfg |= DC_DCFG_TGEN; | ||
| 104 | |||
| 105 | /* Horizontal and vertical timings. */ | ||
| 106 | hactive = info->var.xres; | ||
| 107 | hblankstart = hactive; | ||
| 108 | hsyncstart = hblankstart + info->var.right_margin; | ||
| 109 | hsyncend = hsyncstart + info->var.hsync_len; | ||
| 110 | hblankend = hsyncend + info->var.left_margin; | ||
| 111 | htotal = hblankend; | ||
| 112 | |||
| 113 | vactive = info->var.yres; | ||
| 114 | vblankstart = vactive; | ||
| 115 | vsyncstart = vblankstart + info->var.lower_margin; | ||
| 116 | vsyncend = vsyncstart + info->var.vsync_len; | ||
| 117 | vblankend = vsyncend + info->var.upper_margin; | ||
| 118 | vtotal = vblankend; | ||
| 119 | |||
| 120 | writel((hactive - 1) | ((htotal - 1) << 16), par->dc_regs + DC_H_ACTIVE_TIMING); | ||
| 121 | writel((hblankstart - 1) | ((hblankend - 1) << 16), par->dc_regs + DC_H_BLANK_TIMING); | ||
| 122 | writel((hsyncstart - 1) | ((hsyncend - 1) << 16), par->dc_regs + DC_H_SYNC_TIMING); | ||
| 123 | |||
| 124 | writel((vactive - 1) | ((vtotal - 1) << 16), par->dc_regs + DC_V_ACTIVE_TIMING); | ||
| 125 | writel((vblankstart - 1) | ((vblankend - 1) << 16), par->dc_regs + DC_V_BLANK_TIMING); | ||
| 126 | writel((vsyncstart - 1) | ((vsyncend - 1) << 16), par->dc_regs + DC_V_SYNC_TIMING); | ||
| 127 | |||
| 128 | /* Write final register values. */ | ||
| 129 | writel(dcfg, par->dc_regs + DC_DISPLAY_CFG); | ||
| 130 | writel(gcfg, par->dc_regs + DC_GENERAL_CFG); | ||
| 131 | |||
| 132 | par->vid_ops->configure_display(info); | ||
| 133 | |||
| 134 | /* Relock display controller registers */ | ||
| 135 | writel(0, par->dc_regs + DC_UNLOCK); | ||
| 136 | } | ||
| 137 | |||
| 138 | static void gx_set_hw_palette_reg(struct fb_info *info, unsigned regno, | ||
| 139 | unsigned red, unsigned green, unsigned blue) | ||
| 140 | { | ||
| 141 | struct geodefb_par *par = info->par; | ||
| 142 | int val; | ||
| 143 | |||
| 144 | /* Hardware palette is in RGB 8-8-8 format. */ | ||
| 145 | val = (red << 8) & 0xff0000; | ||
| 146 | val |= (green) & 0x00ff00; | ||
| 147 | val |= (blue >> 8) & 0x0000ff; | ||
| 148 | |||
| 149 | writel(regno, par->dc_regs + DC_PAL_ADDRESS); | ||
| 150 | writel(val, par->dc_regs + DC_PAL_DATA); | ||
| 151 | } | ||
| 152 | |||
| 153 | struct geode_dc_ops gx_dc_ops = { | ||
| 154 | .set_mode = gx_set_mode, | ||
| 155 | .set_palette_reg = gx_set_hw_palette_reg, | ||
| 156 | }; | ||
diff --git a/drivers/video/geode/display_gx.h b/drivers/video/geode/display_gx.h new file mode 100644 index 000000000000..86c623361305 --- /dev/null +++ b/drivers/video/geode/display_gx.h | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | /* | ||
| 2 | * Geode GX display controller | ||
| 3 | * | ||
| 4 | * Copyright (C) 2006 Arcom Control Systems Ltd. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | */ | ||
| 11 | #ifndef __DISPLAY_GX_H__ | ||
| 12 | #define __DISPLAY_GX_H__ | ||
| 13 | |||
| 14 | int gx_frame_buffer_size(void); | ||
| 15 | int gx_line_delta(int xres, int bpp); | ||
| 16 | |||
| 17 | extern struct geode_dc_ops gx_dc_ops; | ||
| 18 | |||
| 19 | /* Display controller registers */ | ||
| 20 | |||
| 21 | #define DC_UNLOCK 0x00 | ||
| 22 | # define DC_UNLOCK_CODE 0x00004758 | ||
| 23 | |||
| 24 | #define DC_GENERAL_CFG 0x04 | ||
| 25 | # define DC_GCFG_DFLE 0x00000001 | ||
| 26 | # define DC_GCFG_CURE 0x00000002 | ||
| 27 | # define DC_GCFG_ICNE 0x00000004 | ||
| 28 | # define DC_GCFG_VIDE 0x00000008 | ||
| 29 | # define DC_GCFG_CMPE 0x00000020 | ||
| 30 | # define DC_GCFG_DECE 0x00000040 | ||
| 31 | # define DC_GCFG_VGAE 0x00000080 | ||
| 32 | # define DC_GCFG_DFHPSL_MASK 0x00000F00 | ||
| 33 | # define DC_GCFG_DFHPSL_POS 8 | ||
| 34 | # define DC_GCFG_DFHPEL_MASK 0x0000F000 | ||
| 35 | # define DC_GCFG_DFHPEL_POS 12 | ||
| 36 | # define DC_GCFG_STFM 0x00010000 | ||
| 37 | # define DC_GCFG_FDTY 0x00020000 | ||
| 38 | # define DC_GCFG_VGAFT 0x00040000 | ||
| 39 | # define DC_GCFG_VDSE 0x00080000 | ||
| 40 | # define DC_GCFG_YUVM 0x00100000 | ||
| 41 | # define DC_GCFG_VFSL 0x00800000 | ||
| 42 | # define DC_GCFG_SIGE 0x01000000 | ||
| 43 | # define DC_GCFG_SGRE 0x02000000 | ||
| 44 | # define DC_GCFG_SGFR 0x04000000 | ||
| 45 | # define DC_GCFG_CRC_MODE 0x08000000 | ||
| 46 | # define DC_GCFG_DIAG 0x10000000 | ||
| 47 | # define DC_GCFG_CFRW 0x20000000 | ||
| 48 | |||
| 49 | #define DC_DISPLAY_CFG 0x08 | ||
| 50 | # define DC_DCFG_TGEN 0x00000001 | ||
| 51 | # define DC_DCFG_GDEN 0x00000008 | ||
| 52 | # define DC_DCFG_VDEN 0x00000010 | ||
| 53 | # define DC_DCFG_TRUP 0x00000040 | ||
| 54 | # define DC_DCFG_DISP_MODE_MASK 0x00000300 | ||
| 55 | # define DC_DCFG_DISP_MODE_8BPP 0x00000000 | ||
| 56 | # define DC_DCFG_DISP_MODE_16BPP 0x00000100 | ||
| 57 | # define DC_DCFG_DISP_MODE_24BPP 0x00000200 | ||
| 58 | # define DC_DCFG_16BPP_MODE_MASK 0x00000c00 | ||
| 59 | # define DC_DCFG_16BPP_MODE_565 0x00000000 | ||
| 60 | # define DC_DCFG_16BPP_MODE_555 0x00000100 | ||
| 61 | # define DC_DCFG_16BPP_MODE_444 0x00000200 | ||
| 62 | # define DC_DCFG_DCEN 0x00080000 | ||
| 63 | # define DC_DCFG_PALB 0x02000000 | ||
| 64 | # define DC_DCFG_FRLK 0x04000000 | ||
| 65 | # define DC_DCFG_VISL 0x08000000 | ||
| 66 | # define DC_DCFG_FRSL 0x20000000 | ||
| 67 | # define DC_DCFG_A18M 0x40000000 | ||
| 68 | # define DC_DCFG_A20M 0x80000000 | ||
| 69 | |||
| 70 | #define DC_FB_ST_OFFSET 0x10 | ||
| 71 | |||
| 72 | #define DC_LINE_SIZE 0x30 | ||
| 73 | # define DC_LINE_SIZE_FB_LINE_SIZE_MASK 0x000007ff | ||
| 74 | # define DC_LINE_SIZE_FB_LINE_SIZE_POS 0 | ||
| 75 | # define DC_LINE_SIZE_CB_LINE_SIZE_MASK 0x007f0000 | ||
| 76 | # define DC_LINE_SIZE_CB_LINE_SIZE_POS 16 | ||
| 77 | # define DC_LINE_SIZE_VID_LINE_SIZE_MASK 0xff000000 | ||
| 78 | # define DC_LINE_SIZE_VID_LINE_SIZE_POS 24 | ||
| 79 | |||
| 80 | #define DC_GFX_PITCH 0x34 | ||
| 81 | # define DC_GFX_PITCH_FB_PITCH_MASK 0x0000ffff | ||
| 82 | # define DC_GFX_PITCH_FB_PITCH_POS 0 | ||
| 83 | # define DC_GFX_PITCH_CB_PITCH_MASK 0xffff0000 | ||
| 84 | # define DC_GFX_PITCH_CB_PITCH_POS 16 | ||
| 85 | |||
| 86 | #define DC_H_ACTIVE_TIMING 0x40 | ||
| 87 | #define DC_H_BLANK_TIMING 0x44 | ||
| 88 | #define DC_H_SYNC_TIMING 0x48 | ||
| 89 | #define DC_V_ACTIVE_TIMING 0x50 | ||
| 90 | #define DC_V_BLANK_TIMING 0x54 | ||
| 91 | #define DC_V_SYNC_TIMING 0x58 | ||
| 92 | |||
| 93 | #define DC_PAL_ADDRESS 0x70 | ||
| 94 | #define DC_PAL_DATA 0x74 | ||
| 95 | |||
| 96 | #endif /* !__DISPLAY_GX1_H__ */ | ||
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c new file mode 100644 index 000000000000..89c34b15f5d4 --- /dev/null +++ b/drivers/video/geode/gxfb_core.c | |||
| @@ -0,0 +1,423 @@ | |||
| 1 | /* | ||
| 2 | * Geode GX framebuffer driver. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2006 Arcom Control Systems Ltd. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | * | ||
| 11 | * | ||
| 12 | * This driver assumes that the BIOS has created a virtual PCI device header | ||
| 13 | * for the video device. The PCI header is assumed to contain the following | ||
| 14 | * BARs: | ||
| 15 | * | ||
| 16 | * BAR0 - framebuffer memory | ||
| 17 | * BAR1 - graphics processor registers | ||
| 18 | * BAR2 - display controller registers | ||
| 19 | * BAR3 - video processor and flat panel control registers. | ||
| 20 | * | ||
| 21 | * 16 MiB of framebuffer memory is assumed to be available. | ||
| 22 | */ | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/errno.h> | ||
| 26 | #include <linux/string.h> | ||
| 27 | #include <linux/mm.h> | ||
| 28 | #include <linux/tty.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | #include <linux/delay.h> | ||
| 31 | #include <linux/fb.h> | ||
| 32 | #include <linux/init.h> | ||
| 33 | #include <linux/pci.h> | ||
| 34 | |||
| 35 | #include "geodefb.h" | ||
| 36 | #include "display_gx.h" | ||
| 37 | #include "video_gx.h" | ||
| 38 | |||
| 39 | static char mode_option[32] = "640x480-16@60"; | ||
| 40 | |||
| 41 | /* Modes relevant to the GX (taken from modedb.c) */ | ||
| 42 | static const struct fb_videomode __initdata gx_modedb[] = { | ||
| 43 | /* 640x480-60 VESA */ | ||
| 44 | { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, | ||
| 45 | 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 46 | /* 640x480-75 VESA */ | ||
| 47 | { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3, | ||
| 48 | 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 49 | /* 640x480-85 VESA */ | ||
| 50 | { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3, | ||
| 51 | 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 52 | /* 800x600-60 VESA */ | ||
| 53 | { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4, | ||
| 54 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 55 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 56 | /* 800x600-75 VESA */ | ||
| 57 | { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3, | ||
| 58 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 59 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 60 | /* 800x600-85 VESA */ | ||
| 61 | { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3, | ||
| 62 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 63 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 64 | /* 1024x768-60 VESA */ | ||
| 65 | { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6, | ||
| 66 | 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 67 | /* 1024x768-75 VESA */ | ||
| 68 | { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3, | ||
| 69 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 70 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 71 | /* 1024x768-85 VESA */ | ||
| 72 | { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3, | ||
| 73 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 74 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 75 | /* 1280x960-60 VESA */ | ||
| 76 | { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3, | ||
| 77 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 78 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 79 | /* 1280x960-85 VESA */ | ||
| 80 | { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3, | ||
| 81 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 82 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 83 | /* 1280x1024-60 VESA */ | ||
| 84 | { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, | ||
| 85 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 86 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 87 | /* 1280x1024-75 VESA */ | ||
| 88 | { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3, | ||
| 89 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 90 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 91 | /* 1280x1024-85 VESA */ | ||
| 92 | { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3, | ||
| 93 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 94 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 95 | /* 1600x1200-60 VESA */ | ||
| 96 | { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, | ||
| 97 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 98 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 99 | /* 1600x1200-75 VESA */ | ||
| 100 | { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, | ||
| 101 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 102 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 103 | /* 1600x1200-85 VESA */ | ||
| 104 | { NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3, | ||
| 105 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
| 106 | FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, | ||
| 107 | }; | ||
| 108 | |||
| 109 | static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
| 110 | { | ||
| 111 | if (var->xres > 1600 || var->yres > 1200) | ||
| 112 | return -EINVAL; | ||
| 113 | if ((var->xres > 1280 || var->yres > 1024) && var->bits_per_pixel > 16) | ||
| 114 | return -EINVAL; | ||
| 115 | |||
| 116 | if (var->bits_per_pixel == 32) { | ||
| 117 | var->red.offset = 16; var->red.length = 8; | ||
| 118 | var->green.offset = 8; var->green.length = 8; | ||
| 119 | var->blue.offset = 0; var->blue.length = 8; | ||
| 120 | } else if (var->bits_per_pixel == 16) { | ||
| 121 | var->red.offset = 11; var->red.length = 5; | ||
| 122 | var->green.offset = 5; var->green.length = 6; | ||
| 123 | var->blue.offset = 0; var->blue.length = 5; | ||
| 124 | } else if (var->bits_per_pixel == 8) { | ||
| 125 | var->red.offset = 0; var->red.length = 8; | ||
| 126 | var->green.offset = 0; var->green.length = 8; | ||
| 127 | var->blue.offset = 0; var->blue.length = 8; | ||
| 128 | } else | ||
| 129 | return -EINVAL; | ||
| 130 | var->transp.offset = 0; var->transp.length = 0; | ||
| 131 | |||
| 132 | /* Enough video memory? */ | ||
| 133 | if (gx_line_delta(var->xres, var->bits_per_pixel) * var->yres > info->fix.smem_len) | ||
| 134 | return -EINVAL; | ||
| 135 | |||
| 136 | /* FIXME: Check timing parameters here? */ | ||
| 137 | |||
| 138 | return 0; | ||
| 139 | } | ||
| 140 | |||
| 141 | static int gxfb_set_par(struct fb_info *info) | ||
| 142 | { | ||
| 143 | struct geodefb_par *par = info->par; | ||
| 144 | |||
| 145 | if (info->var.bits_per_pixel > 8) { | ||
| 146 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
| 147 | fb_dealloc_cmap(&info->cmap); | ||
| 148 | } else { | ||
| 149 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
| 150 | fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0); | ||
| 151 | } | ||
| 152 | |||
| 153 | info->fix.line_length = gx_line_delta(info->var.xres, info->var.bits_per_pixel); | ||
| 154 | |||
| 155 | par->dc_ops->set_mode(info); | ||
| 156 | |||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) | ||
| 161 | { | ||
| 162 | chan &= 0xffff; | ||
| 163 | chan >>= 16 - bf->length; | ||
| 164 | return chan << bf->offset; | ||
| 165 | } | ||
| 166 | |||
| 167 | static int gxfb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
| 168 | unsigned blue, unsigned transp, | ||
| 169 | struct fb_info *info) | ||
| 170 | { | ||
| 171 | struct geodefb_par *par = info->par; | ||
| 172 | |||
| 173 | if (info->var.grayscale) { | ||
| 174 | /* grayscale = 0.30*R + 0.59*G + 0.11*B */ | ||
| 175 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | ||
| 176 | } | ||
| 177 | |||
| 178 | /* Truecolor has hardware independent palette */ | ||
| 179 | if (info->fix.visual == FB_VISUAL_TRUECOLOR) { | ||
| 180 | u32 *pal = info->pseudo_palette; | ||
| 181 | u32 v; | ||
| 182 | |||
| 183 | if (regno >= 16) | ||
| 184 | return -EINVAL; | ||
| 185 | |||
| 186 | v = chan_to_field(red, &info->var.red); | ||
| 187 | v |= chan_to_field(green, &info->var.green); | ||
| 188 | v |= chan_to_field(blue, &info->var.blue); | ||
| 189 | |||
| 190 | pal[regno] = v; | ||
| 191 | } else { | ||
| 192 | if (regno >= 256) | ||
| 193 | return -EINVAL; | ||
| 194 | |||
| 195 | par->dc_ops->set_palette_reg(info, regno, red, green, blue); | ||
| 196 | } | ||
| 197 | |||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static int gxfb_blank(int blank_mode, struct fb_info *info) | ||
| 202 | { | ||
| 203 | struct geodefb_par *par = info->par; | ||
| 204 | |||
| 205 | return par->vid_ops->blank_display(info, blank_mode); | ||
| 206 | } | ||
| 207 | |||
| 208 | static int __init gxfb_map_video_memory(struct fb_info *info, struct pci_dev *dev) | ||
| 209 | { | ||
| 210 | struct geodefb_par *par = info->par; | ||
| 211 | int fb_len; | ||
| 212 | int ret; | ||
| 213 | |||
| 214 | ret = pci_enable_device(dev); | ||
| 215 | if (ret < 0) | ||
| 216 | return ret; | ||
| 217 | |||
| 218 | ret = pci_request_region(dev, 3, "gxfb (video processor)"); | ||
| 219 | if (ret < 0) | ||
| 220 | return ret; | ||
| 221 | par->vid_regs = ioremap(pci_resource_start(dev, 3), | ||
| 222 | pci_resource_len(dev, 3)); | ||
| 223 | if (!par->vid_regs) | ||
| 224 | return -ENOMEM; | ||
| 225 | |||
| 226 | ret = pci_request_region(dev, 2, "gxfb (display controller)"); | ||
| 227 | if (ret < 0) | ||
| 228 | return ret; | ||
| 229 | par->dc_regs = ioremap(pci_resource_start(dev, 2), pci_resource_len(dev, 2)); | ||
| 230 | if (!par->dc_regs) | ||
| 231 | return -ENOMEM; | ||
| 232 | |||
| 233 | ret = pci_request_region(dev, 0, "gxfb (framebuffer)"); | ||
| 234 | if (ret < 0) | ||
| 235 | return ret; | ||
| 236 | if ((fb_len = gx_frame_buffer_size()) < 0) | ||
| 237 | return -ENOMEM; | ||
| 238 | info->fix.smem_start = pci_resource_start(dev, 0); | ||
| 239 | info->fix.smem_len = fb_len; | ||
| 240 | info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); | ||
| 241 | if (!info->screen_base) | ||
| 242 | return -ENOMEM; | ||
| 243 | |||
| 244 | dev_info(&dev->dev, "%d Kibyte of video memory at 0x%lx\n", | ||
| 245 | info->fix.smem_len / 1024, info->fix.smem_start); | ||
| 246 | |||
| 247 | return 0; | ||
| 248 | } | ||
| 249 | |||
| 250 | static struct fb_ops gxfb_ops = { | ||
| 251 | .owner = THIS_MODULE, | ||
| 252 | .fb_check_var = gxfb_check_var, | ||
| 253 | .fb_set_par = gxfb_set_par, | ||
| 254 | .fb_setcolreg = gxfb_setcolreg, | ||
| 255 | .fb_blank = gxfb_blank, | ||
| 256 | /* No HW acceleration for now. */ | ||
| 257 | .fb_fillrect = cfb_fillrect, | ||
| 258 | .fb_copyarea = cfb_copyarea, | ||
| 259 | .fb_imageblit = cfb_imageblit, | ||
| 260 | }; | ||
| 261 | |||
| 262 | static struct fb_info * __init gxfb_init_fbinfo(struct device *dev) | ||
| 263 | { | ||
| 264 | struct geodefb_par *par; | ||
| 265 | struct fb_info *info; | ||
| 266 | |||
| 267 | /* Alloc enough space for the pseudo palette. */ | ||
| 268 | info = framebuffer_alloc(sizeof(struct geodefb_par) + sizeof(u32) * 16, dev); | ||
| 269 | if (!info) | ||
| 270 | return NULL; | ||
| 271 | |||
| 272 | par = info->par; | ||
| 273 | |||
| 274 | strcpy(info->fix.id, "Geode GX"); | ||
| 275 | |||
| 276 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
| 277 | info->fix.type_aux = 0; | ||
| 278 | info->fix.xpanstep = 0; | ||
| 279 | info->fix.ypanstep = 0; | ||
| 280 | info->fix.ywrapstep = 0; | ||
| 281 | info->fix.accel = FB_ACCEL_NONE; | ||
| 282 | |||
| 283 | info->var.nonstd = 0; | ||
| 284 | info->var.activate = FB_ACTIVATE_NOW; | ||
| 285 | info->var.height = -1; | ||
| 286 | info->var.width = -1; | ||
| 287 | info->var.accel_flags = 0; | ||
| 288 | info->var.vmode = FB_VMODE_NONINTERLACED; | ||
| 289 | |||
| 290 | info->fbops = &gxfb_ops; | ||
| 291 | info->flags = FBINFO_DEFAULT; | ||
| 292 | info->node = -1; | ||
| 293 | |||
| 294 | info->pseudo_palette = (void *)par + sizeof(struct geodefb_par); | ||
| 295 | |||
| 296 | info->var.grayscale = 0; | ||
| 297 | |||
| 298 | return info; | ||
| 299 | } | ||
| 300 | |||
| 301 | static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||
| 302 | { | ||
| 303 | struct geodefb_par *par; | ||
| 304 | struct fb_info *info; | ||
| 305 | int ret; | ||
| 306 | |||
| 307 | info = gxfb_init_fbinfo(&pdev->dev); | ||
| 308 | if (!info) | ||
| 309 | return -ENOMEM; | ||
| 310 | par = info->par; | ||
| 311 | |||
| 312 | /* GX display controller and GX video device. */ | ||
| 313 | par->dc_ops = &gx_dc_ops; | ||
| 314 | par->vid_ops = &gx_vid_ops; | ||
| 315 | |||
| 316 | if ((ret = gxfb_map_video_memory(info, pdev)) < 0) { | ||
| 317 | dev_err(&pdev->dev, "failed to map frame buffer or controller registers\n"); | ||
| 318 | goto err; | ||
| 319 | } | ||
| 320 | |||
| 321 | ret = fb_find_mode(&info->var, info, mode_option, | ||
| 322 | gx_modedb, ARRAY_SIZE(gx_modedb), NULL, 16); | ||
| 323 | if (ret == 0 || ret == 4) { | ||
| 324 | dev_err(&pdev->dev, "could not find valid video mode\n"); | ||
| 325 | ret = -EINVAL; | ||
| 326 | goto err; | ||
| 327 | } | ||
| 328 | |||
| 329 | /* Clear the frame buffer of garbage. */ | ||
| 330 | memset_io(info->screen_base, 0, info->fix.smem_len); | ||
| 331 | |||
| 332 | gxfb_check_var(&info->var, info); | ||
| 333 | gxfb_set_par(info); | ||
| 334 | |||
| 335 | if (register_framebuffer(info) < 0) { | ||
| 336 | ret = -EINVAL; | ||
| 337 | goto err; | ||
| 338 | } | ||
| 339 | pci_set_drvdata(pdev, info); | ||
| 340 | printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id); | ||
| 341 | return 0; | ||
| 342 | |||
| 343 | err: | ||
| 344 | if (info->screen_base) { | ||
| 345 | iounmap(info->screen_base); | ||
| 346 | pci_release_region(pdev, 0); | ||
| 347 | } | ||
| 348 | if (par->vid_regs) { | ||
| 349 | iounmap(par->vid_regs); | ||
| 350 | pci_release_region(pdev, 3); | ||
| 351 | } | ||
| 352 | if (par->dc_regs) { | ||
| 353 | iounmap(par->dc_regs); | ||
| 354 | pci_release_region(pdev, 2); | ||
| 355 | } | ||
| 356 | |||
| 357 | pci_disable_device(pdev); | ||
| 358 | |||
| 359 | if (info) | ||
| 360 | framebuffer_release(info); | ||
| 361 | return ret; | ||
| 362 | } | ||
| 363 | |||
| 364 | static void gxfb_remove(struct pci_dev *pdev) | ||
| 365 | { | ||
| 366 | struct fb_info *info = pci_get_drvdata(pdev); | ||
| 367 | struct geodefb_par *par = info->par; | ||
| 368 | |||
| 369 | unregister_framebuffer(info); | ||
| 370 | |||
| 371 | iounmap((void __iomem *)info->screen_base); | ||
| 372 | pci_release_region(pdev, 0); | ||
| 373 | |||
| 374 | iounmap(par->vid_regs); | ||
| 375 | pci_release_region(pdev, 3); | ||
| 376 | |||
| 377 | iounmap(par->dc_regs); | ||
| 378 | pci_release_region(pdev, 2); | ||
| 379 | |||
| 380 | pci_disable_device(pdev); | ||
| 381 | pci_set_drvdata(pdev, NULL); | ||
| 382 | |||
| 383 | framebuffer_release(info); | ||
| 384 | } | ||
| 385 | |||
| 386 | static struct pci_device_id gxfb_id_table[] = { | ||
| 387 | { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_VIDEO, | ||
| 388 | PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, | ||
| 389 | 0xff0000, 0 }, | ||
| 390 | { 0, } | ||
| 391 | }; | ||
| 392 | |||
| 393 | MODULE_DEVICE_TABLE(pci, gxfb_id_table); | ||
| 394 | |||
| 395 | static struct pci_driver gxfb_driver = { | ||
| 396 | .name = "gxfb", | ||
| 397 | .id_table = gxfb_id_table, | ||
| 398 | .probe = gxfb_probe, | ||
| 399 | .remove = gxfb_remove, | ||
| 400 | }; | ||
| 401 | |||
| 402 | static int __init gxfb_init(void) | ||
| 403 | { | ||
| 404 | #ifndef MODULE | ||
| 405 | if (fb_get_options("gxfb", NULL)) | ||
| 406 | return -ENODEV; | ||
| 407 | #endif | ||
| 408 | return pci_register_driver(&gxfb_driver); | ||
| 409 | } | ||
| 410 | |||
| 411 | static void __exit gxfb_cleanup(void) | ||
| 412 | { | ||
| 413 | pci_unregister_driver(&gxfb_driver); | ||
| 414 | } | ||
| 415 | |||
| 416 | module_init(gxfb_init); | ||
| 417 | module_exit(gxfb_cleanup); | ||
| 418 | |||
| 419 | module_param_string(mode, mode_option, sizeof(mode_option), 0444); | ||
| 420 | MODULE_PARM_DESC(mode, "video mode (<x>x<y>[-<bpp>][@<refr>])"); | ||
| 421 | |||
| 422 | MODULE_DESCRIPTION("Framebuffer driver for the AMD Geode GX"); | ||
| 423 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/geode/video_gx.c b/drivers/video/geode/video_gx.c new file mode 100644 index 000000000000..2b2a7880ea75 --- /dev/null +++ b/drivers/video/geode/video_gx.c | |||
| @@ -0,0 +1,262 @@ | |||
| 1 | /* | ||
| 2 | * Geode GX video processor device. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2006 Arcom Control Systems Ltd. | ||
| 5 | * | ||
| 6 | * Portions from AMD's original 2.4 driver: | ||
| 7 | * Copyright (C) 2004 Advanced Micro Devices, Inc. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms of the GNU General Public License as published by the | ||
| 11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 12 | * option) any later version. | ||
| 13 | */ | ||
| 14 | #include <linux/fb.h> | ||
| 15 | #include <linux/delay.h> | ||
| 16 | #include <asm/io.h> | ||
| 17 | #include <asm/delay.h> | ||
| 18 | #include <asm/msr.h> | ||
| 19 | |||
| 20 | #include "geodefb.h" | ||
| 21 | #include "video_gx.h" | ||
| 22 | |||
| 23 | |||
| 24 | /* | ||
| 25 | * Tables of register settings for various DOTCLKs. | ||
| 26 | */ | ||
| 27 | struct gx_pll_entry { | ||
| 28 | long pixclock; /* ps */ | ||
| 29 | u32 sys_rstpll_bits; | ||
| 30 | u32 dotpll_value; | ||
| 31 | }; | ||
| 32 | |||
| 33 | #define POSTDIV3 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3) | ||
| 34 | #define PREMULT2 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPREMULT2) | ||
| 35 | #define PREDIV2 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3) | ||
| 36 | |||
| 37 | static const struct gx_pll_entry gx_pll_table_48MHz[] = { | ||
| 38 | { 40123, POSTDIV3, 0x00000BF2 }, /* 24.9230 */ | ||
| 39 | { 39721, 0, 0x00000037 }, /* 25.1750 */ | ||
| 40 | { 35308, POSTDIV3|PREMULT2, 0x00000B1A }, /* 28.3220 */ | ||
| 41 | { 31746, POSTDIV3, 0x000002D2 }, /* 31.5000 */ | ||
| 42 | { 27777, POSTDIV3|PREMULT2, 0x00000FE2 }, /* 36.0000 */ | ||
| 43 | { 26666, POSTDIV3, 0x0000057A }, /* 37.5000 */ | ||
| 44 | { 25000, POSTDIV3, 0x0000030A }, /* 40.0000 */ | ||
| 45 | { 22271, 0, 0x00000063 }, /* 44.9000 */ | ||
| 46 | { 20202, 0, 0x0000054B }, /* 49.5000 */ | ||
| 47 | { 20000, 0, 0x0000026E }, /* 50.0000 */ | ||
| 48 | { 19860, PREMULT2, 0x00000037 }, /* 50.3500 */ | ||
| 49 | { 18518, POSTDIV3|PREMULT2, 0x00000B0D }, /* 54.0000 */ | ||
| 50 | { 17777, 0, 0x00000577 }, /* 56.2500 */ | ||
| 51 | { 17733, 0, 0x000007F7 }, /* 56.3916 */ | ||
| 52 | { 17653, 0, 0x0000057B }, /* 56.6444 */ | ||
| 53 | { 16949, PREMULT2, 0x00000707 }, /* 59.0000 */ | ||
| 54 | { 15873, POSTDIV3|PREMULT2, 0x00000B39 }, /* 63.0000 */ | ||
| 55 | { 15384, POSTDIV3|PREMULT2, 0x00000B45 }, /* 65.0000 */ | ||
| 56 | { 14814, POSTDIV3|PREMULT2, 0x00000FC1 }, /* 67.5000 */ | ||
| 57 | { 14124, POSTDIV3, 0x00000561 }, /* 70.8000 */ | ||
| 58 | { 13888, POSTDIV3, 0x000007E1 }, /* 72.0000 */ | ||
| 59 | { 13426, PREMULT2, 0x00000F4A }, /* 74.4810 */ | ||
| 60 | { 13333, 0, 0x00000052 }, /* 75.0000 */ | ||
| 61 | { 12698, 0, 0x00000056 }, /* 78.7500 */ | ||
| 62 | { 12500, POSTDIV3|PREMULT2, 0x00000709 }, /* 80.0000 */ | ||
| 63 | { 11135, PREMULT2, 0x00000262 }, /* 89.8000 */ | ||
| 64 | { 10582, 0, 0x000002D2 }, /* 94.5000 */ | ||
| 65 | { 10101, PREMULT2, 0x00000B4A }, /* 99.0000 */ | ||
| 66 | { 10000, PREMULT2, 0x00000036 }, /* 100.0000 */ | ||
| 67 | { 9259, 0, 0x000007E2 }, /* 108.0000 */ | ||
| 68 | { 8888, 0, 0x000007F6 }, /* 112.5000 */ | ||
| 69 | { 7692, POSTDIV3|PREMULT2, 0x00000FB0 }, /* 130.0000 */ | ||
| 70 | { 7407, POSTDIV3|PREMULT2, 0x00000B50 }, /* 135.0000 */ | ||
| 71 | { 6349, 0, 0x00000055 }, /* 157.5000 */ | ||
| 72 | { 6172, 0, 0x000009C1 }, /* 162.0000 */ | ||
| 73 | { 5787, PREMULT2, 0x0000002D }, /* 172.798 */ | ||
| 74 | { 5698, 0, 0x000002C1 }, /* 175.5000 */ | ||
| 75 | { 5291, 0, 0x000002D1 }, /* 189.0000 */ | ||
| 76 | { 4938, 0, 0x00000551 }, /* 202.5000 */ | ||
| 77 | { 4357, 0, 0x0000057D }, /* 229.5000 */ | ||
| 78 | }; | ||
| 79 | |||
| 80 | static const struct gx_pll_entry gx_pll_table_14MHz[] = { | ||
| 81 | { 39721, 0, 0x00000037 }, /* 25.1750 */ | ||
| 82 | { 35308, 0, 0x00000B7B }, /* 28.3220 */ | ||
| 83 | { 31746, 0, 0x000004D3 }, /* 31.5000 */ | ||
| 84 | { 27777, 0, 0x00000BE3 }, /* 36.0000 */ | ||
| 85 | { 26666, 0, 0x0000074F }, /* 37.5000 */ | ||
| 86 | { 25000, 0, 0x0000050B }, /* 40.0000 */ | ||
| 87 | { 22271, 0, 0x00000063 }, /* 44.9000 */ | ||
| 88 | { 20202, 0, 0x0000054B }, /* 49.5000 */ | ||
| 89 | { 20000, 0, 0x0000026E }, /* 50.0000 */ | ||
| 90 | { 19860, 0, 0x000007C3 }, /* 50.3500 */ | ||
| 91 | { 18518, 0, 0x000007E3 }, /* 54.0000 */ | ||
| 92 | { 17777, 0, 0x00000577 }, /* 56.2500 */ | ||
| 93 | { 17733, 0, 0x000002FB }, /* 56.3916 */ | ||
| 94 | { 17653, 0, 0x0000057B }, /* 56.6444 */ | ||
| 95 | { 16949, 0, 0x0000058B }, /* 59.0000 */ | ||
| 96 | { 15873, 0, 0x0000095E }, /* 63.0000 */ | ||
| 97 | { 15384, 0, 0x0000096A }, /* 65.0000 */ | ||
| 98 | { 14814, 0, 0x00000BC2 }, /* 67.5000 */ | ||
| 99 | { 14124, 0, 0x0000098A }, /* 70.8000 */ | ||
| 100 | { 13888, 0, 0x00000BE2 }, /* 72.0000 */ | ||
| 101 | { 13333, 0, 0x00000052 }, /* 75.0000 */ | ||
| 102 | { 12698, 0, 0x00000056 }, /* 78.7500 */ | ||
| 103 | { 12500, 0, 0x0000050A }, /* 80.0000 */ | ||
| 104 | { 11135, 0, 0x0000078E }, /* 89.8000 */ | ||
| 105 | { 10582, 0, 0x000002D2 }, /* 94.5000 */ | ||
| 106 | { 10101, 0, 0x000011F6 }, /* 99.0000 */ | ||
| 107 | { 10000, 0, 0x0000054E }, /* 100.0000 */ | ||
| 108 | { 9259, 0, 0x000007E2 }, /* 108.0000 */ | ||
| 109 | { 8888, 0, 0x000002FA }, /* 112.5000 */ | ||
| 110 | { 7692, 0, 0x00000BB1 }, /* 130.0000 */ | ||
| 111 | { 7407, 0, 0x00000975 }, /* 135.0000 */ | ||
| 112 | { 6349, 0, 0x00000055 }, /* 157.5000 */ | ||
| 113 | { 6172, 0, 0x000009C1 }, /* 162.0000 */ | ||
| 114 | { 5698, 0, 0x000002C1 }, /* 175.5000 */ | ||
| 115 | { 5291, 0, 0x00000539 }, /* 189.0000 */ | ||
| 116 | { 4938, 0, 0x00000551 }, /* 202.5000 */ | ||
| 117 | { 4357, 0, 0x0000057D }, /* 229.5000 */ | ||
| 118 | }; | ||
| 119 | |||
| 120 | static void gx_set_dclk_frequency(struct fb_info *info) | ||
| 121 | { | ||
| 122 | const struct gx_pll_entry *pll_table; | ||
| 123 | int pll_table_len; | ||
| 124 | int i, best_i; | ||
| 125 | long min, diff; | ||
| 126 | u64 dotpll, sys_rstpll; | ||
| 127 | int timeout = 1000; | ||
| 128 | |||
| 129 | /* Rev. 1 Geode GXs use a 14 MHz reference clock instead of 48 MHz. */ | ||
| 130 | if (cpu_data->x86_mask == 1) { | ||
| 131 | pll_table = gx_pll_table_14MHz; | ||
| 132 | pll_table_len = ARRAY_SIZE(gx_pll_table_14MHz); | ||
| 133 | } else { | ||
| 134 | pll_table = gx_pll_table_48MHz; | ||
| 135 | pll_table_len = ARRAY_SIZE(gx_pll_table_48MHz); | ||
| 136 | } | ||
| 137 | |||
| 138 | /* Search the table for the closest pixclock. */ | ||
| 139 | best_i = 0; | ||
| 140 | min = abs(pll_table[0].pixclock - info->var.pixclock); | ||
| 141 | for (i = 1; i < pll_table_len; i++) { | ||
| 142 | diff = abs(pll_table[i].pixclock - info->var.pixclock); | ||
| 143 | if (diff < min) { | ||
| 144 | min = diff; | ||
| 145 | best_i = i; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | rdmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll); | ||
| 150 | rdmsrl(MSR_GLCP_DOTPLL, dotpll); | ||
| 151 | |||
| 152 | /* Program new M, N and P. */ | ||
| 153 | dotpll &= 0x00000000ffffffffull; | ||
| 154 | dotpll |= (u64)pll_table[best_i].dotpll_value << 32; | ||
| 155 | dotpll |= MSR_GLCP_DOTPLL_DOTRESET; | ||
| 156 | dotpll &= ~MSR_GLCP_DOTPLL_BYPASS; | ||
| 157 | |||
| 158 | wrmsrl(MSR_GLCP_DOTPLL, dotpll); | ||
| 159 | |||
| 160 | /* Program dividers. */ | ||
| 161 | sys_rstpll &= ~( MSR_GLCP_SYS_RSTPLL_DOTPREDIV2 | ||
| 162 | | MSR_GLCP_SYS_RSTPLL_DOTPREMULT2 | ||
| 163 | | MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 ); | ||
| 164 | sys_rstpll |= pll_table[best_i].sys_rstpll_bits; | ||
| 165 | |||
| 166 | wrmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll); | ||
| 167 | |||
| 168 | /* Clear reset bit to start PLL. */ | ||
| 169 | dotpll &= ~(MSR_GLCP_DOTPLL_DOTRESET); | ||
| 170 | wrmsrl(MSR_GLCP_DOTPLL, dotpll); | ||
| 171 | |||
| 172 | /* Wait for LOCK bit. */ | ||
| 173 | do { | ||
| 174 | rdmsrl(MSR_GLCP_DOTPLL, dotpll); | ||
| 175 | } while (timeout-- && !(dotpll & MSR_GLCP_DOTPLL_LOCK)); | ||
| 176 | } | ||
| 177 | |||
| 178 | static void gx_configure_display(struct fb_info *info) | ||
| 179 | { | ||
| 180 | struct geodefb_par *par = info->par; | ||
| 181 | u32 dcfg, fp_pm; | ||
| 182 | |||
| 183 | dcfg = readl(par->vid_regs + GX_DCFG); | ||
| 184 | |||
| 185 | /* Clear bits from existing mode. */ | ||
| 186 | dcfg &= ~(GX_DCFG_CRT_SYNC_SKW_MASK | ||
| 187 | | GX_DCFG_CRT_HSYNC_POL | GX_DCFG_CRT_VSYNC_POL | ||
| 188 | | GX_DCFG_VSYNC_EN | GX_DCFG_HSYNC_EN); | ||
| 189 | |||
| 190 | /* Set default sync skew. */ | ||
| 191 | dcfg |= GX_DCFG_CRT_SYNC_SKW_DFLT; | ||
| 192 | |||
| 193 | /* Enable hsync and vsync. */ | ||
| 194 | dcfg |= GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN; | ||
| 195 | |||
| 196 | /* Sync polarities. */ | ||
| 197 | if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) | ||
| 198 | dcfg |= GX_DCFG_CRT_HSYNC_POL; | ||
| 199 | if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) | ||
| 200 | dcfg |= GX_DCFG_CRT_VSYNC_POL; | ||
| 201 | |||
| 202 | writel(dcfg, par->vid_regs + GX_DCFG); | ||
| 203 | |||
| 204 | /* Power on flat panel. */ | ||
| 205 | fp_pm = readl(par->vid_regs + GX_FP_PM); | ||
| 206 | fp_pm |= GX_FP_PM_P; | ||
| 207 | writel(fp_pm, par->vid_regs + GX_FP_PM); | ||
| 208 | } | ||
| 209 | |||
| 210 | static int gx_blank_display(struct fb_info *info, int blank_mode) | ||
| 211 | { | ||
| 212 | struct geodefb_par *par = info->par; | ||
| 213 | u32 dcfg, fp_pm; | ||
| 214 | int blank, hsync, vsync; | ||
| 215 | |||
| 216 | /* CRT power saving modes. */ | ||
| 217 | switch (blank_mode) { | ||
| 218 | case FB_BLANK_UNBLANK: | ||
| 219 | blank = 0; hsync = 1; vsync = 1; | ||
| 220 | break; | ||
| 221 | case FB_BLANK_NORMAL: | ||
| 222 | blank = 1; hsync = 1; vsync = 1; | ||
| 223 | break; | ||
| 224 | case FB_BLANK_VSYNC_SUSPEND: | ||
| 225 | blank = 1; hsync = 1; vsync = 0; | ||
| 226 | break; | ||
| 227 | case FB_BLANK_HSYNC_SUSPEND: | ||
| 228 | blank = 1; hsync = 0; vsync = 1; | ||
| 229 | break; | ||
| 230 | case FB_BLANK_POWERDOWN: | ||
| 231 | blank = 1; hsync = 0; vsync = 0; | ||
| 232 | break; | ||
| 233 | default: | ||
| 234 | return -EINVAL; | ||
| 235 | } | ||
| 236 | dcfg = readl(par->vid_regs + GX_DCFG); | ||
| 237 | dcfg &= ~(GX_DCFG_DAC_BL_EN | ||
| 238 | | GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN); | ||
| 239 | if (!blank) | ||
| 240 | dcfg |= GX_DCFG_DAC_BL_EN; | ||
| 241 | if (hsync) | ||
| 242 | dcfg |= GX_DCFG_HSYNC_EN; | ||
| 243 | if (vsync) | ||
| 244 | dcfg |= GX_DCFG_VSYNC_EN; | ||
| 245 | writel(dcfg, par->vid_regs + GX_DCFG); | ||
| 246 | |||
| 247 | /* Power on/off flat panel. */ | ||
| 248 | fp_pm = readl(par->vid_regs + GX_FP_PM); | ||
| 249 | if (blank_mode == FB_BLANK_POWERDOWN) | ||
| 250 | fp_pm &= ~GX_FP_PM_P; | ||
| 251 | else | ||
| 252 | fp_pm |= GX_FP_PM_P; | ||
| 253 | writel(fp_pm, par->vid_regs + GX_FP_PM); | ||
| 254 | |||
| 255 | return 0; | ||
| 256 | } | ||
| 257 | |||
| 258 | struct geode_vid_ops gx_vid_ops = { | ||
| 259 | .set_dclk = gx_set_dclk_frequency, | ||
| 260 | .configure_display = gx_configure_display, | ||
| 261 | .blank_display = gx_blank_display, | ||
| 262 | }; | ||
diff --git a/drivers/video/geode/video_gx.h b/drivers/video/geode/video_gx.h new file mode 100644 index 000000000000..2d9211f3ed84 --- /dev/null +++ b/drivers/video/geode/video_gx.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | /* | ||
| 2 | * Geode GX video device | ||
| 3 | * | ||
| 4 | * Copyright (C) 2006 Arcom Control Systems Ltd. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | */ | ||
| 11 | #ifndef __VIDEO_GX_H__ | ||
| 12 | #define __VIDEO_GX_H__ | ||
| 13 | |||
| 14 | extern struct geode_vid_ops gx_vid_ops; | ||
| 15 | |||
| 16 | /* Geode GX video processor registers */ | ||
| 17 | |||
| 18 | #define GX_DCFG 0x0008 | ||
| 19 | # define GX_DCFG_CRT_EN 0x00000001 | ||
| 20 | # define GX_DCFG_HSYNC_EN 0x00000002 | ||
| 21 | # define GX_DCFG_VSYNC_EN 0x00000004 | ||
| 22 | # define GX_DCFG_DAC_BL_EN 0x00000008 | ||
| 23 | # define GX_DCFG_CRT_HSYNC_POL 0x00000100 | ||
| 24 | # define GX_DCFG_CRT_VSYNC_POL 0x00000200 | ||
| 25 | # define GX_DCFG_CRT_SYNC_SKW_MASK 0x0001C000 | ||
| 26 | # define GX_DCFG_CRT_SYNC_SKW_DFLT 0x00010000 | ||
| 27 | # define GX_DCFG_VG_CK 0x00100000 | ||
| 28 | # define GX_DCFG_GV_GAM 0x00200000 | ||
| 29 | # define GX_DCFG_DAC_VREF 0x04000000 | ||
| 30 | |||
| 31 | /* Geode GX flat panel display control registers */ | ||
| 32 | #define GX_FP_PM 0x410 | ||
| 33 | # define GX_FP_PM_P 0x01000000 | ||
| 34 | |||
| 35 | /* Geode GX clock control MSRs */ | ||
| 36 | |||
| 37 | #define MSR_GLCP_SYS_RSTPLL 0x4c000014 | ||
| 38 | # define MSR_GLCP_SYS_RSTPLL_DOTPREDIV2 (0x0000000000000002ull) | ||
| 39 | # define MSR_GLCP_SYS_RSTPLL_DOTPREMULT2 (0x0000000000000004ull) | ||
| 40 | # define MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 (0x0000000000000008ull) | ||
| 41 | |||
| 42 | #define MSR_GLCP_DOTPLL 0x4c000015 | ||
| 43 | # define MSR_GLCP_DOTPLL_DOTRESET (0x0000000000000001ull) | ||
| 44 | # define MSR_GLCP_DOTPLL_BYPASS (0x0000000000008000ull) | ||
| 45 | # define MSR_GLCP_DOTPLL_LOCK (0x0000000002000000ull) | ||
| 46 | |||
| 47 | #endif /* !__VIDEO_GX_H__ */ | ||
diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c index e3c8b5f1ca76..3fe3ae1aff12 100644 --- a/drivers/video/i810/i810-i2c.c +++ b/drivers/video/i810/i810-i2c.c | |||
| @@ -210,8 +210,7 @@ int i810_probe_i2c_connector(struct fb_info *info, u8 **out_edid, int conn) | |||
| 210 | } | 210 | } |
| 211 | } | 211 | } |
| 212 | 212 | ||
| 213 | if (out_edid) | 213 | *out_edid = edid; |
| 214 | *out_edid = edid; | ||
| 215 | 214 | ||
| 216 | return (edid) ? 0 : 1; | 215 | return (edid) ? 0 : 1; |
| 217 | } | 216 | } |
diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c index 7db42542eb19..f73c642b50c2 100644 --- a/drivers/video/imsttfb.c +++ b/drivers/video/imsttfb.c | |||
| @@ -440,9 +440,9 @@ getclkMHz(struct imstt_par *par) | |||
| 440 | static void | 440 | static void |
| 441 | setclkMHz(struct imstt_par *par, __u32 MHz) | 441 | setclkMHz(struct imstt_par *par, __u32 MHz) |
| 442 | { | 442 | { |
| 443 | __u32 clk_m, clk_n, clk_p, x, stage, spilled; | 443 | __u32 clk_m, clk_n, x, stage, spilled; |
| 444 | 444 | ||
| 445 | clk_m = clk_n = clk_p = 0; | 445 | clk_m = clk_n = 0; |
| 446 | stage = spilled = 0; | 446 | stage = spilled = 0; |
| 447 | for (;;) { | 447 | for (;;) { |
| 448 | switch (stage) { | 448 | switch (stage) { |
| @@ -453,7 +453,7 @@ setclkMHz(struct imstt_par *par, __u32 MHz) | |||
| 453 | clk_n++; | 453 | clk_n++; |
| 454 | break; | 454 | break; |
| 455 | } | 455 | } |
| 456 | x = 20 * (clk_m + 1) / ((clk_n + 1) * (clk_p ? 2 * clk_p : 1)); | 456 | x = 20 * (clk_m + 1) / (clk_n + 1); |
| 457 | if (x == MHz) | 457 | if (x == MHz) |
| 458 | break; | 458 | break; |
| 459 | if (x > MHz) { | 459 | if (x > MHz) { |
| @@ -466,7 +466,7 @@ setclkMHz(struct imstt_par *par, __u32 MHz) | |||
| 466 | 466 | ||
| 467 | par->init.pclk_m = clk_m; | 467 | par->init.pclk_m = clk_m; |
| 468 | par->init.pclk_n = clk_n; | 468 | par->init.pclk_n = clk_n; |
| 469 | par->init.pclk_p = clk_p; | 469 | par->init.pclk_p = 0; |
| 470 | } | 470 | } |
| 471 | 471 | ||
| 472 | static struct imstt_regvals * | 472 | static struct imstt_regvals * |
| @@ -1372,18 +1372,24 @@ init_imstt(struct fb_info *info) | |||
| 1372 | write_reg_le32(par->dc_regs, STGCTL, tmp & ~0x1); | 1372 | write_reg_le32(par->dc_regs, STGCTL, tmp & ~0x1); |
| 1373 | write_reg_le32(par->dc_regs, SSR, 0); | 1373 | write_reg_le32(par->dc_regs, SSR, 0); |
| 1374 | 1374 | ||
| 1375 | /* set default values for DAC registers */ | 1375 | /* set default values for DAC registers */ |
| 1376 | if (par->ramdac == IBM) { | 1376 | if (par->ramdac == IBM) { |
| 1377 | par->cmap_regs[PPMASK] = 0xff; eieio(); | 1377 | par->cmap_regs[PPMASK] = 0xff; |
| 1378 | par->cmap_regs[PIDXHI] = 0; eieio(); | 1378 | eieio(); |
| 1379 | for (i = 0; i < sizeof(ibm_initregs) / sizeof(*ibm_initregs); i++) { | 1379 | par->cmap_regs[PIDXHI] = 0; |
| 1380 | par->cmap_regs[PIDXLO] = ibm_initregs[i].addr; eieio(); | 1380 | eieio(); |
| 1381 | par->cmap_regs[PIDXDATA] = ibm_initregs[i].value; eieio(); | 1381 | for (i = 0; i < ARRAY_SIZE(ibm_initregs); i++) { |
| 1382 | par->cmap_regs[PIDXLO] = ibm_initregs[i].addr; | ||
| 1383 | eieio(); | ||
| 1384 | par->cmap_regs[PIDXDATA] = ibm_initregs[i].value; | ||
| 1385 | eieio(); | ||
| 1382 | } | 1386 | } |
| 1383 | } else { | 1387 | } else { |
| 1384 | for (i = 0; i < sizeof(tvp_initregs) / sizeof(*tvp_initregs); i++) { | 1388 | for (i = 0; i < ARRAY_SIZE(tvp_initregs); i++) { |
| 1385 | par->cmap_regs[TVPADDRW] = tvp_initregs[i].addr; eieio(); | 1389 | par->cmap_regs[TVPADDRW] = tvp_initregs[i].addr; |
| 1386 | par->cmap_regs[TVPIDATA] = tvp_initregs[i].value; eieio(); | 1390 | eieio(); |
| 1391 | par->cmap_regs[TVPIDATA] = tvp_initregs[i].value; | ||
| 1392 | eieio(); | ||
| 1387 | } | 1393 | } |
| 1388 | } | 1394 | } |
| 1389 | 1395 | ||
diff --git a/drivers/video/macmodes.c b/drivers/video/macmodes.c index 2fc71081f7e7..c0385c6f7db5 100644 --- a/drivers/video/macmodes.c +++ b/drivers/video/macmodes.c | |||
| @@ -380,7 +380,7 @@ int __init mac_find_mode(struct fb_var_screeninfo *var, struct fb_info *info, | |||
| 380 | if (mode_option && !strncmp(mode_option, "mac", 3)) { | 380 | if (mode_option && !strncmp(mode_option, "mac", 3)) { |
| 381 | mode_option += 3; | 381 | mode_option += 3; |
| 382 | db = mac_modedb; | 382 | db = mac_modedb; |
| 383 | dbsize = sizeof(mac_modedb)/sizeof(*mac_modedb); | 383 | dbsize = ARRAY_SIZE(mac_modedb); |
| 384 | } | 384 | } |
| 385 | return fb_find_mode(var, info, mode_option, db, dbsize, | 385 | return fb_find_mode(var, info, mode_option, db, dbsize, |
| 386 | &mac_modedb[DEFAULT_MODEDB_INDEX], default_bpp); | 386 | &mac_modedb[DEFAULT_MODEDB_INDEX], default_bpp); |
diff --git a/drivers/video/matrox/matroxfb_g450.c b/drivers/video/matrox/matroxfb_g450.c index c122d8743dd2..4d610b405d45 100644 --- a/drivers/video/matrox/matroxfb_g450.c +++ b/drivers/video/matrox/matroxfb_g450.c | |||
| @@ -59,7 +59,7 @@ static const struct mctl g450_controls[] = | |||
| 59 | }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, | 59 | }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, |
| 60 | }; | 60 | }; |
| 61 | 61 | ||
| 62 | #define G450CTRLS (sizeof(g450_controls)/sizeof(g450_controls[0])) | 62 | #define G450CTRLS ARRAY_SIZE(g450_controls) |
| 63 | 63 | ||
| 64 | /* Return: positive number: id found | 64 | /* Return: positive number: id found |
| 65 | -EINVAL: id not found, return failure | 65 | -EINVAL: id not found, return failure |
diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index 6019710dc298..5d29a26b8cdf 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c | |||
| @@ -89,12 +89,12 @@ static const struct mctl maven_controls[] = | |||
| 89 | }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) }, | 89 | }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) }, |
| 90 | { { V4L2_CID_GAMMA, V4L2_CTRL_TYPE_INTEGER, | 90 | { { V4L2_CID_GAMMA, V4L2_CTRL_TYPE_INTEGER, |
| 91 | "gamma", | 91 | "gamma", |
| 92 | 0, sizeof(maven_gamma)/sizeof(maven_gamma[0])-1, 1, 3, | 92 | 0, ARRAY_SIZE(maven_gamma) - 1, 1, 3, |
| 93 | 0, | 93 | 0, |
| 94 | }, offsetof(struct matrox_fb_info, altout.tvo_params.gamma) }, | 94 | }, offsetof(struct matrox_fb_info, altout.tvo_params.gamma) }, |
| 95 | { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN, | 95 | { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN, |
| 96 | "test output", | 96 | "test output", |
| 97 | 0, 1, 1, 0, | 97 | 0, 1, 1, 0, |
| 98 | 0, | 98 | 0, |
| 99 | }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, | 99 | }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, |
| 100 | { { MATROXFB_CID_DEFLICKER, V4L2_CTRL_TYPE_INTEGER, | 100 | { { MATROXFB_CID_DEFLICKER, V4L2_CTRL_TYPE_INTEGER, |
| @@ -105,7 +105,7 @@ static const struct mctl maven_controls[] = | |||
| 105 | 105 | ||
| 106 | }; | 106 | }; |
| 107 | 107 | ||
| 108 | #define MAVCTRLS (sizeof(maven_controls)/sizeof(maven_controls[0])) | 108 | #define MAVCTRLS ARRAY_SIZE(maven_controls) |
| 109 | 109 | ||
| 110 | /* Return: positive number: id found | 110 | /* Return: positive number: id found |
| 111 | -EINVAL: id not found, return failure | 111 | -EINVAL: id not found, return failure |
| @@ -129,7 +129,7 @@ static int get_ctrl_id(__u32 v4l2_id) { | |||
| 129 | 129 | ||
| 130 | struct maven_data { | 130 | struct maven_data { |
| 131 | struct matrox_fb_info* primary_head; | 131 | struct matrox_fb_info* primary_head; |
| 132 | struct i2c_client* client; | 132 | struct i2c_client client; |
| 133 | int version; | 133 | int version; |
| 134 | }; | 134 | }; |
| 135 | 135 | ||
| @@ -970,7 +970,7 @@ static inline int maven_compute_timming(struct maven_data* md, | |||
| 970 | 970 | ||
| 971 | static int maven_program_timming(struct maven_data* md, | 971 | static int maven_program_timming(struct maven_data* md, |
| 972 | const struct mavenregs* m) { | 972 | const struct mavenregs* m) { |
| 973 | struct i2c_client* c = md->client; | 973 | struct i2c_client* c = &md->client; |
| 974 | 974 | ||
| 975 | if (m->mode == MATROXFB_OUTPUT_MODE_MONITOR) { | 975 | if (m->mode == MATROXFB_OUTPUT_MODE_MONITOR) { |
| 976 | LR(0x80); | 976 | LR(0x80); |
| @@ -1007,7 +1007,7 @@ static int maven_program_timming(struct maven_data* md, | |||
| 1007 | } | 1007 | } |
| 1008 | 1008 | ||
| 1009 | static inline int maven_resync(struct maven_data* md) { | 1009 | static inline int maven_resync(struct maven_data* md) { |
| 1010 | struct i2c_client* c = md->client; | 1010 | struct i2c_client* c = &md->client; |
| 1011 | maven_set_reg(c, 0x95, 0x20); /* start whole thing */ | 1011 | maven_set_reg(c, 0x95, 0x20); /* start whole thing */ |
| 1012 | return 0; | 1012 | return 0; |
| 1013 | } | 1013 | } |
| @@ -1065,48 +1065,48 @@ static int maven_set_control (struct maven_data* md, | |||
| 1065 | maven_compute_bwlevel(md, &blacklevel, &whitelevel); | 1065 | maven_compute_bwlevel(md, &blacklevel, &whitelevel); |
| 1066 | blacklevel = (blacklevel >> 2) | ((blacklevel & 3) << 8); | 1066 | blacklevel = (blacklevel >> 2) | ((blacklevel & 3) << 8); |
| 1067 | whitelevel = (whitelevel >> 2) | ((whitelevel & 3) << 8); | 1067 | whitelevel = (whitelevel >> 2) | ((whitelevel & 3) << 8); |
| 1068 | maven_set_reg_pair(md->client, 0x0e, blacklevel); | 1068 | maven_set_reg_pair(&md->client, 0x0e, blacklevel); |
| 1069 | maven_set_reg_pair(md->client, 0x1e, whitelevel); | 1069 | maven_set_reg_pair(&md->client, 0x1e, whitelevel); |
| 1070 | } | 1070 | } |
| 1071 | break; | 1071 | break; |
| 1072 | case V4L2_CID_SATURATION: | 1072 | case V4L2_CID_SATURATION: |
| 1073 | { | 1073 | { |
| 1074 | maven_set_reg(md->client, 0x20, p->value); | 1074 | maven_set_reg(&md->client, 0x20, p->value); |
| 1075 | maven_set_reg(md->client, 0x22, p->value); | 1075 | maven_set_reg(&md->client, 0x22, p->value); |
| 1076 | } | 1076 | } |
| 1077 | break; | 1077 | break; |
| 1078 | case V4L2_CID_HUE: | 1078 | case V4L2_CID_HUE: |
| 1079 | { | 1079 | { |
| 1080 | maven_set_reg(md->client, 0x25, p->value); | 1080 | maven_set_reg(&md->client, 0x25, p->value); |
| 1081 | } | 1081 | } |
| 1082 | break; | 1082 | break; |
| 1083 | case V4L2_CID_GAMMA: | 1083 | case V4L2_CID_GAMMA: |
| 1084 | { | 1084 | { |
| 1085 | const struct maven_gamma* g; | 1085 | const struct maven_gamma* g; |
| 1086 | g = maven_compute_gamma(md); | 1086 | g = maven_compute_gamma(md); |
| 1087 | maven_set_reg(md->client, 0x83, g->reg83); | 1087 | maven_set_reg(&md->client, 0x83, g->reg83); |
| 1088 | maven_set_reg(md->client, 0x84, g->reg84); | 1088 | maven_set_reg(&md->client, 0x84, g->reg84); |
| 1089 | maven_set_reg(md->client, 0x85, g->reg85); | 1089 | maven_set_reg(&md->client, 0x85, g->reg85); |
| 1090 | maven_set_reg(md->client, 0x86, g->reg86); | 1090 | maven_set_reg(&md->client, 0x86, g->reg86); |
| 1091 | maven_set_reg(md->client, 0x87, g->reg87); | 1091 | maven_set_reg(&md->client, 0x87, g->reg87); |
| 1092 | maven_set_reg(md->client, 0x88, g->reg88); | 1092 | maven_set_reg(&md->client, 0x88, g->reg88); |
| 1093 | maven_set_reg(md->client, 0x89, g->reg89); | 1093 | maven_set_reg(&md->client, 0x89, g->reg89); |
| 1094 | maven_set_reg(md->client, 0x8a, g->reg8a); | 1094 | maven_set_reg(&md->client, 0x8a, g->reg8a); |
| 1095 | maven_set_reg(md->client, 0x8b, g->reg8b); | 1095 | maven_set_reg(&md->client, 0x8b, g->reg8b); |
| 1096 | } | 1096 | } |
| 1097 | break; | 1097 | break; |
| 1098 | case MATROXFB_CID_TESTOUT: | 1098 | case MATROXFB_CID_TESTOUT: |
| 1099 | { | 1099 | { |
| 1100 | unsigned char val | 1100 | unsigned char val |
| 1101 | = maven_get_reg (md->client,0x8d); | 1101 | = maven_get_reg(&md->client,0x8d); |
| 1102 | if (p->value) val |= 0x10; | 1102 | if (p->value) val |= 0x10; |
| 1103 | else val &= ~0x10; | 1103 | else val &= ~0x10; |
| 1104 | maven_set_reg (md->client, 0x8d, val); | 1104 | maven_set_reg(&md->client, 0x8d, val); |
| 1105 | } | 1105 | } |
| 1106 | break; | 1106 | break; |
| 1107 | case MATROXFB_CID_DEFLICKER: | 1107 | case MATROXFB_CID_DEFLICKER: |
| 1108 | { | 1108 | { |
| 1109 | maven_set_reg(md->client, 0x93, maven_compute_deflicker(md)); | 1109 | maven_set_reg(&md->client, 0x93, maven_compute_deflicker(md)); |
| 1110 | } | 1110 | } |
| 1111 | break; | 1111 | break; |
| 1112 | } | 1112 | } |
| @@ -1185,7 +1185,6 @@ static int maven_init_client(struct i2c_client* clnt) { | |||
| 1185 | MINFO_FROM(container_of(clnt->adapter, struct i2c_bit_adapter, adapter)->minfo); | 1185 | MINFO_FROM(container_of(clnt->adapter, struct i2c_bit_adapter, adapter)->minfo); |
| 1186 | 1186 | ||
| 1187 | md->primary_head = MINFO; | 1187 | md->primary_head = MINFO; |
| 1188 | md->client = clnt; | ||
| 1189 | down_write(&ACCESS_FBINFO(altout.lock)); | 1188 | down_write(&ACCESS_FBINFO(altout.lock)); |
| 1190 | ACCESS_FBINFO(outputs[1]).output = &maven_altout; | 1189 | ACCESS_FBINFO(outputs[1]).output = &maven_altout; |
| 1191 | ACCESS_FBINFO(outputs[1]).src = ACCESS_FBINFO(outputs[1]).default_src; | 1190 | ACCESS_FBINFO(outputs[1]).src = ACCESS_FBINFO(outputs[1]).default_src; |
| @@ -1243,19 +1242,17 @@ static int maven_detect_client(struct i2c_adapter* adapter, int address, int kin | |||
| 1243 | I2C_FUNC_SMBUS_BYTE_DATA | | 1242 | I2C_FUNC_SMBUS_BYTE_DATA | |
| 1244 | I2C_FUNC_PROTOCOL_MANGLING)) | 1243 | I2C_FUNC_PROTOCOL_MANGLING)) |
| 1245 | goto ERROR0; | 1244 | goto ERROR0; |
| 1246 | if (!(new_client = (struct i2c_client*)kmalloc(sizeof(*new_client) + sizeof(*data), | 1245 | if (!(data = kzalloc(sizeof(*data), GFP_KERNEL))) { |
| 1247 | GFP_KERNEL))) { | ||
| 1248 | err = -ENOMEM; | 1246 | err = -ENOMEM; |
| 1249 | goto ERROR0; | 1247 | goto ERROR0; |
| 1250 | } | 1248 | } |
| 1251 | memset(new_client, 0, sizeof(*new_client) + sizeof(*data)); | 1249 | new_client = &data->client; |
| 1252 | data = (struct maven_data*)(new_client + 1); | ||
| 1253 | i2c_set_clientdata(new_client, data); | 1250 | i2c_set_clientdata(new_client, data); |
| 1254 | new_client->addr = address; | 1251 | new_client->addr = address; |
| 1255 | new_client->adapter = adapter; | 1252 | new_client->adapter = adapter; |
| 1256 | new_client->driver = &maven_driver; | 1253 | new_client->driver = &maven_driver; |
| 1257 | new_client->flags = 0; | 1254 | new_client->flags = 0; |
| 1258 | strcpy(new_client->name, "maven client"); | 1255 | strlcpy(new_client->name, "maven", I2C_NAME_SIZE); |
| 1259 | if ((err = i2c_attach_client(new_client))) | 1256 | if ((err = i2c_attach_client(new_client))) |
| 1260 | goto ERROR3; | 1257 | goto ERROR3; |
| 1261 | err = maven_init_client(new_client); | 1258 | err = maven_init_client(new_client); |
| @@ -1279,12 +1276,10 @@ static int maven_attach_adapter(struct i2c_adapter* adapter) { | |||
| 1279 | static int maven_detach_client(struct i2c_client* client) { | 1276 | static int maven_detach_client(struct i2c_client* client) { |
| 1280 | int err; | 1277 | int err; |
| 1281 | 1278 | ||
| 1282 | if ((err = i2c_detach_client(client))) { | 1279 | if ((err = i2c_detach_client(client))) |
| 1283 | printk(KERN_ERR "maven: Cannot deregister client\n"); | ||
| 1284 | return err; | 1280 | return err; |
| 1285 | } | ||
| 1286 | maven_shutdown_client(client); | 1281 | maven_shutdown_client(client); |
| 1287 | kfree(client); | 1282 | kfree(i2c_get_clientdata(client)); |
| 1288 | return 0; | 1283 | return 0; |
| 1289 | } | 1284 | } |
| 1290 | 1285 | ||
| @@ -1297,20 +1292,13 @@ static struct i2c_driver maven_driver={ | |||
| 1297 | .detach_client = maven_detach_client, | 1292 | .detach_client = maven_detach_client, |
| 1298 | }; | 1293 | }; |
| 1299 | 1294 | ||
| 1300 | /* ************************** */ | 1295 | static int __init matroxfb_maven_init(void) |
| 1301 | 1296 | { | |
| 1302 | static int matroxfb_maven_init(void) { | 1297 | return i2c_add_driver(&maven_driver); |
| 1303 | int err; | ||
| 1304 | |||
| 1305 | err = i2c_add_driver(&maven_driver); | ||
| 1306 | if (err) { | ||
| 1307 | printk(KERN_ERR "maven: Maven driver failed to register (%d).\n", err); | ||
| 1308 | return err; | ||
| 1309 | } | ||
| 1310 | return 0; | ||
| 1311 | } | 1298 | } |
| 1312 | 1299 | ||
| 1313 | static void matroxfb_maven_exit(void) { | 1300 | static void __exit matroxfb_maven_exit(void) |
| 1301 | { | ||
| 1314 | i2c_del_driver(&maven_driver); | 1302 | i2c_del_driver(&maven_driver); |
| 1315 | } | 1303 | } |
| 1316 | 1304 | ||
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 1da2f84bdc25..26a1c618a205 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c | |||
| @@ -183,6 +183,10 @@ static const struct fb_videomode modedb[] = { | |||
| 183 | NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, | 183 | NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, |
| 184 | FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED | 184 | FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED |
| 185 | }, { | 185 | }, { |
| 186 | /* 1680x1050 @ 60 Hz, 65.191 kHz hsync */ | ||
| 187 | NULL, 60, 1680, 1050, 6848, 280, 104, 30, 3, 176, 6, | ||
| 188 | FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED | ||
| 189 | }, { | ||
| 186 | /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */ | 190 | /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */ |
| 187 | NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3, | 191 | NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3, |
| 188 | FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED | 192 | FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED |
| @@ -496,7 +500,7 @@ int fb_find_mode(struct fb_var_screeninfo *var, | |||
| 496 | /* Set up defaults */ | 500 | /* Set up defaults */ |
| 497 | if (!db) { | 501 | if (!db) { |
| 498 | db = modedb; | 502 | db = modedb; |
| 499 | dbsize = sizeof(modedb)/sizeof(*modedb); | 503 | dbsize = ARRAY_SIZE(modedb); |
| 500 | } | 504 | } |
| 501 | if (!default_mode) | 505 | if (!default_mode) |
| 502 | default_mode = &modedb[DEFAULT_MODEDB_INDEX]; | 506 | default_mode = &modedb[DEFAULT_MODEDB_INDEX]; |
diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c index b961d5601bd9..24b12f71d5a8 100644 --- a/drivers/video/neofb.c +++ b/drivers/video/neofb.c | |||
| @@ -165,20 +165,20 @@ static int neoFindMode(int xres, int yres, int depth) | |||
| 165 | 165 | ||
| 166 | switch (depth) { | 166 | switch (depth) { |
| 167 | case 8: | 167 | case 8: |
| 168 | size = sizeof(bios8) / sizeof(biosMode); | 168 | size = ARRAY_SIZE(bios8); |
| 169 | mode = bios8; | 169 | mode = bios8; |
| 170 | break; | 170 | break; |
| 171 | case 16: | 171 | case 16: |
| 172 | size = sizeof(bios16) / sizeof(biosMode); | 172 | size = ARRAY_SIZE(bios16); |
| 173 | mode = bios16; | 173 | mode = bios16; |
| 174 | break; | 174 | break; |
| 175 | case 24: | 175 | case 24: |
| 176 | size = sizeof(bios24) / sizeof(biosMode); | 176 | size = ARRAY_SIZE(bios24); |
| 177 | mode = bios24; | 177 | mode = bios24; |
| 178 | break; | 178 | break; |
| 179 | #ifdef NO_32BIT_SUPPORT_YET | 179 | #ifdef NO_32BIT_SUPPORT_YET |
| 180 | case 32: | 180 | case 32: |
| 181 | size = sizeof(bios32) / sizeof(biosMode); | 181 | size = ARRAY_SIZE(bios32); |
| 182 | mode = bios32; | 182 | mode = bios32; |
| 183 | break; | 183 | break; |
| 184 | #endif | 184 | #endif |
diff --git a/drivers/video/nvidia/nv_accel.c b/drivers/video/nvidia/nv_accel.c index f377a29ec97a..4aefb8f41637 100644 --- a/drivers/video/nvidia/nv_accel.c +++ b/drivers/video/nvidia/nv_accel.c | |||
| @@ -300,6 +300,9 @@ int nvidiafb_sync(struct fb_info *info) | |||
| 300 | { | 300 | { |
| 301 | struct nvidia_par *par = info->par; | 301 | struct nvidia_par *par = info->par; |
| 302 | 302 | ||
| 303 | if (info->state != FBINFO_STATE_RUNNING) | ||
| 304 | return 0; | ||
| 305 | |||
| 303 | if (!par->lockup) | 306 | if (!par->lockup) |
| 304 | NVFlush(par); | 307 | NVFlush(par); |
| 305 | 308 | ||
| @@ -313,6 +316,9 @@ void nvidiafb_copyarea(struct fb_info *info, const struct fb_copyarea *region) | |||
| 313 | { | 316 | { |
| 314 | struct nvidia_par *par = info->par; | 317 | struct nvidia_par *par = info->par; |
| 315 | 318 | ||
| 319 | if (info->state != FBINFO_STATE_RUNNING) | ||
| 320 | return; | ||
| 321 | |||
| 316 | if (par->lockup) | 322 | if (par->lockup) |
| 317 | return cfb_copyarea(info, region); | 323 | return cfb_copyarea(info, region); |
| 318 | 324 | ||
| @@ -329,6 +335,9 @@ void nvidiafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | |||
| 329 | struct nvidia_par *par = info->par; | 335 | struct nvidia_par *par = info->par; |
| 330 | u32 color; | 336 | u32 color; |
| 331 | 337 | ||
| 338 | if (info->state != FBINFO_STATE_RUNNING) | ||
| 339 | return; | ||
| 340 | |||
| 332 | if (par->lockup) | 341 | if (par->lockup) |
| 333 | return cfb_fillrect(info, rect); | 342 | return cfb_fillrect(info, rect); |
| 334 | 343 | ||
| @@ -412,6 +421,9 @@ void nvidiafb_imageblit(struct fb_info *info, const struct fb_image *image) | |||
| 412 | { | 421 | { |
| 413 | struct nvidia_par *par = info->par; | 422 | struct nvidia_par *par = info->par; |
| 414 | 423 | ||
| 424 | if (info->state != FBINFO_STATE_RUNNING) | ||
| 425 | return; | ||
| 426 | |||
| 415 | if (image->depth == 1 && !par->lockup) | 427 | if (image->depth == 1 && !par->lockup) |
| 416 | nvidiafb_mono_color_expand(info, image); | 428 | nvidiafb_mono_color_expand(info, image); |
| 417 | else | 429 | else |
diff --git a/drivers/video/nvidia/nv_i2c.c b/drivers/video/nvidia/nv_i2c.c index bd9eca05e146..1edb1c432b75 100644 --- a/drivers/video/nvidia/nv_i2c.c +++ b/drivers/video/nvidia/nv_i2c.c | |||
| @@ -218,8 +218,7 @@ int nvidia_probe_i2c_connector(struct fb_info *info, int conn, u8 **out_edid) | |||
| 218 | } | 218 | } |
| 219 | } | 219 | } |
| 220 | 220 | ||
| 221 | if (out_edid) | 221 | *out_edid = edid; |
| 222 | *out_edid = edid; | ||
| 223 | 222 | ||
| 224 | return (edid) ? 0 : 1; | 223 | return (edid) ? 0 : 1; |
| 225 | } | 224 | } |
diff --git a/drivers/video/nvidia/nv_type.h b/drivers/video/nvidia/nv_type.h index e4a5b1da71c4..acdc26693402 100644 --- a/drivers/video/nvidia/nv_type.h +++ b/drivers/video/nvidia/nv_type.h | |||
| @@ -129,6 +129,7 @@ struct nvidia_par { | |||
| 129 | int fpHeight; | 129 | int fpHeight; |
| 130 | int PanelTweak; | 130 | int PanelTweak; |
| 131 | int paneltweak; | 131 | int paneltweak; |
| 132 | int pm_state; | ||
| 132 | u32 crtcSync_read; | 133 | u32 crtcSync_read; |
| 133 | u32 fpSyncs; | 134 | u32 fpSyncs; |
| 134 | u32 dmaPut; | 135 | u32 dmaPut; |
diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index a7c4e5e8ead6..6d3e4890cb43 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/fb.h> | 21 | #include <linux/fb.h> |
| 22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
| 23 | #include <linux/pci.h> | 23 | #include <linux/pci.h> |
| 24 | #include <linux/console.h> | ||
| 24 | #ifdef CONFIG_MTRR | 25 | #ifdef CONFIG_MTRR |
| 25 | #include <asm/mtrr.h> | 26 | #include <asm/mtrr.h> |
| 26 | #endif | 27 | #endif |
| @@ -296,6 +297,8 @@ static struct pci_device_id nvidiafb_pci_tbl[] = { | |||
| 296 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | 297 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
| 297 | {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT, | 298 | {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT, |
| 298 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | 299 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
| 300 | {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280, | ||
| 301 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | ||
| 299 | {PCI_VENDOR_ID_NVIDIA, 0x0252, | 302 | {PCI_VENDOR_ID_NVIDIA, 0x0252, |
| 300 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | 303 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
| 301 | {PCI_VENDOR_ID_NVIDIA, 0x0313, | 304 | {PCI_VENDOR_ID_NVIDIA, 0x0313, |
| @@ -615,6 +618,30 @@ static int nvidia_panel_tweak(struct nvidia_par *par, | |||
| 615 | return tweak; | 618 | return tweak; |
| 616 | } | 619 | } |
| 617 | 620 | ||
| 621 | static void nvidia_vga_protect(struct nvidia_par *par, int on) | ||
| 622 | { | ||
| 623 | unsigned char tmp; | ||
| 624 | |||
| 625 | if (on) { | ||
| 626 | /* | ||
| 627 | * Turn off screen and disable sequencer. | ||
| 628 | */ | ||
| 629 | tmp = NVReadSeq(par, 0x01); | ||
| 630 | |||
| 631 | NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */ | ||
| 632 | NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */ | ||
| 633 | } else { | ||
| 634 | /* | ||
| 635 | * Reenable sequencer, then turn on screen. | ||
| 636 | */ | ||
| 637 | |||
| 638 | tmp = NVReadSeq(par, 0x01); | ||
| 639 | |||
| 640 | NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */ | ||
| 641 | NVWriteSeq(par, 0x00, 0x03); /* End Reset */ | ||
| 642 | } | ||
| 643 | } | ||
| 644 | |||
| 618 | static void nvidia_save_vga(struct nvidia_par *par, | 645 | static void nvidia_save_vga(struct nvidia_par *par, |
| 619 | struct _riva_hw_state *state) | 646 | struct _riva_hw_state *state) |
| 620 | { | 647 | { |
| @@ -643,9 +670,9 @@ static void nvidia_save_vga(struct nvidia_par *par, | |||
| 643 | 670 | ||
| 644 | #undef DUMP_REG | 671 | #undef DUMP_REG |
| 645 | 672 | ||
| 646 | static void nvidia_write_regs(struct nvidia_par *par) | 673 | static void nvidia_write_regs(struct nvidia_par *par, |
| 674 | struct _riva_hw_state *state) | ||
| 647 | { | 675 | { |
| 648 | struct _riva_hw_state *state = &par->ModeReg; | ||
| 649 | int i; | 676 | int i; |
| 650 | 677 | ||
| 651 | NVTRACE_ENTER(); | 678 | NVTRACE_ENTER(); |
| @@ -694,32 +721,6 @@ static void nvidia_write_regs(struct nvidia_par *par) | |||
| 694 | NVTRACE_LEAVE(); | 721 | NVTRACE_LEAVE(); |
| 695 | } | 722 | } |
| 696 | 723 | ||
| 697 | static void nvidia_vga_protect(struct nvidia_par *par, int on) | ||
| 698 | { | ||
| 699 | unsigned char tmp; | ||
| 700 | |||
| 701 | if (on) { | ||
| 702 | /* | ||
| 703 | * Turn off screen and disable sequencer. | ||
| 704 | */ | ||
| 705 | tmp = NVReadSeq(par, 0x01); | ||
| 706 | |||
| 707 | NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */ | ||
| 708 | NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */ | ||
| 709 | } else { | ||
| 710 | /* | ||
| 711 | * Reenable sequencer, then turn on screen. | ||
| 712 | */ | ||
| 713 | |||
| 714 | tmp = NVReadSeq(par, 0x01); | ||
| 715 | |||
| 716 | NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */ | ||
| 717 | NVWriteSeq(par, 0x00, 0x03); /* End Reset */ | ||
| 718 | } | ||
| 719 | } | ||
| 720 | |||
| 721 | |||
| 722 | |||
| 723 | static int nvidia_calc_regs(struct fb_info *info) | 724 | static int nvidia_calc_regs(struct fb_info *info) |
| 724 | { | 725 | { |
| 725 | struct nvidia_par *par = info->par; | 726 | struct nvidia_par *par = info->par; |
| @@ -1068,7 +1069,8 @@ static int nvidiafb_set_par(struct fb_info *info) | |||
| 1068 | 1069 | ||
| 1069 | nvidia_vga_protect(par, 1); | 1070 | nvidia_vga_protect(par, 1); |
| 1070 | 1071 | ||
| 1071 | nvidia_write_regs(par); | 1072 | nvidia_write_regs(par, &par->ModeReg); |
| 1073 | NVSetStartAddress(par, 0); | ||
| 1072 | 1074 | ||
| 1073 | #if defined (__BIG_ENDIAN) | 1075 | #if defined (__BIG_ENDIAN) |
| 1074 | /* turn on LFB swapping */ | 1076 | /* turn on LFB swapping */ |
| @@ -1377,6 +1379,57 @@ static struct fb_ops nvidia_fb_ops = { | |||
| 1377 | .fb_sync = nvidiafb_sync, | 1379 | .fb_sync = nvidiafb_sync, |
| 1378 | }; | 1380 | }; |
| 1379 | 1381 | ||
| 1382 | #ifdef CONFIG_PM | ||
| 1383 | static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state) | ||
| 1384 | { | ||
| 1385 | struct fb_info *info = pci_get_drvdata(dev); | ||
| 1386 | struct nvidia_par *par = info->par; | ||
| 1387 | |||
| 1388 | acquire_console_sem(); | ||
| 1389 | par->pm_state = state.event; | ||
| 1390 | |||
| 1391 | if (state.event == PM_EVENT_FREEZE) { | ||
| 1392 | dev->dev.power.power_state = state; | ||
| 1393 | } else { | ||
| 1394 | fb_set_suspend(info, 1); | ||
| 1395 | nvidiafb_blank(FB_BLANK_POWERDOWN, info); | ||
| 1396 | nvidia_write_regs(par, &par->SavedReg); | ||
| 1397 | pci_save_state(dev); | ||
| 1398 | pci_disable_device(dev); | ||
| 1399 | pci_set_power_state(dev, pci_choose_state(dev, state)); | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | release_console_sem(); | ||
| 1403 | return 0; | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | static int nvidiafb_resume(struct pci_dev *dev) | ||
| 1407 | { | ||
| 1408 | struct fb_info *info = pci_get_drvdata(dev); | ||
| 1409 | struct nvidia_par *par = info->par; | ||
| 1410 | |||
| 1411 | acquire_console_sem(); | ||
| 1412 | pci_set_power_state(dev, PCI_D0); | ||
| 1413 | |||
| 1414 | if (par->pm_state != PM_EVENT_FREEZE) { | ||
| 1415 | pci_restore_state(dev); | ||
| 1416 | pci_enable_device(dev); | ||
| 1417 | pci_set_master(dev); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | par->pm_state = PM_EVENT_ON; | ||
| 1421 | nvidiafb_set_par(info); | ||
| 1422 | fb_set_suspend (info, 0); | ||
| 1423 | nvidiafb_blank(FB_BLANK_UNBLANK, info); | ||
| 1424 | |||
| 1425 | release_console_sem(); | ||
| 1426 | return 0; | ||
| 1427 | } | ||
| 1428 | #else | ||
| 1429 | #define nvidiafb_suspend NULL | ||
| 1430 | #define nvidiafb_resume NULL | ||
| 1431 | #endif | ||
| 1432 | |||
| 1380 | static int __devinit nvidia_set_fbinfo(struct fb_info *info) | 1433 | static int __devinit nvidia_set_fbinfo(struct fb_info *info) |
| 1381 | { | 1434 | { |
| 1382 | struct fb_monspecs *specs = &info->monspecs; | 1435 | struct fb_monspecs *specs = &info->monspecs; |
| @@ -1720,8 +1773,6 @@ static void __exit nvidiafb_remove(struct pci_dev *pd) | |||
| 1720 | struct nvidia_par *par = info->par; | 1773 | struct nvidia_par *par = info->par; |
| 1721 | 1774 | ||
| 1722 | NVTRACE_ENTER(); | 1775 | NVTRACE_ENTER(); |
| 1723 | if (!info) | ||
| 1724 | return; | ||
| 1725 | 1776 | ||
| 1726 | unregister_framebuffer(info); | 1777 | unregister_framebuffer(info); |
| 1727 | #ifdef CONFIG_MTRR | 1778 | #ifdef CONFIG_MTRR |
| @@ -1798,8 +1849,10 @@ static int __devinit nvidiafb_setup(char *options) | |||
| 1798 | static struct pci_driver nvidiafb_driver = { | 1849 | static struct pci_driver nvidiafb_driver = { |
| 1799 | .name = "nvidiafb", | 1850 | .name = "nvidiafb", |
| 1800 | .id_table = nvidiafb_pci_tbl, | 1851 | .id_table = nvidiafb_pci_tbl, |
| 1801 | .probe = nvidiafb_probe, | 1852 | .probe = nvidiafb_probe, |
| 1802 | .remove = __exit_p(nvidiafb_remove), | 1853 | .suspend = nvidiafb_suspend, |
| 1854 | .resume = nvidiafb_resume, | ||
| 1855 | .remove = __exit_p(nvidiafb_remove), | ||
| 1803 | }; | 1856 | }; |
| 1804 | 1857 | ||
| 1805 | /* ------------------------------------------------------------------------- * | 1858 | /* ------------------------------------------------------------------------- * |
diff --git a/drivers/video/pmagb-b-fb.c b/drivers/video/pmagb-b-fb.c index eeeac924b500..73e2d7d16608 100644 --- a/drivers/video/pmagb-b-fb.c +++ b/drivers/video/pmagb-b-fb.c | |||
| @@ -228,7 +228,7 @@ static void __init pmagbbfb_osc_setup(struct fb_info *info) | |||
| 228 | 228 | ||
| 229 | freq1 = (par->osc0 * count1 + count0 / 2) / count0; | 229 | freq1 = (par->osc0 * count1 + count0 / 2) / count0; |
| 230 | par->osc1 = freq1; | 230 | par->osc1 = freq1; |
| 231 | for (i = 0; i < sizeof(pmagbbfb_freqs) / sizeof(*pmagbbfb_freqs); i++) | 231 | for (i = 0; i < ARRAY_SIZE(pmagbbfb_freqs); i++) |
| 232 | if (freq1 >= pmagbbfb_freqs[i] - | 232 | if (freq1 >= pmagbbfb_freqs[i] - |
| 233 | (pmagbbfb_freqs[i] + 128) / 256 && | 233 | (pmagbbfb_freqs[i] + 128) / 256 && |
| 234 | freq1 <= pmagbbfb_freqs[i] + | 234 | freq1 <= pmagbbfb_freqs[i] + |
diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c index db9fb9074dbc..24982adb3aa2 100644 --- a/drivers/video/radeonfb.c +++ b/drivers/video/radeonfb.c | |||
| @@ -759,7 +759,7 @@ static void __iomem *radeon_find_rom(struct radeonfb_info *rinfo) | |||
| 759 | rom = rom_base; | 759 | rom = rom_base; |
| 760 | 760 | ||
| 761 | for (i = 0; (i < 512) && (stage != 4); i++) { | 761 | for (i = 0; (i < 512) && (stage != 4); i++) { |
| 762 | for(j = 0;j < sizeof(radeon_sig)/sizeof(char *);j++) { | 762 | for (j = 0; j < ARRAY_SIZE(radeon_sig); j++) { |
| 763 | if (radeon_sig[j][0] == *rom) | 763 | if (radeon_sig[j][0] == *rom) |
| 764 | if (strncmp(radeon_sig[j], rom, | 764 | if (strncmp(radeon_sig[j], rom, |
| 765 | strlen(radeon_sig[j])) == 0) { | 765 | strlen(radeon_sig[j])) == 0) { |
diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 6c19ab6afb01..f841f013b96f 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c | |||
| @@ -2072,8 +2072,6 @@ static void __exit rivafb_remove(struct pci_dev *pd) | |||
| 2072 | struct riva_par *par = info->par; | 2072 | struct riva_par *par = info->par; |
| 2073 | 2073 | ||
| 2074 | NVTRACE_ENTER(); | 2074 | NVTRACE_ENTER(); |
| 2075 | if (!info) | ||
| 2076 | return; | ||
| 2077 | 2075 | ||
| 2078 | #ifdef CONFIG_FB_RIVA_I2C | 2076 | #ifdef CONFIG_FB_RIVA_I2C |
| 2079 | riva_delete_i2c_busses(par); | 2077 | riva_delete_i2c_busses(par); |
diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c index 00719a91479f..21debed863ac 100644 --- a/drivers/video/savage/savagefb-i2c.c +++ b/drivers/video/savage/savagefb-i2c.c | |||
| @@ -273,8 +273,7 @@ int savagefb_probe_i2c_connector(struct fb_info *info, u8 **out_edid) | |||
| 273 | } | 273 | } |
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | if (out_edid) | 276 | *out_edid = edid; |
| 277 | *out_edid = edid; | ||
| 278 | 277 | ||
| 279 | return (edid) ? 0 : 1; | 278 | return (edid) ? 0 : 1; |
| 280 | } | 279 | } |
diff --git a/drivers/video/sis/init301.c b/drivers/video/sis/init301.c index 2d88f908170a..c3e070a6effd 100644 --- a/drivers/video/sis/init301.c +++ b/drivers/video/sis/init301.c | |||
| @@ -8564,11 +8564,9 @@ SiS_ChrontelDoSomething3(struct SiS_Private *SiS_Pr, unsigned short ModeNo) | |||
| 8564 | static void | 8564 | static void |
| 8565 | SiS_ChrontelDoSomething2(struct SiS_Private *SiS_Pr) | 8565 | SiS_ChrontelDoSomething2(struct SiS_Private *SiS_Pr) |
| 8566 | { | 8566 | { |
| 8567 | unsigned short temp,tempcl,tempch; | 8567 | unsigned short temp; |
| 8568 | 8568 | ||
| 8569 | SiS_LongDelay(SiS_Pr, 1); | 8569 | SiS_LongDelay(SiS_Pr, 1); |
| 8570 | tempcl = 3; | ||
| 8571 | tempch = 0; | ||
| 8572 | 8570 | ||
| 8573 | do { | 8571 | do { |
| 8574 | temp = SiS_GetCH701x(SiS_Pr,0x66); | 8572 | temp = SiS_GetCH701x(SiS_Pr,0x66); |
| @@ -8582,13 +8580,6 @@ SiS_ChrontelDoSomething2(struct SiS_Private *SiS_Pr) | |||
| 8582 | 8580 | ||
| 8583 | SiS_SetCH701xForLCD(SiS_Pr); | 8581 | SiS_SetCH701xForLCD(SiS_Pr); |
| 8584 | 8582 | ||
| 8585 | if(tempcl == 0) { | ||
| 8586 | if(tempch == 3) break; | ||
| 8587 | SiS_ChrontelResetDB(SiS_Pr); | ||
| 8588 | tempcl = 3; | ||
| 8589 | tempch++; | ||
| 8590 | } | ||
| 8591 | tempcl--; | ||
| 8592 | temp = SiS_GetCH701x(SiS_Pr,0x76); | 8583 | temp = SiS_GetCH701x(SiS_Pr,0x76); |
| 8593 | temp &= 0xfb; /* Reset PLL */ | 8584 | temp &= 0xfb; /* Reset PLL */ |
| 8594 | SiS_SetCH701x(SiS_Pr,0x76,temp); | 8585 | SiS_SetCH701x(SiS_Pr,0x76,temp); |
diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c index 8c1a8b5135c6..c44de90ca12e 100644 --- a/drivers/video/sstfb.c +++ b/drivers/video/sstfb.c | |||
| @@ -1194,10 +1194,11 @@ static struct dac_switch dacs[] __devinitdata = { | |||
| 1194 | static int __devinit sst_detect_dactype(struct fb_info *info, struct sstfb_par *par) | 1194 | static int __devinit sst_detect_dactype(struct fb_info *info, struct sstfb_par *par) |
| 1195 | { | 1195 | { |
| 1196 | int i, ret = 0; | 1196 | int i, ret = 0; |
| 1197 | 1197 | ||
| 1198 | for (i=0; i<sizeof(dacs)/sizeof(dacs[0]); i++) { | 1198 | for (i = 0; i < ARRAY_SIZE(dacs); i++) { |
| 1199 | ret = dacs[i].detect(info); | 1199 | ret = dacs[i].detect(info); |
| 1200 | if (ret) break; | 1200 | if (ret) |
| 1201 | break; | ||
| 1201 | } | 1202 | } |
| 1202 | if (!ret) | 1203 | if (!ret) |
| 1203 | return 0; | 1204 | return 0; |
| @@ -1604,8 +1605,8 @@ static int sstfb_dump_regs(struct fb_info *info) | |||
| 1604 | {FBZMODE,"fbzmode"}, | 1605 | {FBZMODE,"fbzmode"}, |
| 1605 | }; | 1606 | }; |
| 1606 | 1607 | ||
| 1607 | const int pci_s = sizeof(pci_regs)/sizeof(pci_regs[0]); | 1608 | const int pci_s = ARRAY_SIZE(pci_regs); |
| 1608 | const int sst_s = sizeof(sst_regs)/sizeof(sst_regs[0]); | 1609 | const int sst_s = ARRAY_SIZE(sst_regs); |
| 1609 | struct sstfb_par *par = info->par; | 1610 | struct sstfb_par *par = info->par; |
| 1610 | struct pci_dev *dev = par->dev; | 1611 | struct pci_dev *dev = par->dev; |
| 1611 | u32 pci_res[pci_s]; | 1612 | u32 pci_res[pci_s]; |
diff --git a/drivers/video/virgefb.c b/drivers/video/virgefb.c index ed78747487e2..5ea2345dab99 100644 --- a/drivers/video/virgefb.c +++ b/drivers/video/virgefb.c | |||
| @@ -616,8 +616,7 @@ static struct { | |||
| 616 | #endif | 616 | #endif |
| 617 | }; | 617 | }; |
| 618 | 618 | ||
| 619 | #define arraysize(x) (sizeof(x)/sizeof(*(x))) | 619 | #define NUM_TOTAL_MODES ARRAY_SIZE(virgefb_predefined) |
| 620 | #define NUM_TOTAL_MODES arraysize(virgefb_predefined) | ||
| 621 | 620 | ||
| 622 | /* | 621 | /* |
| 623 | * Default to 800x600 for video=virge8:, virge16: or virge32: | 622 | * Default to 800x600 for video=virge8:, virge16: or virge32: |
diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 5ccfcf26310d..3fded389d06b 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c | |||
| @@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, | |||
| 92 | ; | 92 | ; |
| 93 | dput(dentry); | 93 | dput(dentry); |
| 94 | 94 | ||
| 95 | if ( may_umount(mnt) == 0 ) { | 95 | if ( may_umount(mnt) ) { |
| 96 | mntput(mnt); | 96 | mntput(mnt); |
| 97 | DPRINTK(("autofs: signaling expire on %s\n", ent->name)); | 97 | DPRINTK(("autofs: signaling expire on %s\n", ent->name)); |
| 98 | return ent; /* Expirable! */ | 98 | return ent; /* Expirable! */ |
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index f54c5b21f876..617fd7b37447 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | * linux/fs/autofs/autofs_i.h | 3 | * linux/fs/autofs/autofs_i.h |
| 4 | * | 4 | * |
| 5 | * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved | 5 | * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved |
| 6 | * Copyright 2005-2006 Ian Kent <raven@themaw.net> | ||
| 6 | * | 7 | * |
| 7 | * This file is part of the Linux kernel and is made available under | 8 | * This file is part of the Linux kernel and is made available under |
| 8 | * the terms of the GNU General Public License, version 2, or at your | 9 | * the terms of the GNU General Public License, version 2, or at your |
| @@ -41,14 +42,6 @@ | |||
| 41 | 42 | ||
| 42 | #define AUTOFS_SUPER_MAGIC 0x0187 | 43 | #define AUTOFS_SUPER_MAGIC 0x0187 |
| 43 | 44 | ||
| 44 | /* | ||
| 45 | * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the | ||
| 46 | * kernel will keep the negative response cached for up to the time given | ||
| 47 | * here, although the time can be shorter if the kernel throws the dcache | ||
| 48 | * entry away. This probably should be settable from user space. | ||
| 49 | */ | ||
| 50 | #define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ | ||
| 51 | |||
| 52 | /* Unified info structure. This is pointed to by both the dentry and | 45 | /* Unified info structure. This is pointed to by both the dentry and |
| 53 | inode structures. Each file in the filesystem has an instance of this | 46 | inode structures. Each file in the filesystem has an instance of this |
| 54 | structure. It holds a reference to the dentry, so dentries are never | 47 | structure. It holds a reference to the dentry, so dentries are never |
| @@ -63,6 +56,7 @@ struct autofs_info { | |||
| 63 | 56 | ||
| 64 | struct autofs_sb_info *sbi; | 57 | struct autofs_sb_info *sbi; |
| 65 | unsigned long last_used; | 58 | unsigned long last_used; |
| 59 | atomic_t count; | ||
| 66 | 60 | ||
| 67 | mode_t mode; | 61 | mode_t mode; |
| 68 | size_t size; | 62 | size_t size; |
| @@ -83,23 +77,37 @@ struct autofs_wait_queue { | |||
| 83 | int hash; | 77 | int hash; |
| 84 | int len; | 78 | int len; |
| 85 | char *name; | 79 | char *name; |
| 80 | u32 dev; | ||
| 81 | u64 ino; | ||
| 82 | uid_t uid; | ||
| 83 | gid_t gid; | ||
| 84 | pid_t pid; | ||
| 85 | pid_t tgid; | ||
| 86 | /* This is for status reporting upon return */ | 86 | /* This is for status reporting upon return */ |
| 87 | int status; | 87 | int status; |
| 88 | atomic_t notified; | 88 | atomic_t notify; |
| 89 | atomic_t wait_ctr; | 89 | atomic_t wait_ctr; |
| 90 | }; | 90 | }; |
| 91 | 91 | ||
| 92 | #define AUTOFS_SBI_MAGIC 0x6d4a556d | 92 | #define AUTOFS_SBI_MAGIC 0x6d4a556d |
| 93 | 93 | ||
| 94 | #define AUTOFS_TYPE_INDIRECT 0x0001 | ||
| 95 | #define AUTOFS_TYPE_DIRECT 0x0002 | ||
| 96 | #define AUTOFS_TYPE_OFFSET 0x0004 | ||
| 97 | |||
| 94 | struct autofs_sb_info { | 98 | struct autofs_sb_info { |
| 95 | u32 magic; | 99 | u32 magic; |
| 96 | struct dentry *root; | 100 | struct dentry *root; |
| 101 | int pipefd; | ||
| 97 | struct file *pipe; | 102 | struct file *pipe; |
| 98 | pid_t oz_pgrp; | 103 | pid_t oz_pgrp; |
| 99 | int catatonic; | 104 | int catatonic; |
| 100 | int version; | 105 | int version; |
| 101 | int sub_version; | 106 | int sub_version; |
| 107 | int min_proto; | ||
| 108 | int max_proto; | ||
| 102 | unsigned long exp_timeout; | 109 | unsigned long exp_timeout; |
| 110 | unsigned int type; | ||
| 103 | int reghost_enabled; | 111 | int reghost_enabled; |
| 104 | int needs_reghost; | 112 | int needs_reghost; |
| 105 | struct super_block *sb; | 113 | struct super_block *sb; |
| @@ -166,6 +174,8 @@ int autofs4_expire_multi(struct super_block *, struct vfsmount *, | |||
| 166 | extern struct inode_operations autofs4_symlink_inode_operations; | 174 | extern struct inode_operations autofs4_symlink_inode_operations; |
| 167 | extern struct inode_operations autofs4_dir_inode_operations; | 175 | extern struct inode_operations autofs4_dir_inode_operations; |
| 168 | extern struct inode_operations autofs4_root_inode_operations; | 176 | extern struct inode_operations autofs4_root_inode_operations; |
| 177 | extern struct inode_operations autofs4_indirect_root_inode_operations; | ||
| 178 | extern struct inode_operations autofs4_direct_root_inode_operations; | ||
| 169 | extern struct file_operations autofs4_dir_operations; | 179 | extern struct file_operations autofs4_dir_operations; |
| 170 | extern struct file_operations autofs4_root_operations; | 180 | extern struct file_operations autofs4_root_operations; |
| 171 | 181 | ||
| @@ -176,13 +186,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *, struct autofs_sb_info | |||
| 176 | 186 | ||
| 177 | /* Queue management functions */ | 187 | /* Queue management functions */ |
| 178 | 188 | ||
| 179 | enum autofs_notify | ||
| 180 | { | ||
| 181 | NFY_NONE, | ||
| 182 | NFY_MOUNT, | ||
| 183 | NFY_EXPIRE | ||
| 184 | }; | ||
| 185 | |||
| 186 | int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); | 189 | int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); |
| 187 | int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); | 190 | int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); |
| 188 | void autofs4_catatonic_mode(struct autofs_sb_info *); | 191 | void autofs4_catatonic_mode(struct autofs_sb_info *); |
| @@ -200,12 +203,22 @@ static inline int autofs4_follow_mount(struct vfsmount **mnt, struct dentry **de | |||
| 200 | return res; | 203 | return res; |
| 201 | } | 204 | } |
| 202 | 205 | ||
| 206 | static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) | ||
| 207 | { | ||
| 208 | return new_encode_dev(sbi->sb->s_dev); | ||
| 209 | } | ||
| 210 | |||
| 211 | static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) | ||
| 212 | { | ||
| 213 | return sbi->sb->s_root->d_inode->i_ino; | ||
| 214 | } | ||
| 215 | |||
| 203 | static inline int simple_positive(struct dentry *dentry) | 216 | static inline int simple_positive(struct dentry *dentry) |
| 204 | { | 217 | { |
| 205 | return dentry->d_inode && !d_unhashed(dentry); | 218 | return dentry->d_inode && !d_unhashed(dentry); |
| 206 | } | 219 | } |
| 207 | 220 | ||
| 208 | static inline int simple_empty_nolock(struct dentry *dentry) | 221 | static inline int __simple_empty(struct dentry *dentry) |
| 209 | { | 222 | { |
| 210 | struct dentry *child; | 223 | struct dentry *child; |
| 211 | int ret = 0; | 224 | int ret = 0; |
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index dc39589df165..b8ce02607d66 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | * | 4 | * |
| 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved | 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved |
| 6 | * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> | 6 | * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> |
| 7 | * Copyright 2001-2003 Ian Kent <raven@themaw.net> | 7 | * Copyright 2001-2006 Ian Kent <raven@themaw.net> |
| 8 | * | 8 | * |
| 9 | * This file is part of the Linux kernel and is made available under | 9 | * This file is part of the Linux kernel and is made available under |
| 10 | * the terms of the GNU General Public License, version 2, or at your | 10 | * the terms of the GNU General Public License, version 2, or at your |
| @@ -16,7 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | static unsigned long now; | 17 | static unsigned long now; |
| 18 | 18 | ||
| 19 | /* Check if a dentry can be expired return 1 if it can else return 0 */ | 19 | /* Check if a dentry can be expired */ |
| 20 | static inline int autofs4_can_expire(struct dentry *dentry, | 20 | static inline int autofs4_can_expire(struct dentry *dentry, |
| 21 | unsigned long timeout, int do_now) | 21 | unsigned long timeout, int do_now) |
| 22 | { | 22 | { |
| @@ -41,14 +41,14 @@ static inline int autofs4_can_expire(struct dentry *dentry, | |||
| 41 | attempts if expire fails the first time */ | 41 | attempts if expire fails the first time */ |
| 42 | ino->last_used = now; | 42 | ino->last_used = now; |
| 43 | } | 43 | } |
| 44 | |||
| 45 | return 1; | 44 | return 1; |
| 46 | } | 45 | } |
| 47 | 46 | ||
| 48 | /* Check a mount point for busyness return 1 if not busy, otherwise */ | 47 | /* Check a mount point for busyness */ |
| 49 | static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) | 48 | static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) |
| 50 | { | 49 | { |
| 51 | int status = 0; | 50 | struct dentry *top = dentry; |
| 51 | int status = 1; | ||
| 52 | 52 | ||
| 53 | DPRINTK("dentry %p %.*s", | 53 | DPRINTK("dentry %p %.*s", |
| 54 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | 54 | dentry, (int)dentry->d_name.len, dentry->d_name.name); |
| @@ -63,9 +63,14 @@ static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) | |||
| 63 | if (is_autofs4_dentry(dentry)) | 63 | if (is_autofs4_dentry(dentry)) |
| 64 | goto done; | 64 | goto done; |
| 65 | 65 | ||
| 66 | /* The big question */ | 66 | /* Update the expiry counter if fs is busy */ |
| 67 | if (may_umount_tree(mnt) == 0) | 67 | if (!may_umount_tree(mnt)) { |
| 68 | status = 1; | 68 | struct autofs_info *ino = autofs4_dentry_ino(top); |
| 69 | ino->last_used = jiffies; | ||
| 70 | goto done; | ||
| 71 | } | ||
| 72 | |||
| 73 | status = 0; | ||
| 69 | done: | 74 | done: |
| 70 | DPRINTK("returning = %d", status); | 75 | DPRINTK("returning = %d", status); |
| 71 | mntput(mnt); | 76 | mntput(mnt); |
| @@ -73,78 +78,124 @@ done: | |||
| 73 | return status; | 78 | return status; |
| 74 | } | 79 | } |
| 75 | 80 | ||
| 81 | /* | ||
| 82 | * Calculate next entry in top down tree traversal. | ||
| 83 | * From next_mnt in namespace.c - elegant. | ||
| 84 | */ | ||
| 85 | static struct dentry *next_dentry(struct dentry *p, struct dentry *root) | ||
| 86 | { | ||
| 87 | struct list_head *next = p->d_subdirs.next; | ||
| 88 | |||
| 89 | if (next == &p->d_subdirs) { | ||
| 90 | while (1) { | ||
| 91 | if (p == root) | ||
| 92 | return NULL; | ||
| 93 | next = p->d_u.d_child.next; | ||
| 94 | if (next != &p->d_parent->d_subdirs) | ||
| 95 | break; | ||
| 96 | p = p->d_parent; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | return list_entry(next, struct dentry, d_u.d_child); | ||
| 100 | } | ||
| 101 | |||
| 102 | /* | ||
| 103 | * Check a direct mount point for busyness. | ||
| 104 | * Direct mounts have similar expiry semantics to tree mounts. | ||
| 105 | * The tree is not busy iff no mountpoints are busy and there are no | ||
| 106 | * autofs submounts. | ||
| 107 | */ | ||
| 108 | static int autofs4_direct_busy(struct vfsmount *mnt, | ||
| 109 | struct dentry *top, | ||
| 110 | unsigned long timeout, | ||
| 111 | int do_now) | ||
| 112 | { | ||
| 113 | DPRINTK("top %p %.*s", | ||
| 114 | top, (int) top->d_name.len, top->d_name.name); | ||
| 115 | |||
| 116 | /* If it's busy update the expiry counters */ | ||
| 117 | if (!may_umount_tree(mnt)) { | ||
| 118 | struct autofs_info *ino = autofs4_dentry_ino(top); | ||
| 119 | if (ino) | ||
| 120 | ino->last_used = jiffies; | ||
| 121 | return 1; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* Timeout of a direct mount is determined by its top dentry */ | ||
| 125 | if (!autofs4_can_expire(top, timeout, do_now)) | ||
| 126 | return 1; | ||
| 127 | |||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 76 | /* Check a directory tree of mount points for busyness | 131 | /* Check a directory tree of mount points for busyness |
| 77 | * The tree is not busy iff no mountpoints are busy | 132 | * The tree is not busy iff no mountpoints are busy |
| 78 | * Return 1 if the tree is busy or 0 otherwise | ||
| 79 | */ | 133 | */ |
| 80 | static int autofs4_check_tree(struct vfsmount *mnt, | 134 | static int autofs4_tree_busy(struct vfsmount *mnt, |
| 81 | struct dentry *top, | 135 | struct dentry *top, |
| 82 | unsigned long timeout, | 136 | unsigned long timeout, |
| 83 | int do_now) | 137 | int do_now) |
| 84 | { | 138 | { |
| 85 | struct dentry *this_parent = top; | 139 | struct autofs_info *top_ino = autofs4_dentry_ino(top); |
| 86 | struct list_head *next; | 140 | struct dentry *p; |
| 87 | 141 | ||
| 88 | DPRINTK("parent %p %.*s", | 142 | DPRINTK("top %p %.*s", |
| 89 | top, (int)top->d_name.len, top->d_name.name); | 143 | top, (int)top->d_name.len, top->d_name.name); |
| 90 | 144 | ||
| 91 | /* Negative dentry - give up */ | 145 | /* Negative dentry - give up */ |
| 92 | if (!simple_positive(top)) | 146 | if (!simple_positive(top)) |
| 93 | return 0; | 147 | return 1; |
| 94 | |||
| 95 | /* Timeout of a tree mount is determined by its top dentry */ | ||
| 96 | if (!autofs4_can_expire(top, timeout, do_now)) | ||
| 97 | return 0; | ||
| 98 | |||
| 99 | /* Is someone visiting anywhere in the tree ? */ | ||
| 100 | if (may_umount_tree(mnt)) | ||
| 101 | return 0; | ||
| 102 | 148 | ||
| 103 | spin_lock(&dcache_lock); | 149 | spin_lock(&dcache_lock); |
| 104 | repeat: | 150 | for (p = top; p; p = next_dentry(p, top)) { |
| 105 | next = this_parent->d_subdirs.next; | ||
| 106 | resume: | ||
| 107 | while (next != &this_parent->d_subdirs) { | ||
| 108 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); | ||
| 109 | |||
| 110 | /* Negative dentry - give up */ | 151 | /* Negative dentry - give up */ |
| 111 | if (!simple_positive(dentry)) { | 152 | if (!simple_positive(p)) |
| 112 | next = next->next; | ||
| 113 | continue; | 153 | continue; |
| 114 | } | ||
| 115 | 154 | ||
| 116 | DPRINTK("dentry %p %.*s", | 155 | DPRINTK("dentry %p %.*s", |
| 117 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | 156 | p, (int) p->d_name.len, p->d_name.name); |
| 118 | |||
| 119 | if (!simple_empty_nolock(dentry)) { | ||
| 120 | this_parent = dentry; | ||
| 121 | goto repeat; | ||
| 122 | } | ||
| 123 | 157 | ||
| 124 | dentry = dget(dentry); | 158 | p = dget(p); |
| 125 | spin_unlock(&dcache_lock); | 159 | spin_unlock(&dcache_lock); |
| 126 | 160 | ||
| 127 | if (d_mountpoint(dentry)) { | 161 | /* |
| 128 | /* First busy => tree busy */ | 162 | * Is someone visiting anywhere in the subtree ? |
| 129 | if (!autofs4_check_mount(mnt, dentry)) { | 163 | * If there's no mount we need to check the usage |
| 130 | dput(dentry); | 164 | * count for the autofs dentry. |
| 131 | return 0; | 165 | * If the fs is busy update the expiry counter. |
| 166 | */ | ||
| 167 | if (d_mountpoint(p)) { | ||
| 168 | if (autofs4_mount_busy(mnt, p)) { | ||
| 169 | top_ino->last_used = jiffies; | ||
| 170 | dput(p); | ||
| 171 | return 1; | ||
| 172 | } | ||
| 173 | } else { | ||
| 174 | struct autofs_info *ino = autofs4_dentry_ino(p); | ||
| 175 | unsigned int ino_count = atomic_read(&ino->count); | ||
| 176 | |||
| 177 | /* allow for dget above and top is already dgot */ | ||
| 178 | if (p == top) | ||
| 179 | ino_count += 2; | ||
| 180 | else | ||
| 181 | ino_count++; | ||
| 182 | |||
| 183 | if (atomic_read(&p->d_count) > ino_count) { | ||
| 184 | top_ino->last_used = jiffies; | ||
| 185 | dput(p); | ||
| 186 | return 1; | ||
| 132 | } | 187 | } |
| 133 | } | 188 | } |
| 134 | 189 | dput(p); | |
| 135 | dput(dentry); | ||
| 136 | spin_lock(&dcache_lock); | 190 | spin_lock(&dcache_lock); |
| 137 | next = next->next; | ||
| 138 | } | ||
| 139 | |||
| 140 | if (this_parent != top) { | ||
| 141 | next = this_parent->d_u.d_child.next; | ||
| 142 | this_parent = this_parent->d_parent; | ||
| 143 | goto resume; | ||
| 144 | } | 191 | } |
| 145 | spin_unlock(&dcache_lock); | 192 | spin_unlock(&dcache_lock); |
| 146 | 193 | ||
| 147 | return 1; | 194 | /* Timeout of a tree mount is ultimately determined by its top dentry */ |
| 195 | if (!autofs4_can_expire(top, timeout, do_now)) | ||
| 196 | return 1; | ||
| 197 | |||
| 198 | return 0; | ||
| 148 | } | 199 | } |
| 149 | 200 | ||
| 150 | static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, | 201 | static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, |
| @@ -152,58 +203,68 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, | |||
| 152 | unsigned long timeout, | 203 | unsigned long timeout, |
| 153 | int do_now) | 204 | int do_now) |
| 154 | { | 205 | { |
| 155 | struct dentry *this_parent = parent; | 206 | struct dentry *p; |
| 156 | struct list_head *next; | ||
| 157 | 207 | ||
| 158 | DPRINTK("parent %p %.*s", | 208 | DPRINTK("parent %p %.*s", |
| 159 | parent, (int)parent->d_name.len, parent->d_name.name); | 209 | parent, (int)parent->d_name.len, parent->d_name.name); |
| 160 | 210 | ||
| 161 | spin_lock(&dcache_lock); | 211 | spin_lock(&dcache_lock); |
| 162 | repeat: | 212 | for (p = parent; p; p = next_dentry(p, parent)) { |
| 163 | next = this_parent->d_subdirs.next; | ||
| 164 | resume: | ||
| 165 | while (next != &this_parent->d_subdirs) { | ||
| 166 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); | ||
| 167 | |||
| 168 | /* Negative dentry - give up */ | 213 | /* Negative dentry - give up */ |
| 169 | if (!simple_positive(dentry)) { | 214 | if (!simple_positive(p)) |
| 170 | next = next->next; | ||
| 171 | continue; | 215 | continue; |
| 172 | } | ||
| 173 | 216 | ||
| 174 | DPRINTK("dentry %p %.*s", | 217 | DPRINTK("dentry %p %.*s", |
| 175 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | 218 | p, (int) p->d_name.len, p->d_name.name); |
| 176 | |||
| 177 | if (!list_empty(&dentry->d_subdirs)) { | ||
| 178 | this_parent = dentry; | ||
| 179 | goto repeat; | ||
| 180 | } | ||
| 181 | 219 | ||
| 182 | dentry = dget(dentry); | 220 | p = dget(p); |
| 183 | spin_unlock(&dcache_lock); | 221 | spin_unlock(&dcache_lock); |
| 184 | 222 | ||
| 185 | if (d_mountpoint(dentry)) { | 223 | if (d_mountpoint(p)) { |
| 186 | /* Can we expire this guy */ | ||
| 187 | if (!autofs4_can_expire(dentry, timeout, do_now)) | ||
| 188 | goto cont; | ||
| 189 | |||
| 190 | /* Can we umount this guy */ | 224 | /* Can we umount this guy */ |
| 191 | if (autofs4_check_mount(mnt, dentry)) | 225 | if (autofs4_mount_busy(mnt, p)) |
| 192 | return dentry; | 226 | goto cont; |
| 193 | 227 | ||
| 228 | /* Can we expire this guy */ | ||
| 229 | if (autofs4_can_expire(p, timeout, do_now)) | ||
| 230 | return p; | ||
| 194 | } | 231 | } |
| 195 | cont: | 232 | cont: |
| 196 | dput(dentry); | 233 | dput(p); |
| 197 | spin_lock(&dcache_lock); | 234 | spin_lock(&dcache_lock); |
| 198 | next = next->next; | ||
| 199 | } | 235 | } |
| 236 | spin_unlock(&dcache_lock); | ||
| 237 | return NULL; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* Check if we can expire a direct mount (possibly a tree) */ | ||
| 241 | static struct dentry *autofs4_expire_direct(struct super_block *sb, | ||
| 242 | struct vfsmount *mnt, | ||
| 243 | struct autofs_sb_info *sbi, | ||
| 244 | int how) | ||
| 245 | { | ||
| 246 | unsigned long timeout; | ||
| 247 | struct dentry *root = dget(sb->s_root); | ||
| 248 | int do_now = how & AUTOFS_EXP_IMMEDIATE; | ||
| 249 | |||
| 250 | if (!sbi->exp_timeout || !root) | ||
| 251 | return NULL; | ||
| 252 | |||
| 253 | now = jiffies; | ||
| 254 | timeout = sbi->exp_timeout; | ||
| 255 | |||
| 256 | /* Lock the tree as we must expire as a whole */ | ||
| 257 | spin_lock(&sbi->fs_lock); | ||
| 258 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { | ||
| 259 | struct autofs_info *ino = autofs4_dentry_ino(root); | ||
| 200 | 260 | ||
| 201 | if (this_parent != parent) { | 261 | /* Set this flag early to catch sys_chdir and the like */ |
| 202 | next = this_parent->d_u.d_child.next; | 262 | ino->flags |= AUTOFS_INF_EXPIRING; |
| 203 | this_parent = this_parent->d_parent; | 263 | spin_unlock(&sbi->fs_lock); |
| 204 | goto resume; | 264 | return root; |
| 205 | } | 265 | } |
| 206 | spin_unlock(&dcache_lock); | 266 | spin_unlock(&sbi->fs_lock); |
| 267 | dput(root); | ||
| 207 | 268 | ||
| 208 | return NULL; | 269 | return NULL; |
| 209 | } | 270 | } |
| @@ -214,10 +275,10 @@ cont: | |||
| 214 | * - it is unused by any user process | 275 | * - it is unused by any user process |
| 215 | * - it has been unused for exp_timeout time | 276 | * - it has been unused for exp_timeout time |
| 216 | */ | 277 | */ |
| 217 | static struct dentry *autofs4_expire(struct super_block *sb, | 278 | static struct dentry *autofs4_expire_indirect(struct super_block *sb, |
| 218 | struct vfsmount *mnt, | 279 | struct vfsmount *mnt, |
| 219 | struct autofs_sb_info *sbi, | 280 | struct autofs_sb_info *sbi, |
| 220 | int how) | 281 | int how) |
| 221 | { | 282 | { |
| 222 | unsigned long timeout; | 283 | unsigned long timeout; |
| 223 | struct dentry *root = sb->s_root; | 284 | struct dentry *root = sb->s_root; |
| @@ -241,7 +302,7 @@ static struct dentry *autofs4_expire(struct super_block *sb, | |||
| 241 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); | 302 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); |
| 242 | 303 | ||
| 243 | /* Negative dentry - give up */ | 304 | /* Negative dentry - give up */ |
| 244 | if ( !simple_positive(dentry) ) { | 305 | if (!simple_positive(dentry)) { |
| 245 | next = next->next; | 306 | next = next->next; |
| 246 | continue; | 307 | continue; |
| 247 | } | 308 | } |
| @@ -249,31 +310,36 @@ static struct dentry *autofs4_expire(struct super_block *sb, | |||
| 249 | dentry = dget(dentry); | 310 | dentry = dget(dentry); |
| 250 | spin_unlock(&dcache_lock); | 311 | spin_unlock(&dcache_lock); |
| 251 | 312 | ||
| 252 | /* Case 1: indirect mount or top level direct mount */ | 313 | /* |
| 314 | * Case 1: (i) indirect mount or top level pseudo direct mount | ||
| 315 | * (autofs-4.1). | ||
| 316 | * (ii) indirect mount with offset mount, check the "/" | ||
| 317 | * offset (autofs-5.0+). | ||
| 318 | */ | ||
| 253 | if (d_mountpoint(dentry)) { | 319 | if (d_mountpoint(dentry)) { |
| 254 | DPRINTK("checking mountpoint %p %.*s", | 320 | DPRINTK("checking mountpoint %p %.*s", |
| 255 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | 321 | dentry, (int)dentry->d_name.len, dentry->d_name.name); |
| 256 | 322 | ||
| 257 | /* Can we expire this guy */ | 323 | /* Can we umount this guy */ |
| 258 | if (!autofs4_can_expire(dentry, timeout, do_now)) | 324 | if (autofs4_mount_busy(mnt, dentry)) |
| 259 | goto next; | 325 | goto next; |
| 260 | 326 | ||
| 261 | /* Can we umount this guy */ | 327 | /* Can we expire this guy */ |
| 262 | if (autofs4_check_mount(mnt, dentry)) { | 328 | if (autofs4_can_expire(dentry, timeout, do_now)) { |
| 263 | expired = dentry; | 329 | expired = dentry; |
| 264 | break; | 330 | break; |
| 265 | } | 331 | } |
| 266 | goto next; | 332 | goto next; |
| 267 | } | 333 | } |
| 268 | 334 | ||
| 269 | if ( simple_empty(dentry) ) | 335 | if (simple_empty(dentry)) |
| 270 | goto next; | 336 | goto next; |
| 271 | 337 | ||
| 272 | /* Case 2: tree mount, expire iff entire tree is not busy */ | 338 | /* Case 2: tree mount, expire iff entire tree is not busy */ |
| 273 | if (!exp_leaves) { | 339 | if (!exp_leaves) { |
| 274 | /* Lock the tree as we must expire as a whole */ | 340 | /* Lock the tree as we must expire as a whole */ |
| 275 | spin_lock(&sbi->fs_lock); | 341 | spin_lock(&sbi->fs_lock); |
| 276 | if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { | 342 | if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { |
| 277 | struct autofs_info *inf = autofs4_dentry_ino(dentry); | 343 | struct autofs_info *inf = autofs4_dentry_ino(dentry); |
| 278 | 344 | ||
| 279 | /* Set this flag early to catch sys_chdir and the like */ | 345 | /* Set this flag early to catch sys_chdir and the like */ |
| @@ -283,7 +349,10 @@ static struct dentry *autofs4_expire(struct super_block *sb, | |||
| 283 | break; | 349 | break; |
| 284 | } | 350 | } |
| 285 | spin_unlock(&sbi->fs_lock); | 351 | spin_unlock(&sbi->fs_lock); |
| 286 | /* Case 3: direct mount, expire individual leaves */ | 352 | /* |
| 353 | * Case 3: pseudo direct mount, expire individual leaves | ||
| 354 | * (autofs-4.1). | ||
| 355 | */ | ||
| 287 | } else { | 356 | } else { |
| 288 | expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); | 357 | expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); |
| 289 | if (expired) { | 358 | if (expired) { |
| @@ -297,7 +366,7 @@ next: | |||
| 297 | next = next->next; | 366 | next = next->next; |
| 298 | } | 367 | } |
| 299 | 368 | ||
| 300 | if ( expired ) { | 369 | if (expired) { |
| 301 | DPRINTK("returning %p %.*s", | 370 | DPRINTK("returning %p %.*s", |
| 302 | expired, (int)expired->d_name.len, expired->d_name.name); | 371 | expired, (int)expired->d_name.len, expired->d_name.name); |
| 303 | spin_lock(&dcache_lock); | 372 | spin_lock(&dcache_lock); |
| @@ -325,7 +394,7 @@ int autofs4_expire_run(struct super_block *sb, | |||
| 325 | pkt.hdr.proto_version = sbi->version; | 394 | pkt.hdr.proto_version = sbi->version; |
| 326 | pkt.hdr.type = autofs_ptype_expire; | 395 | pkt.hdr.type = autofs_ptype_expire; |
| 327 | 396 | ||
| 328 | if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) | 397 | if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) |
| 329 | return -EAGAIN; | 398 | return -EAGAIN; |
| 330 | 399 | ||
| 331 | pkt.len = dentry->d_name.len; | 400 | pkt.len = dentry->d_name.len; |
| @@ -351,17 +420,22 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, | |||
| 351 | if (arg && get_user(do_now, arg)) | 420 | if (arg && get_user(do_now, arg)) |
| 352 | return -EFAULT; | 421 | return -EFAULT; |
| 353 | 422 | ||
| 354 | if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { | 423 | if (sbi->type & AUTOFS_TYPE_DIRECT) |
| 355 | struct autofs_info *de_info = autofs4_dentry_ino(dentry); | 424 | dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); |
| 425 | else | ||
| 426 | dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); | ||
| 427 | |||
| 428 | if (dentry) { | ||
| 429 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | ||
| 356 | 430 | ||
| 357 | /* This is synchronous because it makes the daemon a | 431 | /* This is synchronous because it makes the daemon a |
| 358 | little easier */ | 432 | little easier */ |
| 359 | de_info->flags |= AUTOFS_INF_EXPIRING; | 433 | ino->flags |= AUTOFS_INF_EXPIRING; |
| 360 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); | 434 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); |
| 361 | de_info->flags &= ~AUTOFS_INF_EXPIRING; | 435 | ino->flags &= ~AUTOFS_INF_EXPIRING; |
| 362 | dput(dentry); | 436 | dput(dentry); |
| 363 | } | 437 | } |
| 364 | 438 | ||
| 365 | return ret; | 439 | return ret; |
| 366 | } | 440 | } |
| 367 | 441 | ||
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 1ad98d48e550..4eddee4e76fc 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | * linux/fs/autofs/inode.c | 3 | * linux/fs/autofs/inode.c |
| 4 | * | 4 | * |
| 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved | 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved |
| 6 | * Copyright 2005-2006 Ian Kent <raven@themaw.net> | ||
| 6 | * | 7 | * |
| 7 | * This file is part of the Linux kernel and is made available under | 8 | * This file is part of the Linux kernel and is made available under |
| 8 | * the terms of the GNU General Public License, version 2, or at your | 9 | * the terms of the GNU General Public License, version 2, or at your |
| @@ -13,6 +14,7 @@ | |||
| 13 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 14 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
| 15 | #include <linux/file.h> | 16 | #include <linux/file.h> |
| 17 | #include <linux/seq_file.h> | ||
| 16 | #include <linux/pagemap.h> | 18 | #include <linux/pagemap.h> |
| 17 | #include <linux/parser.h> | 19 | #include <linux/parser.h> |
| 18 | #include <linux/bitops.h> | 20 | #include <linux/bitops.h> |
| @@ -46,6 +48,7 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, | |||
| 46 | ino->size = 0; | 48 | ino->size = 0; |
| 47 | 49 | ||
| 48 | ino->last_used = jiffies; | 50 | ino->last_used = jiffies; |
| 51 | atomic_set(&ino->count, 0); | ||
| 49 | 52 | ||
| 50 | ino->sbi = sbi; | 53 | ino->sbi = sbi; |
| 51 | 54 | ||
| @@ -64,10 +67,19 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, | |||
| 64 | 67 | ||
| 65 | void autofs4_free_ino(struct autofs_info *ino) | 68 | void autofs4_free_ino(struct autofs_info *ino) |
| 66 | { | 69 | { |
| 70 | struct autofs_info *p_ino; | ||
| 71 | |||
| 67 | if (ino->dentry) { | 72 | if (ino->dentry) { |
| 68 | ino->dentry->d_fsdata = NULL; | 73 | ino->dentry->d_fsdata = NULL; |
| 69 | if (ino->dentry->d_inode) | 74 | if (ino->dentry->d_inode) { |
| 75 | struct dentry *parent = ino->dentry->d_parent; | ||
| 76 | if (atomic_dec_and_test(&ino->count)) { | ||
| 77 | p_ino = autofs4_dentry_ino(parent); | ||
| 78 | if (p_ino && parent != ino->dentry) | ||
| 79 | atomic_dec(&p_ino->count); | ||
| 80 | } | ||
| 70 | dput(ino->dentry); | 81 | dput(ino->dentry); |
| 82 | } | ||
| 71 | ino->dentry = NULL; | 83 | ino->dentry = NULL; |
| 72 | } | 84 | } |
| 73 | if (ino->free) | 85 | if (ino->free) |
| @@ -145,20 +157,44 @@ static void autofs4_put_super(struct super_block *sb) | |||
| 145 | autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ | 157 | autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ |
| 146 | 158 | ||
| 147 | /* Clean up and release dangling references */ | 159 | /* Clean up and release dangling references */ |
| 148 | if (sbi) | 160 | autofs4_force_release(sbi); |
| 149 | autofs4_force_release(sbi); | ||
| 150 | 161 | ||
| 151 | kfree(sbi); | 162 | kfree(sbi); |
| 152 | 163 | ||
| 153 | DPRINTK("shutting down"); | 164 | DPRINTK("shutting down"); |
| 154 | } | 165 | } |
| 155 | 166 | ||
| 167 | static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) | ||
| 168 | { | ||
| 169 | struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); | ||
| 170 | |||
| 171 | if (!sbi) | ||
| 172 | return 0; | ||
| 173 | |||
| 174 | seq_printf(m, ",fd=%d", sbi->pipefd); | ||
| 175 | seq_printf(m, ",pgrp=%d", sbi->oz_pgrp); | ||
| 176 | seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ); | ||
| 177 | seq_printf(m, ",minproto=%d", sbi->min_proto); | ||
| 178 | seq_printf(m, ",maxproto=%d", sbi->max_proto); | ||
| 179 | |||
| 180 | if (sbi->type & AUTOFS_TYPE_OFFSET) | ||
| 181 | seq_printf(m, ",offset"); | ||
| 182 | else if (sbi->type & AUTOFS_TYPE_DIRECT) | ||
| 183 | seq_printf(m, ",direct"); | ||
| 184 | else | ||
| 185 | seq_printf(m, ",indirect"); | ||
| 186 | |||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 156 | static struct super_operations autofs4_sops = { | 190 | static struct super_operations autofs4_sops = { |
| 157 | .put_super = autofs4_put_super, | 191 | .put_super = autofs4_put_super, |
| 158 | .statfs = simple_statfs, | 192 | .statfs = simple_statfs, |
| 193 | .show_options = autofs4_show_options, | ||
| 159 | }; | 194 | }; |
| 160 | 195 | ||
| 161 | enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; | 196 | enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, |
| 197 | Opt_indirect, Opt_direct, Opt_offset}; | ||
| 162 | 198 | ||
| 163 | static match_table_t tokens = { | 199 | static match_table_t tokens = { |
| 164 | {Opt_fd, "fd=%u"}, | 200 | {Opt_fd, "fd=%u"}, |
| @@ -167,11 +203,15 @@ static match_table_t tokens = { | |||
| 167 | {Opt_pgrp, "pgrp=%u"}, | 203 | {Opt_pgrp, "pgrp=%u"}, |
| 168 | {Opt_minproto, "minproto=%u"}, | 204 | {Opt_minproto, "minproto=%u"}, |
| 169 | {Opt_maxproto, "maxproto=%u"}, | 205 | {Opt_maxproto, "maxproto=%u"}, |
| 206 | {Opt_indirect, "indirect"}, | ||
| 207 | {Opt_direct, "direct"}, | ||
| 208 | {Opt_offset, "offset"}, | ||
| 170 | {Opt_err, NULL} | 209 | {Opt_err, NULL} |
| 171 | }; | 210 | }; |
| 172 | 211 | ||
| 173 | static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, | 212 | static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, |
| 174 | pid_t *pgrp, int *minproto, int *maxproto) | 213 | pid_t *pgrp, unsigned int *type, |
| 214 | int *minproto, int *maxproto) | ||
| 175 | { | 215 | { |
| 176 | char *p; | 216 | char *p; |
| 177 | substring_t args[MAX_OPT_ARGS]; | 217 | substring_t args[MAX_OPT_ARGS]; |
| @@ -225,6 +265,15 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, | |||
| 225 | return 1; | 265 | return 1; |
| 226 | *maxproto = option; | 266 | *maxproto = option; |
| 227 | break; | 267 | break; |
| 268 | case Opt_indirect: | ||
| 269 | *type = AUTOFS_TYPE_INDIRECT; | ||
| 270 | break; | ||
| 271 | case Opt_direct: | ||
| 272 | *type = AUTOFS_TYPE_DIRECT; | ||
| 273 | break; | ||
| 274 | case Opt_offset: | ||
| 275 | *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; | ||
| 276 | break; | ||
| 228 | default: | 277 | default: |
| 229 | return 1; | 278 | return 1; |
| 230 | } | 279 | } |
| @@ -243,6 +292,11 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi) | |||
| 243 | return ino; | 292 | return ino; |
| 244 | } | 293 | } |
| 245 | 294 | ||
| 295 | void autofs4_dentry_release(struct dentry *); | ||
| 296 | static struct dentry_operations autofs4_sb_dentry_operations = { | ||
| 297 | .d_release = autofs4_dentry_release, | ||
| 298 | }; | ||
| 299 | |||
| 246 | int autofs4_fill_super(struct super_block *s, void *data, int silent) | 300 | int autofs4_fill_super(struct super_block *s, void *data, int silent) |
| 247 | { | 301 | { |
| 248 | struct inode * root_inode; | 302 | struct inode * root_inode; |
| @@ -251,7 +305,6 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) | |||
| 251 | int pipefd; | 305 | int pipefd; |
| 252 | struct autofs_sb_info *sbi; | 306 | struct autofs_sb_info *sbi; |
| 253 | struct autofs_info *ino; | 307 | struct autofs_info *ino; |
| 254 | int minproto, maxproto; | ||
| 255 | 308 | ||
| 256 | sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); | 309 | sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); |
| 257 | if ( !sbi ) | 310 | if ( !sbi ) |
| @@ -263,12 +316,16 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) | |||
| 263 | s->s_fs_info = sbi; | 316 | s->s_fs_info = sbi; |
| 264 | sbi->magic = AUTOFS_SBI_MAGIC; | 317 | sbi->magic = AUTOFS_SBI_MAGIC; |
| 265 | sbi->root = NULL; | 318 | sbi->root = NULL; |
| 319 | sbi->pipefd = -1; | ||
| 266 | sbi->catatonic = 0; | 320 | sbi->catatonic = 0; |
| 267 | sbi->exp_timeout = 0; | 321 | sbi->exp_timeout = 0; |
| 268 | sbi->oz_pgrp = process_group(current); | 322 | sbi->oz_pgrp = process_group(current); |
| 269 | sbi->sb = s; | 323 | sbi->sb = s; |
| 270 | sbi->version = 0; | 324 | sbi->version = 0; |
| 271 | sbi->sub_version = 0; | 325 | sbi->sub_version = 0; |
| 326 | sbi->type = 0; | ||
| 327 | sbi->min_proto = 0; | ||
| 328 | sbi->max_proto = 0; | ||
| 272 | mutex_init(&sbi->wq_mutex); | 329 | mutex_init(&sbi->wq_mutex); |
| 273 | spin_lock_init(&sbi->fs_lock); | 330 | spin_lock_init(&sbi->fs_lock); |
| 274 | sbi->queues = NULL; | 331 | sbi->queues = NULL; |
| @@ -285,38 +342,46 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) | |||
| 285 | if (!ino) | 342 | if (!ino) |
| 286 | goto fail_free; | 343 | goto fail_free; |
| 287 | root_inode = autofs4_get_inode(s, ino); | 344 | root_inode = autofs4_get_inode(s, ino); |
| 288 | kfree(ino); | ||
| 289 | if (!root_inode) | 345 | if (!root_inode) |
| 290 | goto fail_free; | 346 | goto fail_ino; |
| 291 | 347 | ||
| 292 | root_inode->i_op = &autofs4_root_inode_operations; | ||
| 293 | root_inode->i_fop = &autofs4_root_operations; | ||
| 294 | root = d_alloc_root(root_inode); | 348 | root = d_alloc_root(root_inode); |
| 295 | pipe = NULL; | ||
| 296 | |||
| 297 | if (!root) | 349 | if (!root) |
| 298 | goto fail_iput; | 350 | goto fail_iput; |
| 351 | pipe = NULL; | ||
| 352 | |||
| 353 | root->d_op = &autofs4_sb_dentry_operations; | ||
| 354 | root->d_fsdata = ino; | ||
| 299 | 355 | ||
| 300 | /* Can this call block? */ | 356 | /* Can this call block? */ |
| 301 | if (parse_options(data, &pipefd, | 357 | if (parse_options(data, &pipefd, |
| 302 | &root_inode->i_uid, &root_inode->i_gid, | 358 | &root_inode->i_uid, &root_inode->i_gid, |
| 303 | &sbi->oz_pgrp, | 359 | &sbi->oz_pgrp, &sbi->type, |
| 304 | &minproto, &maxproto)) { | 360 | &sbi->min_proto, &sbi->max_proto)) { |
| 305 | printk("autofs: called with bogus options\n"); | 361 | printk("autofs: called with bogus options\n"); |
| 306 | goto fail_dput; | 362 | goto fail_dput; |
| 307 | } | 363 | } |
| 308 | 364 | ||
| 365 | root_inode->i_fop = &autofs4_root_operations; | ||
| 366 | root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? | ||
| 367 | &autofs4_direct_root_inode_operations : | ||
| 368 | &autofs4_indirect_root_inode_operations; | ||
| 369 | |||
| 309 | /* Couldn't this be tested earlier? */ | 370 | /* Couldn't this be tested earlier? */ |
| 310 | if (maxproto < AUTOFS_MIN_PROTO_VERSION || | 371 | if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || |
| 311 | minproto > AUTOFS_MAX_PROTO_VERSION) { | 372 | sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { |
| 312 | printk("autofs: kernel does not match daemon version " | 373 | printk("autofs: kernel does not match daemon version " |
| 313 | "daemon (%d, %d) kernel (%d, %d)\n", | 374 | "daemon (%d, %d) kernel (%d, %d)\n", |
| 314 | minproto, maxproto, | 375 | sbi->min_proto, sbi->max_proto, |
| 315 | AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); | 376 | AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); |
| 316 | goto fail_dput; | 377 | goto fail_dput; |
| 317 | } | 378 | } |
| 318 | 379 | ||
| 319 | sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; | 380 | /* Establish highest kernel protocol version */ |
| 381 | if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) | ||
| 382 | sbi->version = AUTOFS_MAX_PROTO_VERSION; | ||
| 383 | else | ||
| 384 | sbi->version = sbi->max_proto; | ||
| 320 | sbi->sub_version = AUTOFS_PROTO_SUBVERSION; | 385 | sbi->sub_version = AUTOFS_PROTO_SUBVERSION; |
| 321 | 386 | ||
| 322 | DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); | 387 | DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); |
| @@ -329,6 +394,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) | |||
| 329 | if ( !pipe->f_op || !pipe->f_op->write ) | 394 | if ( !pipe->f_op || !pipe->f_op->write ) |
| 330 | goto fail_fput; | 395 | goto fail_fput; |
| 331 | sbi->pipe = pipe; | 396 | sbi->pipe = pipe; |
| 397 | sbi->pipefd = pipefd; | ||
| 332 | 398 | ||
| 333 | /* | 399 | /* |
| 334 | * Take a reference to the root dentry so we get a chance to | 400 | * Take a reference to the root dentry so we get a chance to |
| @@ -356,6 +422,8 @@ fail_dput: | |||
| 356 | fail_iput: | 422 | fail_iput: |
| 357 | printk("autofs: get root dentry failed\n"); | 423 | printk("autofs: get root dentry failed\n"); |
| 358 | iput(root_inode); | 424 | iput(root_inode); |
| 425 | fail_ino: | ||
| 426 | kfree(ino); | ||
| 359 | fail_free: | 427 | fail_free: |
| 360 | kfree(sbi); | 428 | kfree(sbi); |
| 361 | fail_unlock: | 429 | fail_unlock: |
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 62d8d4acb8bb..c8fe43a475e2 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | * | 4 | * |
| 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved | 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved |
| 6 | * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> | 6 | * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> |
| 7 | * Copyright 2001-2003 Ian Kent <raven@themaw.net> | 7 | * Copyright 2001-2006 Ian Kent <raven@themaw.net> |
| 8 | * | 8 | * |
| 9 | * This file is part of the Linux kernel and is made available under | 9 | * This file is part of the Linux kernel and is made available under |
| 10 | * the terms of the GNU General Public License, version 2, or at your | 10 | * the terms of the GNU General Public License, version 2, or at your |
| @@ -30,7 +30,7 @@ static int autofs4_dir_close(struct inode *inode, struct file *file); | |||
| 30 | static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); | 30 | static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); |
| 31 | static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); | 31 | static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); |
| 32 | static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); | 32 | static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); |
| 33 | static int autofs4_dcache_readdir(struct file *, void *, filldir_t); | 33 | static void *autofs4_follow_link(struct dentry *, struct nameidata *); |
| 34 | 34 | ||
| 35 | struct file_operations autofs4_root_operations = { | 35 | struct file_operations autofs4_root_operations = { |
| 36 | .open = dcache_dir_open, | 36 | .open = dcache_dir_open, |
| @@ -47,7 +47,7 @@ struct file_operations autofs4_dir_operations = { | |||
| 47 | .readdir = autofs4_dir_readdir, | 47 | .readdir = autofs4_dir_readdir, |
| 48 | }; | 48 | }; |
| 49 | 49 | ||
| 50 | struct inode_operations autofs4_root_inode_operations = { | 50 | struct inode_operations autofs4_indirect_root_inode_operations = { |
| 51 | .lookup = autofs4_lookup, | 51 | .lookup = autofs4_lookup, |
| 52 | .unlink = autofs4_dir_unlink, | 52 | .unlink = autofs4_dir_unlink, |
| 53 | .symlink = autofs4_dir_symlink, | 53 | .symlink = autofs4_dir_symlink, |
| @@ -55,6 +55,14 @@ struct inode_operations autofs4_root_inode_operations = { | |||
| 55 | .rmdir = autofs4_dir_rmdir, | 55 | .rmdir = autofs4_dir_rmdir, |
| 56 | }; | 56 | }; |
| 57 | 57 | ||
| 58 | struct inode_operations autofs4_direct_root_inode_operations = { | ||
| 59 | .lookup = autofs4_lookup, | ||
| 60 | .unlink = autofs4_dir_unlink, | ||
| 61 | .mkdir = autofs4_dir_mkdir, | ||
| 62 | .rmdir = autofs4_dir_rmdir, | ||
| 63 | .follow_link = autofs4_follow_link, | ||
| 64 | }; | ||
| 65 | |||
| 58 | struct inode_operations autofs4_dir_inode_operations = { | 66 | struct inode_operations autofs4_dir_inode_operations = { |
| 59 | .lookup = autofs4_lookup, | 67 | .lookup = autofs4_lookup, |
| 60 | .unlink = autofs4_dir_unlink, | 68 | .unlink = autofs4_dir_unlink, |
| @@ -82,87 +90,7 @@ static int autofs4_root_readdir(struct file *file, void *dirent, | |||
| 82 | 90 | ||
| 83 | DPRINTK("needs_reghost = %d", sbi->needs_reghost); | 91 | DPRINTK("needs_reghost = %d", sbi->needs_reghost); |
| 84 | 92 | ||
| 85 | return autofs4_dcache_readdir(file, dirent, filldir); | 93 | return dcache_readdir(file, dirent, filldir); |
| 86 | } | ||
| 87 | |||
| 88 | /* Update usage from here to top of tree, so that scan of | ||
| 89 | top-level directories will give a useful result */ | ||
| 90 | static void autofs4_update_usage(struct vfsmount *mnt, struct dentry *dentry) | ||
| 91 | { | ||
| 92 | struct dentry *top = dentry->d_sb->s_root; | ||
| 93 | |||
| 94 | spin_lock(&dcache_lock); | ||
| 95 | for(; dentry != top; dentry = dentry->d_parent) { | ||
| 96 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | ||
| 97 | |||
| 98 | if (ino) { | ||
| 99 | touch_atime(mnt, dentry); | ||
| 100 | ino->last_used = jiffies; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | spin_unlock(&dcache_lock); | ||
| 104 | } | ||
| 105 | |||
| 106 | /* | ||
| 107 | * From 2.4 kernel readdir.c | ||
| 108 | */ | ||
| 109 | static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) | ||
| 110 | { | ||
| 111 | int i; | ||
| 112 | struct dentry *dentry = filp->f_dentry; | ||
| 113 | |||
| 114 | i = filp->f_pos; | ||
| 115 | switch (i) { | ||
| 116 | case 0: | ||
| 117 | if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) | ||
| 118 | break; | ||
| 119 | i++; | ||
| 120 | filp->f_pos++; | ||
| 121 | /* fallthrough */ | ||
| 122 | case 1: | ||
| 123 | if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) | ||
| 124 | break; | ||
| 125 | i++; | ||
| 126 | filp->f_pos++; | ||
| 127 | /* fallthrough */ | ||
| 128 | default: { | ||
| 129 | struct list_head *list; | ||
| 130 | int j = i-2; | ||
| 131 | |||
| 132 | spin_lock(&dcache_lock); | ||
| 133 | list = dentry->d_subdirs.next; | ||
| 134 | |||
| 135 | for (;;) { | ||
| 136 | if (list == &dentry->d_subdirs) { | ||
| 137 | spin_unlock(&dcache_lock); | ||
| 138 | return 0; | ||
| 139 | } | ||
| 140 | if (!j) | ||
| 141 | break; | ||
| 142 | j--; | ||
| 143 | list = list->next; | ||
| 144 | } | ||
| 145 | |||
| 146 | while(1) { | ||
| 147 | struct dentry *de = list_entry(list, | ||
| 148 | struct dentry, d_u.d_child); | ||
| 149 | |||
| 150 | if (!d_unhashed(de) && de->d_inode) { | ||
| 151 | spin_unlock(&dcache_lock); | ||
| 152 | if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) | ||
| 153 | break; | ||
| 154 | spin_lock(&dcache_lock); | ||
| 155 | } | ||
| 156 | filp->f_pos++; | ||
| 157 | list = list->next; | ||
| 158 | if (list != &dentry->d_subdirs) | ||
| 159 | continue; | ||
| 160 | spin_unlock(&dcache_lock); | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | return 0; | ||
| 166 | } | 94 | } |
| 167 | 95 | ||
| 168 | static int autofs4_dir_open(struct inode *inode, struct file *file) | 96 | static int autofs4_dir_open(struct inode *inode, struct file *file) |
| @@ -170,8 +98,16 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) | |||
| 170 | struct dentry *dentry = file->f_dentry; | 98 | struct dentry *dentry = file->f_dentry; |
| 171 | struct vfsmount *mnt = file->f_vfsmnt; | 99 | struct vfsmount *mnt = file->f_vfsmnt; |
| 172 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | 100 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
| 101 | struct dentry *cursor; | ||
| 173 | int status; | 102 | int status; |
| 174 | 103 | ||
| 104 | status = dcache_dir_open(inode, file); | ||
| 105 | if (status) | ||
| 106 | goto out; | ||
| 107 | |||
| 108 | cursor = file->private_data; | ||
| 109 | cursor->d_fsdata = NULL; | ||
| 110 | |||
| 175 | DPRINTK("file=%p dentry=%p %.*s", | 111 | DPRINTK("file=%p dentry=%p %.*s", |
| 176 | file, dentry, dentry->d_name.len, dentry->d_name.name); | 112 | file, dentry, dentry->d_name.len, dentry->d_name.name); |
| 177 | 113 | ||
| @@ -180,12 +116,15 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) | |||
| 180 | 116 | ||
| 181 | if (autofs4_ispending(dentry)) { | 117 | if (autofs4_ispending(dentry)) { |
| 182 | DPRINTK("dentry busy"); | 118 | DPRINTK("dentry busy"); |
| 183 | return -EBUSY; | 119 | dcache_dir_close(inode, file); |
| 120 | status = -EBUSY; | ||
| 121 | goto out; | ||
| 184 | } | 122 | } |
| 185 | 123 | ||
| 124 | status = -ENOENT; | ||
| 186 | if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { | 125 | if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { |
| 187 | struct nameidata nd; | 126 | struct nameidata nd; |
| 188 | int empty; | 127 | int empty, ret; |
| 189 | 128 | ||
| 190 | /* In case there are stale directory dentrys from a failed mount */ | 129 | /* In case there are stale directory dentrys from a failed mount */ |
| 191 | spin_lock(&dcache_lock); | 130 | spin_lock(&dcache_lock); |
| @@ -195,13 +134,13 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) | |||
| 195 | if (!empty) | 134 | if (!empty) |
| 196 | d_invalidate(dentry); | 135 | d_invalidate(dentry); |
| 197 | 136 | ||
| 198 | nd.dentry = dentry; | ||
| 199 | nd.mnt = mnt; | ||
| 200 | nd.flags = LOOKUP_DIRECTORY; | 137 | nd.flags = LOOKUP_DIRECTORY; |
| 201 | status = (dentry->d_op->d_revalidate)(dentry, &nd); | 138 | ret = (dentry->d_op->d_revalidate)(dentry, &nd); |
| 202 | 139 | ||
| 203 | if (!status) | 140 | if (!ret) { |
| 204 | return -ENOENT; | 141 | dcache_dir_close(inode, file); |
| 142 | goto out; | ||
| 143 | } | ||
| 205 | } | 144 | } |
| 206 | 145 | ||
| 207 | if (d_mountpoint(dentry)) { | 146 | if (d_mountpoint(dentry)) { |
| @@ -212,25 +151,29 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) | |||
| 212 | if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { | 151 | if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { |
| 213 | dput(fp_dentry); | 152 | dput(fp_dentry); |
| 214 | mntput(fp_mnt); | 153 | mntput(fp_mnt); |
| 215 | return -ENOENT; | 154 | dcache_dir_close(inode, file); |
| 155 | goto out; | ||
| 216 | } | 156 | } |
| 217 | 157 | ||
| 218 | fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); | 158 | fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); |
| 219 | status = PTR_ERR(fp); | 159 | status = PTR_ERR(fp); |
| 220 | if (IS_ERR(fp)) { | 160 | if (IS_ERR(fp)) { |
| 221 | file->private_data = NULL; | 161 | dcache_dir_close(inode, file); |
| 222 | return status; | 162 | goto out; |
| 223 | } | 163 | } |
| 224 | file->private_data = fp; | 164 | cursor->d_fsdata = fp; |
| 225 | } | 165 | } |
| 226 | out: | ||
| 227 | return 0; | 166 | return 0; |
| 167 | out: | ||
| 168 | return status; | ||
| 228 | } | 169 | } |
| 229 | 170 | ||
| 230 | static int autofs4_dir_close(struct inode *inode, struct file *file) | 171 | static int autofs4_dir_close(struct inode *inode, struct file *file) |
| 231 | { | 172 | { |
| 232 | struct dentry *dentry = file->f_dentry; | 173 | struct dentry *dentry = file->f_dentry; |
| 233 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | 174 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
| 175 | struct dentry *cursor = file->private_data; | ||
| 176 | int status = 0; | ||
| 234 | 177 | ||
| 235 | DPRINTK("file=%p dentry=%p %.*s", | 178 | DPRINTK("file=%p dentry=%p %.*s", |
| 236 | file, dentry, dentry->d_name.len, dentry->d_name.name); | 179 | file, dentry, dentry->d_name.len, dentry->d_name.name); |
| @@ -240,26 +183,28 @@ static int autofs4_dir_close(struct inode *inode, struct file *file) | |||
| 240 | 183 | ||
| 241 | if (autofs4_ispending(dentry)) { | 184 | if (autofs4_ispending(dentry)) { |
| 242 | DPRINTK("dentry busy"); | 185 | DPRINTK("dentry busy"); |
| 243 | return -EBUSY; | 186 | status = -EBUSY; |
| 187 | goto out; | ||
| 244 | } | 188 | } |
| 245 | 189 | ||
| 246 | if (d_mountpoint(dentry)) { | 190 | if (d_mountpoint(dentry)) { |
| 247 | struct file *fp = file->private_data; | 191 | struct file *fp = cursor->d_fsdata; |
| 248 | 192 | if (!fp) { | |
| 249 | if (!fp) | 193 | status = -ENOENT; |
| 250 | return -ENOENT; | 194 | goto out; |
| 251 | 195 | } | |
| 252 | filp_close(fp, current->files); | 196 | filp_close(fp, current->files); |
| 253 | file->private_data = NULL; | ||
| 254 | } | 197 | } |
| 255 | out: | 198 | out: |
| 256 | return 0; | 199 | dcache_dir_close(inode, file); |
| 200 | return status; | ||
| 257 | } | 201 | } |
| 258 | 202 | ||
| 259 | static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) | 203 | static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) |
| 260 | { | 204 | { |
| 261 | struct dentry *dentry = file->f_dentry; | 205 | struct dentry *dentry = file->f_dentry; |
| 262 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | 206 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
| 207 | struct dentry *cursor = file->private_data; | ||
| 263 | int status; | 208 | int status; |
| 264 | 209 | ||
| 265 | DPRINTK("file=%p dentry=%p %.*s", | 210 | DPRINTK("file=%p dentry=%p %.*s", |
| @@ -274,7 +219,7 @@ static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldi | |||
| 274 | } | 219 | } |
| 275 | 220 | ||
| 276 | if (d_mountpoint(dentry)) { | 221 | if (d_mountpoint(dentry)) { |
| 277 | struct file *fp = file->private_data; | 222 | struct file *fp = cursor->d_fsdata; |
| 278 | 223 | ||
| 279 | if (!fp) | 224 | if (!fp) |
| 280 | return -ENOENT; | 225 | return -ENOENT; |
| @@ -289,27 +234,26 @@ static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldi | |||
| 289 | return status; | 234 | return status; |
| 290 | } | 235 | } |
| 291 | out: | 236 | out: |
| 292 | return autofs4_dcache_readdir(file, dirent, filldir); | 237 | return dcache_readdir(file, dirent, filldir); |
| 293 | } | 238 | } |
| 294 | 239 | ||
| 295 | static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int flags) | 240 | static int try_to_fill_dentry(struct dentry *dentry, int flags) |
| 296 | { | 241 | { |
| 297 | struct super_block *sb = mnt->mnt_sb; | 242 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
| 298 | struct autofs_sb_info *sbi = autofs4_sbi(sb); | 243 | struct autofs_info *ino = autofs4_dentry_ino(dentry); |
| 299 | struct autofs_info *de_info = autofs4_dentry_ino(dentry); | ||
| 300 | int status = 0; | 244 | int status = 0; |
| 301 | 245 | ||
| 302 | /* Block on any pending expiry here; invalidate the dentry | 246 | /* Block on any pending expiry here; invalidate the dentry |
| 303 | when expiration is done to trigger mount request with a new | 247 | when expiration is done to trigger mount request with a new |
| 304 | dentry */ | 248 | dentry */ |
| 305 | if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { | 249 | if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { |
| 306 | DPRINTK("waiting for expire %p name=%.*s", | 250 | DPRINTK("waiting for expire %p name=%.*s", |
| 307 | dentry, dentry->d_name.len, dentry->d_name.name); | 251 | dentry, dentry->d_name.len, dentry->d_name.name); |
| 308 | 252 | ||
| 309 | status = autofs4_wait(sbi, dentry, NFY_NONE); | 253 | status = autofs4_wait(sbi, dentry, NFY_NONE); |
| 310 | 254 | ||
| 311 | DPRINTK("expire done status=%d", status); | 255 | DPRINTK("expire done status=%d", status); |
| 312 | 256 | ||
| 313 | /* | 257 | /* |
| 314 | * If the directory still exists the mount request must | 258 | * If the directory still exists the mount request must |
| 315 | * continue otherwise it can't be followed at the right | 259 | * continue otherwise it can't be followed at the right |
| @@ -317,34 +261,36 @@ static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int f | |||
| 317 | */ | 261 | */ |
| 318 | status = d_invalidate(dentry); | 262 | status = d_invalidate(dentry); |
| 319 | if (status != -EBUSY) | 263 | if (status != -EBUSY) |
| 320 | return 0; | 264 | return -ENOENT; |
| 321 | } | 265 | } |
| 322 | 266 | ||
| 323 | DPRINTK("dentry=%p %.*s ino=%p", | 267 | DPRINTK("dentry=%p %.*s ino=%p", |
| 324 | dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); | 268 | dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); |
| 325 | 269 | ||
| 326 | /* Wait for a pending mount, triggering one if there isn't one already */ | 270 | /* |
| 271 | * Wait for a pending mount, triggering one if there | ||
| 272 | * isn't one already | ||
| 273 | */ | ||
| 327 | if (dentry->d_inode == NULL) { | 274 | if (dentry->d_inode == NULL) { |
| 328 | DPRINTK("waiting for mount name=%.*s", | 275 | DPRINTK("waiting for mount name=%.*s", |
| 329 | dentry->d_name.len, dentry->d_name.name); | 276 | dentry->d_name.len, dentry->d_name.name); |
| 330 | 277 | ||
| 331 | status = autofs4_wait(sbi, dentry, NFY_MOUNT); | 278 | status = autofs4_wait(sbi, dentry, NFY_MOUNT); |
| 332 | 279 | ||
| 333 | DPRINTK("mount done status=%d", status); | 280 | DPRINTK("mount done status=%d", status); |
| 334 | 281 | ||
| 335 | if (status && dentry->d_inode) | 282 | if (status && dentry->d_inode) |
| 336 | return 0; /* Try to get the kernel to invalidate this dentry */ | 283 | return status; /* Try to get the kernel to invalidate this dentry */ |
| 337 | 284 | ||
| 338 | /* Turn this into a real negative dentry? */ | 285 | /* Turn this into a real negative dentry? */ |
| 339 | if (status == -ENOENT) { | 286 | if (status == -ENOENT) { |
| 340 | dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; | ||
| 341 | spin_lock(&dentry->d_lock); | 287 | spin_lock(&dentry->d_lock); |
| 342 | dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; | 288 | dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; |
| 343 | spin_unlock(&dentry->d_lock); | 289 | spin_unlock(&dentry->d_lock); |
| 344 | return 1; | 290 | return status; |
| 345 | } else if (status) { | 291 | } else if (status) { |
| 346 | /* Return a negative dentry, but leave it "pending" */ | 292 | /* Return a negative dentry, but leave it "pending" */ |
| 347 | return 1; | 293 | return status; |
| 348 | } | 294 | } |
| 349 | /* Trigger mount for path component or follow link */ | 295 | /* Trigger mount for path component or follow link */ |
| 350 | } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || | 296 | } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || |
| @@ -363,19 +309,87 @@ static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int f | |||
| 363 | spin_lock(&dentry->d_lock); | 309 | spin_lock(&dentry->d_lock); |
| 364 | dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; | 310 | dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; |
| 365 | spin_unlock(&dentry->d_lock); | 311 | spin_unlock(&dentry->d_lock); |
| 366 | return 0; | 312 | return status; |
| 367 | } | 313 | } |
| 368 | } | 314 | } |
| 369 | 315 | ||
| 370 | /* We don't update the usages for the autofs daemon itself, this | 316 | /* Initialize expiry counter after successful mount */ |
| 371 | is necessary for recursive autofs mounts */ | 317 | if (ino) |
| 372 | if (!autofs4_oz_mode(sbi)) | 318 | ino->last_used = jiffies; |
| 373 | autofs4_update_usage(mnt, dentry); | ||
| 374 | 319 | ||
| 375 | spin_lock(&dentry->d_lock); | 320 | spin_lock(&dentry->d_lock); |
| 376 | dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; | 321 | dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; |
| 377 | spin_unlock(&dentry->d_lock); | 322 | spin_unlock(&dentry->d_lock); |
| 378 | return 1; | 323 | return status; |
| 324 | } | ||
| 325 | |||
| 326 | /* For autofs direct mounts the follow link triggers the mount */ | ||
| 327 | static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
| 328 | { | ||
| 329 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | ||
| 330 | int oz_mode = autofs4_oz_mode(sbi); | ||
| 331 | unsigned int lookup_type; | ||
| 332 | int status; | ||
| 333 | |||
| 334 | DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", | ||
| 335 | dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, | ||
| 336 | nd->flags); | ||
| 337 | |||
| 338 | /* If it's our master or we shouldn't trigger a mount we're done */ | ||
| 339 | lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); | ||
| 340 | if (oz_mode || !lookup_type) | ||
| 341 | goto done; | ||
| 342 | |||
| 343 | /* | ||
| 344 | * If a request is pending wait for it. | ||
| 345 | * If it's a mount then it won't be expired till at least | ||
| 346 | * a liitle later and if it's an expire then we might need | ||
| 347 | * to mount it again. | ||
| 348 | */ | ||
| 349 | if (autofs4_ispending(dentry)) { | ||
| 350 | DPRINTK("waiting for active request %p name=%.*s", | ||
| 351 | dentry, dentry->d_name.len, dentry->d_name.name); | ||
| 352 | |||
| 353 | status = autofs4_wait(sbi, dentry, NFY_NONE); | ||
| 354 | |||
| 355 | DPRINTK("request done status=%d", status); | ||
| 356 | } | ||
| 357 | |||
| 358 | /* | ||
| 359 | * If the dentry contains directories then it is an | ||
| 360 | * autofs multi-mount with no root mount offset. So | ||
| 361 | * don't try to mount it again. | ||
| 362 | */ | ||
| 363 | spin_lock(&dcache_lock); | ||
| 364 | if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { | ||
| 365 | spin_unlock(&dcache_lock); | ||
| 366 | |||
| 367 | status = try_to_fill_dentry(dentry, 0); | ||
| 368 | if (status) | ||
| 369 | goto out_error; | ||
| 370 | |||
| 371 | /* | ||
| 372 | * The mount succeeded but if there is no root mount | ||
| 373 | * it must be an autofs multi-mount with no root offset | ||
| 374 | * so we don't need to follow the mount. | ||
| 375 | */ | ||
| 376 | if (d_mountpoint(dentry)) { | ||
| 377 | if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { | ||
| 378 | status = -ENOENT; | ||
| 379 | goto out_error; | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | goto done; | ||
| 384 | } | ||
| 385 | spin_unlock(&dcache_lock); | ||
| 386 | |||
| 387 | done: | ||
| 388 | return NULL; | ||
| 389 | |||
| 390 | out_error: | ||
| 391 | path_release(nd); | ||
| 392 | return ERR_PTR(status); | ||
| 379 | } | 393 | } |
| 380 | 394 | ||
| 381 | /* | 395 | /* |
| @@ -384,47 +398,43 @@ static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int f | |||
| 384 | * yet completely filled in, and revalidate has to delay such | 398 | * yet completely filled in, and revalidate has to delay such |
| 385 | * lookups.. | 399 | * lookups.. |
| 386 | */ | 400 | */ |
| 387 | static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) | 401 | static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 388 | { | 402 | { |
| 389 | struct inode * dir = dentry->d_parent->d_inode; | 403 | struct inode *dir = dentry->d_parent->d_inode; |
| 390 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); | 404 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); |
| 391 | int oz_mode = autofs4_oz_mode(sbi); | 405 | int oz_mode = autofs4_oz_mode(sbi); |
| 392 | int flags = nd ? nd->flags : 0; | 406 | int flags = nd ? nd->flags : 0; |
| 393 | int status = 1; | 407 | int status = 0; |
| 394 | 408 | ||
| 395 | /* Pending dentry */ | 409 | /* Pending dentry */ |
| 396 | if (autofs4_ispending(dentry)) { | 410 | if (autofs4_ispending(dentry)) { |
| 397 | if (!oz_mode) | 411 | if (!oz_mode) |
| 398 | status = try_to_fill_dentry(nd->mnt, dentry, flags); | 412 | status = try_to_fill_dentry(dentry, flags); |
| 399 | return status; | 413 | return !status; |
| 400 | } | 414 | } |
| 401 | 415 | ||
| 402 | /* Negative dentry.. invalidate if "old" */ | 416 | /* Negative dentry.. invalidate if "old" */ |
| 403 | if (dentry->d_inode == NULL) | 417 | if (dentry->d_inode == NULL) |
| 404 | return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); | 418 | return 0; |
| 405 | 419 | ||
| 406 | /* Check for a non-mountpoint directory with no contents */ | 420 | /* Check for a non-mountpoint directory with no contents */ |
| 407 | spin_lock(&dcache_lock); | 421 | spin_lock(&dcache_lock); |
| 408 | if (S_ISDIR(dentry->d_inode->i_mode) && | 422 | if (S_ISDIR(dentry->d_inode->i_mode) && |
| 409 | !d_mountpoint(dentry) && | 423 | !d_mountpoint(dentry) && |
| 410 | list_empty(&dentry->d_subdirs)) { | 424 | __simple_empty(dentry)) { |
| 411 | DPRINTK("dentry=%p %.*s, emptydir", | 425 | DPRINTK("dentry=%p %.*s, emptydir", |
| 412 | dentry, dentry->d_name.len, dentry->d_name.name); | 426 | dentry, dentry->d_name.len, dentry->d_name.name); |
| 413 | spin_unlock(&dcache_lock); | 427 | spin_unlock(&dcache_lock); |
| 414 | if (!oz_mode) | 428 | if (!oz_mode) |
| 415 | status = try_to_fill_dentry(nd->mnt, dentry, flags); | 429 | status = try_to_fill_dentry(dentry, flags); |
| 416 | return status; | 430 | return !status; |
| 417 | } | 431 | } |
| 418 | spin_unlock(&dcache_lock); | 432 | spin_unlock(&dcache_lock); |
| 419 | 433 | ||
| 420 | /* Update the usage list */ | ||
| 421 | if (!oz_mode) | ||
| 422 | autofs4_update_usage(nd->mnt, dentry); | ||
| 423 | |||
| 424 | return 1; | 434 | return 1; |
| 425 | } | 435 | } |
| 426 | 436 | ||
| 427 | static void autofs4_dentry_release(struct dentry *de) | 437 | void autofs4_dentry_release(struct dentry *de) |
| 428 | { | 438 | { |
| 429 | struct autofs_info *inf; | 439 | struct autofs_info *inf; |
| 430 | 440 | ||
| @@ -462,12 +472,13 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
| 462 | DPRINTK("name = %.*s", | 472 | DPRINTK("name = %.*s", |
| 463 | dentry->d_name.len, dentry->d_name.name); | 473 | dentry->d_name.len, dentry->d_name.name); |
| 464 | 474 | ||
| 475 | /* File name too long to exist */ | ||
| 465 | if (dentry->d_name.len > NAME_MAX) | 476 | if (dentry->d_name.len > NAME_MAX) |
| 466 | return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ | 477 | return ERR_PTR(-ENAMETOOLONG); |
| 467 | 478 | ||
| 468 | sbi = autofs4_sbi(dir->i_sb); | 479 | sbi = autofs4_sbi(dir->i_sb); |
| 469 | |||
| 470 | oz_mode = autofs4_oz_mode(sbi); | 480 | oz_mode = autofs4_oz_mode(sbi); |
| 481 | |||
| 471 | DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", | 482 | DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", |
| 472 | current->pid, process_group(current), sbi->catatonic, oz_mode); | 483 | current->pid, process_group(current), sbi->catatonic, oz_mode); |
| 473 | 484 | ||
| @@ -519,7 +530,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
| 519 | * doesn't do the right thing for all system calls, but it should | 530 | * doesn't do the right thing for all system calls, but it should |
| 520 | * be OK for the operations we permit from an autofs. | 531 | * be OK for the operations we permit from an autofs. |
| 521 | */ | 532 | */ |
| 522 | if ( dentry->d_inode && d_unhashed(dentry) ) | 533 | if (dentry->d_inode && d_unhashed(dentry)) |
| 523 | return ERR_PTR(-ENOENT); | 534 | return ERR_PTR(-ENOENT); |
| 524 | 535 | ||
| 525 | return NULL; | 536 | return NULL; |
| @@ -531,6 +542,7 @@ static int autofs4_dir_symlink(struct inode *dir, | |||
| 531 | { | 542 | { |
| 532 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); | 543 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); |
| 533 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | 544 | struct autofs_info *ino = autofs4_dentry_ino(dentry); |
| 545 | struct autofs_info *p_ino; | ||
| 534 | struct inode *inode; | 546 | struct inode *inode; |
| 535 | char *cp; | 547 | char *cp; |
| 536 | 548 | ||
| @@ -564,6 +576,10 @@ static int autofs4_dir_symlink(struct inode *dir, | |||
| 564 | 576 | ||
| 565 | dentry->d_fsdata = ino; | 577 | dentry->d_fsdata = ino; |
| 566 | ino->dentry = dget(dentry); | 578 | ino->dentry = dget(dentry); |
| 579 | atomic_inc(&ino->count); | ||
| 580 | p_ino = autofs4_dentry_ino(dentry->d_parent); | ||
| 581 | if (p_ino && dentry->d_parent != dentry) | ||
| 582 | atomic_inc(&p_ino->count); | ||
| 567 | ino->inode = inode; | 583 | ino->inode = inode; |
| 568 | 584 | ||
| 569 | dir->i_mtime = CURRENT_TIME; | 585 | dir->i_mtime = CURRENT_TIME; |
| @@ -590,11 +606,17 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) | |||
| 590 | { | 606 | { |
| 591 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); | 607 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); |
| 592 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | 608 | struct autofs_info *ino = autofs4_dentry_ino(dentry); |
| 609 | struct autofs_info *p_ino; | ||
| 593 | 610 | ||
| 594 | /* This allows root to remove symlinks */ | 611 | /* This allows root to remove symlinks */ |
| 595 | if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) | 612 | if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) |
| 596 | return -EACCES; | 613 | return -EACCES; |
| 597 | 614 | ||
| 615 | if (atomic_dec_and_test(&ino->count)) { | ||
| 616 | p_ino = autofs4_dentry_ino(dentry->d_parent); | ||
| 617 | if (p_ino && dentry->d_parent != dentry) | ||
| 618 | atomic_dec(&p_ino->count); | ||
| 619 | } | ||
| 598 | dput(ino->dentry); | 620 | dput(ino->dentry); |
| 599 | 621 | ||
| 600 | dentry->d_inode->i_size = 0; | 622 | dentry->d_inode->i_size = 0; |
| @@ -611,6 +633,7 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) | |||
| 611 | { | 633 | { |
| 612 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); | 634 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); |
| 613 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | 635 | struct autofs_info *ino = autofs4_dentry_ino(dentry); |
| 636 | struct autofs_info *p_ino; | ||
| 614 | 637 | ||
| 615 | if (!autofs4_oz_mode(sbi)) | 638 | if (!autofs4_oz_mode(sbi)) |
| 616 | return -EACCES; | 639 | return -EACCES; |
| @@ -625,8 +648,12 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) | |||
| 625 | spin_unlock(&dentry->d_lock); | 648 | spin_unlock(&dentry->d_lock); |
| 626 | spin_unlock(&dcache_lock); | 649 | spin_unlock(&dcache_lock); |
| 627 | 650 | ||
| 651 | if (atomic_dec_and_test(&ino->count)) { | ||
| 652 | p_ino = autofs4_dentry_ino(dentry->d_parent); | ||
| 653 | if (p_ino && dentry->d_parent != dentry) | ||
| 654 | atomic_dec(&p_ino->count); | ||
| 655 | } | ||
| 628 | dput(ino->dentry); | 656 | dput(ino->dentry); |
| 629 | |||
| 630 | dentry->d_inode->i_size = 0; | 657 | dentry->d_inode->i_size = 0; |
| 631 | dentry->d_inode->i_nlink = 0; | 658 | dentry->d_inode->i_nlink = 0; |
| 632 | 659 | ||
| @@ -640,6 +667,7 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
| 640 | { | 667 | { |
| 641 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); | 668 | struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); |
| 642 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | 669 | struct autofs_info *ino = autofs4_dentry_ino(dentry); |
| 670 | struct autofs_info *p_ino; | ||
| 643 | struct inode *inode; | 671 | struct inode *inode; |
| 644 | 672 | ||
| 645 | if ( !autofs4_oz_mode(sbi) ) | 673 | if ( !autofs4_oz_mode(sbi) ) |
| @@ -662,6 +690,10 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
| 662 | 690 | ||
| 663 | dentry->d_fsdata = ino; | 691 | dentry->d_fsdata = ino; |
| 664 | ino->dentry = dget(dentry); | 692 | ino->dentry = dget(dentry); |
| 693 | atomic_inc(&ino->count); | ||
| 694 | p_ino = autofs4_dentry_ino(dentry->d_parent); | ||
| 695 | if (p_ino && dentry->d_parent != dentry) | ||
| 696 | atomic_inc(&p_ino->count); | ||
| 665 | ino->inode = inode; | 697 | ino->inode = inode; |
| 666 | dir->i_nlink++; | 698 | dir->i_nlink++; |
| 667 | dir->i_mtime = CURRENT_TIME; | 699 | dir->i_mtime = CURRENT_TIME; |
| @@ -745,7 +777,7 @@ static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) | |||
| 745 | { | 777 | { |
| 746 | int status = 0; | 778 | int status = 0; |
| 747 | 779 | ||
| 748 | if (may_umount(mnt) == 0) | 780 | if (may_umount(mnt)) |
| 749 | status = 1; | 781 | status = 1; |
| 750 | 782 | ||
| 751 | DPRINTK("returning %d", status); | 783 | DPRINTK("returning %d", status); |
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index be78e9378c03..142ab6aa2aa1 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * linux/fs/autofs/waitq.c | 3 | * linux/fs/autofs/waitq.c |
| 4 | * | 4 | * |
| 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved | 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved |
| 6 | * Copyright 2001-2003 Ian Kent <raven@themaw.net> | 6 | * Copyright 2001-2006 Ian Kent <raven@themaw.net> |
| 7 | * | 7 | * |
| 8 | * This file is part of the Linux kernel and is made available under | 8 | * This file is part of the Linux kernel and is made available under |
| 9 | * the terms of the GNU General Public License, version 2, or at your | 9 | * the terms of the GNU General Public License, version 2, or at your |
| @@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi) | |||
| 33 | sbi->catatonic = 1; | 33 | sbi->catatonic = 1; |
| 34 | wq = sbi->queues; | 34 | wq = sbi->queues; |
| 35 | sbi->queues = NULL; /* Erase all wait queues */ | 35 | sbi->queues = NULL; /* Erase all wait queues */ |
| 36 | while ( wq ) { | 36 | while (wq) { |
| 37 | nwq = wq->next; | 37 | nwq = wq->next; |
| 38 | wq->status = -ENOENT; /* Magic is gone - report failure */ | 38 | wq->status = -ENOENT; /* Magic is gone - report failure */ |
| 39 | kfree(wq->name); | 39 | kfree(wq->name); |
| @@ -45,7 +45,6 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi) | |||
| 45 | fput(sbi->pipe); /* Close the pipe */ | 45 | fput(sbi->pipe); /* Close the pipe */ |
| 46 | sbi->pipe = NULL; | 46 | sbi->pipe = NULL; |
| 47 | } | 47 | } |
| 48 | |||
| 49 | shrink_dcache_sb(sbi->sb); | 48 | shrink_dcache_sb(sbi->sb); |
| 50 | } | 49 | } |
| 51 | 50 | ||
| @@ -98,7 +97,10 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, | |||
| 98 | 97 | ||
| 99 | pkt.hdr.proto_version = sbi->version; | 98 | pkt.hdr.proto_version = sbi->version; |
| 100 | pkt.hdr.type = type; | 99 | pkt.hdr.type = type; |
| 101 | if (type == autofs_ptype_missing) { | 100 | switch (type) { |
| 101 | /* Kernel protocol v4 missing and expire packets */ | ||
| 102 | case autofs_ptype_missing: | ||
| 103 | { | ||
| 102 | struct autofs_packet_missing *mp = &pkt.missing; | 104 | struct autofs_packet_missing *mp = &pkt.missing; |
| 103 | 105 | ||
| 104 | pktsz = sizeof(*mp); | 106 | pktsz = sizeof(*mp); |
| @@ -107,7 +109,10 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, | |||
| 107 | mp->len = wq->len; | 109 | mp->len = wq->len; |
| 108 | memcpy(mp->name, wq->name, wq->len); | 110 | memcpy(mp->name, wq->name, wq->len); |
| 109 | mp->name[wq->len] = '\0'; | 111 | mp->name[wq->len] = '\0'; |
| 110 | } else if (type == autofs_ptype_expire_multi) { | 112 | break; |
| 113 | } | ||
| 114 | case autofs_ptype_expire_multi: | ||
| 115 | { | ||
| 111 | struct autofs_packet_expire_multi *ep = &pkt.expire_multi; | 116 | struct autofs_packet_expire_multi *ep = &pkt.expire_multi; |
| 112 | 117 | ||
| 113 | pktsz = sizeof(*ep); | 118 | pktsz = sizeof(*ep); |
| @@ -116,7 +121,34 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, | |||
| 116 | ep->len = wq->len; | 121 | ep->len = wq->len; |
| 117 | memcpy(ep->name, wq->name, wq->len); | 122 | memcpy(ep->name, wq->name, wq->len); |
| 118 | ep->name[wq->len] = '\0'; | 123 | ep->name[wq->len] = '\0'; |
| 119 | } else { | 124 | break; |
| 125 | } | ||
| 126 | /* | ||
| 127 | * Kernel protocol v5 packet for handling indirect and direct | ||
| 128 | * mount missing and expire requests | ||
| 129 | */ | ||
| 130 | case autofs_ptype_missing_indirect: | ||
| 131 | case autofs_ptype_expire_indirect: | ||
| 132 | case autofs_ptype_missing_direct: | ||
| 133 | case autofs_ptype_expire_direct: | ||
| 134 | { | ||
| 135 | struct autofs_v5_packet *packet = &pkt.v5_packet; | ||
| 136 | |||
| 137 | pktsz = sizeof(*packet); | ||
| 138 | |||
| 139 | packet->wait_queue_token = wq->wait_queue_token; | ||
| 140 | packet->len = wq->len; | ||
| 141 | memcpy(packet->name, wq->name, wq->len); | ||
| 142 | packet->name[wq->len] = '\0'; | ||
| 143 | packet->dev = wq->dev; | ||
| 144 | packet->ino = wq->ino; | ||
| 145 | packet->uid = wq->uid; | ||
| 146 | packet->gid = wq->gid; | ||
| 147 | packet->pid = wq->pid; | ||
| 148 | packet->tgid = wq->tgid; | ||
| 149 | break; | ||
| 150 | } | ||
| 151 | default: | ||
| 120 | printk("autofs4_notify_daemon: bad type %d!\n", type); | 152 | printk("autofs4_notify_daemon: bad type %d!\n", type); |
| 121 | return; | 153 | return; |
| 122 | } | 154 | } |
| @@ -162,21 +194,29 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, | |||
| 162 | { | 194 | { |
| 163 | struct autofs_wait_queue *wq; | 195 | struct autofs_wait_queue *wq; |
| 164 | char *name; | 196 | char *name; |
| 165 | int len, status; | 197 | unsigned int len = 0; |
| 198 | unsigned int hash = 0; | ||
| 199 | int status; | ||
| 166 | 200 | ||
| 167 | /* In catatonic mode, we don't wait for nobody */ | 201 | /* In catatonic mode, we don't wait for nobody */ |
| 168 | if ( sbi->catatonic ) | 202 | if (sbi->catatonic) |
| 169 | return -ENOENT; | 203 | return -ENOENT; |
| 170 | 204 | ||
| 171 | name = kmalloc(NAME_MAX + 1, GFP_KERNEL); | 205 | name = kmalloc(NAME_MAX + 1, GFP_KERNEL); |
| 172 | if (!name) | 206 | if (!name) |
| 173 | return -ENOMEM; | 207 | return -ENOMEM; |
| 174 | 208 | ||
| 175 | len = autofs4_getpath(sbi, dentry, &name); | 209 | /* If this is a direct mount request create a dummy name */ |
| 176 | if (!len) { | 210 | if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) |
| 177 | kfree(name); | 211 | len = sprintf(name, "%p", dentry); |
| 178 | return -ENOENT; | 212 | else { |
| 213 | len = autofs4_getpath(sbi, dentry, &name); | ||
| 214 | if (!len) { | ||
| 215 | kfree(name); | ||
| 216 | return -ENOENT; | ||
| 217 | } | ||
| 179 | } | 218 | } |
| 219 | hash = full_name_hash(name, len); | ||
| 180 | 220 | ||
| 181 | if (mutex_lock_interruptible(&sbi->wq_mutex)) { | 221 | if (mutex_lock_interruptible(&sbi->wq_mutex)) { |
| 182 | kfree(name); | 222 | kfree(name); |
| @@ -190,7 +230,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, | |||
| 190 | break; | 230 | break; |
| 191 | } | 231 | } |
| 192 | 232 | ||
| 193 | if ( !wq ) { | 233 | if (!wq) { |
| 194 | /* Can't wait for an expire if there's no mount */ | 234 | /* Can't wait for an expire if there's no mount */ |
| 195 | if (notify == NFY_NONE && !d_mountpoint(dentry)) { | 235 | if (notify == NFY_NONE && !d_mountpoint(dentry)) { |
| 196 | kfree(name); | 236 | kfree(name); |
| @@ -200,7 +240,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, | |||
| 200 | 240 | ||
| 201 | /* Create a new wait queue */ | 241 | /* Create a new wait queue */ |
| 202 | wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); | 242 | wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); |
| 203 | if ( !wq ) { | 243 | if (!wq) { |
| 204 | kfree(name); | 244 | kfree(name); |
| 205 | mutex_unlock(&sbi->wq_mutex); | 245 | mutex_unlock(&sbi->wq_mutex); |
| 206 | return -ENOMEM; | 246 | return -ENOMEM; |
| @@ -212,12 +252,18 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, | |||
| 212 | wq->next = sbi->queues; | 252 | wq->next = sbi->queues; |
| 213 | sbi->queues = wq; | 253 | sbi->queues = wq; |
| 214 | init_waitqueue_head(&wq->queue); | 254 | init_waitqueue_head(&wq->queue); |
| 215 | wq->hash = dentry->d_name.hash; | 255 | wq->hash = hash; |
| 216 | wq->name = name; | 256 | wq->name = name; |
| 217 | wq->len = len; | 257 | wq->len = len; |
| 258 | wq->dev = autofs4_get_dev(sbi); | ||
| 259 | wq->ino = autofs4_get_ino(sbi); | ||
| 260 | wq->uid = current->uid; | ||
| 261 | wq->gid = current->gid; | ||
| 262 | wq->pid = current->pid; | ||
| 263 | wq->tgid = current->tgid; | ||
| 218 | wq->status = -EINTR; /* Status return if interrupted */ | 264 | wq->status = -EINTR; /* Status return if interrupted */ |
| 219 | atomic_set(&wq->wait_ctr, 2); | 265 | atomic_set(&wq->wait_ctr, 2); |
| 220 | atomic_set(&wq->notified, 1); | 266 | atomic_set(&wq->notify, 1); |
| 221 | mutex_unlock(&sbi->wq_mutex); | 267 | mutex_unlock(&sbi->wq_mutex); |
| 222 | } else { | 268 | } else { |
| 223 | atomic_inc(&wq->wait_ctr); | 269 | atomic_inc(&wq->wait_ctr); |
| @@ -227,9 +273,26 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, | |||
| 227 | (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); | 273 | (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); |
| 228 | } | 274 | } |
| 229 | 275 | ||
| 230 | if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { | 276 | if (notify != NFY_NONE && atomic_read(&wq->notify)) { |
| 231 | int type = (notify == NFY_MOUNT ? | 277 | int type; |
| 232 | autofs_ptype_missing : autofs_ptype_expire_multi); | 278 | |
| 279 | atomic_dec(&wq->notify); | ||
| 280 | |||
| 281 | if (sbi->version < 5) { | ||
| 282 | if (notify == NFY_MOUNT) | ||
| 283 | type = autofs_ptype_missing; | ||
| 284 | else | ||
| 285 | type = autofs_ptype_expire_multi; | ||
| 286 | } else { | ||
| 287 | if (notify == NFY_MOUNT) | ||
| 288 | type = (sbi->type & AUTOFS_TYPE_DIRECT) ? | ||
| 289 | autofs_ptype_missing_direct : | ||
| 290 | autofs_ptype_missing_indirect; | ||
| 291 | else | ||
| 292 | type = (sbi->type & AUTOFS_TYPE_DIRECT) ? | ||
| 293 | autofs_ptype_expire_direct : | ||
| 294 | autofs_ptype_expire_indirect; | ||
| 295 | } | ||
| 233 | 296 | ||
| 234 | DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", | 297 | DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", |
| 235 | (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); | 298 | (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); |
| @@ -240,14 +303,14 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, | |||
| 240 | 303 | ||
| 241 | /* wq->name is NULL if and only if the lock is already released */ | 304 | /* wq->name is NULL if and only if the lock is already released */ |
| 242 | 305 | ||
| 243 | if ( sbi->catatonic ) { | 306 | if (sbi->catatonic) { |
| 244 | /* We might have slept, so check again for catatonic mode */ | 307 | /* We might have slept, so check again for catatonic mode */ |
| 245 | wq->status = -ENOENT; | 308 | wq->status = -ENOENT; |
| 246 | kfree(wq->name); | 309 | kfree(wq->name); |
| 247 | wq->name = NULL; | 310 | wq->name = NULL; |
| 248 | } | 311 | } |
| 249 | 312 | ||
| 250 | if ( wq->name ) { | 313 | if (wq->name) { |
| 251 | /* Block all but "shutdown" signals while waiting */ | 314 | /* Block all but "shutdown" signals while waiting */ |
| 252 | sigset_t oldset; | 315 | sigset_t oldset; |
| 253 | unsigned long irqflags; | 316 | unsigned long irqflags; |
| @@ -283,12 +346,12 @@ int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_tok | |||
| 283 | struct autofs_wait_queue *wq, **wql; | 346 | struct autofs_wait_queue *wq, **wql; |
| 284 | 347 | ||
| 285 | mutex_lock(&sbi->wq_mutex); | 348 | mutex_lock(&sbi->wq_mutex); |
| 286 | for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { | 349 | for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { |
| 287 | if ( wq->wait_queue_token == wait_queue_token ) | 350 | if (wq->wait_queue_token == wait_queue_token) |
| 288 | break; | 351 | break; |
| 289 | } | 352 | } |
| 290 | 353 | ||
| 291 | if ( !wq ) { | 354 | if (!wq) { |
| 292 | mutex_unlock(&sbi->wq_mutex); | 355 | mutex_unlock(&sbi->wq_mutex); |
| 293 | return -EINVAL; | 356 | return -EINVAL; |
| 294 | } | 357 | } |
diff --git a/fs/block_dev.c b/fs/block_dev.c index 5983d42df015..17c76182f389 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
| @@ -266,6 +266,9 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) | |||
| 266 | mutex_init(&bdev->bd_mount_mutex); | 266 | mutex_init(&bdev->bd_mount_mutex); |
| 267 | INIT_LIST_HEAD(&bdev->bd_inodes); | 267 | INIT_LIST_HEAD(&bdev->bd_inodes); |
| 268 | INIT_LIST_HEAD(&bdev->bd_list); | 268 | INIT_LIST_HEAD(&bdev->bd_list); |
| 269 | #ifdef CONFIG_SYSFS | ||
| 270 | INIT_LIST_HEAD(&bdev->bd_holder_list); | ||
| 271 | #endif | ||
| 269 | inode_init_once(&ei->vfs_inode); | 272 | inode_init_once(&ei->vfs_inode); |
| 270 | } | 273 | } |
| 271 | } | 274 | } |
| @@ -490,6 +493,300 @@ void bd_release(struct block_device *bdev) | |||
| 490 | 493 | ||
| 491 | EXPORT_SYMBOL(bd_release); | 494 | EXPORT_SYMBOL(bd_release); |
| 492 | 495 | ||
| 496 | #ifdef CONFIG_SYSFS | ||
| 497 | /* | ||
| 498 | * Functions for bd_claim_by_kobject / bd_release_from_kobject | ||
| 499 | * | ||
| 500 | * If a kobject is passed to bd_claim_by_kobject() | ||
| 501 | * and the kobject has a parent directory, | ||
| 502 | * following symlinks are created: | ||
| 503 | * o from the kobject to the claimed bdev | ||
| 504 | * o from "holders" directory of the bdev to the parent of the kobject | ||
| 505 | * bd_release_from_kobject() removes these symlinks. | ||
| 506 | * | ||
| 507 | * Example: | ||
| 508 | * If /dev/dm-0 maps to /dev/sda, kobject corresponding to | ||
| 509 | * /sys/block/dm-0/slaves is passed to bd_claim_by_kobject(), then: | ||
| 510 | * /sys/block/dm-0/slaves/sda --> /sys/block/sda | ||
| 511 | * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0 | ||
| 512 | */ | ||
| 513 | |||
| 514 | static struct kobject *bdev_get_kobj(struct block_device *bdev) | ||
| 515 | { | ||
| 516 | if (bdev->bd_contains != bdev) | ||
| 517 | return kobject_get(&bdev->bd_part->kobj); | ||
| 518 | else | ||
| 519 | return kobject_get(&bdev->bd_disk->kobj); | ||
| 520 | } | ||
| 521 | |||
| 522 | static struct kobject *bdev_get_holder(struct block_device *bdev) | ||
| 523 | { | ||
| 524 | if (bdev->bd_contains != bdev) | ||
| 525 | return kobject_get(bdev->bd_part->holder_dir); | ||
| 526 | else | ||
| 527 | return kobject_get(bdev->bd_disk->holder_dir); | ||
| 528 | } | ||
| 529 | |||
| 530 | static void add_symlink(struct kobject *from, struct kobject *to) | ||
| 531 | { | ||
| 532 | if (!from || !to) | ||
| 533 | return; | ||
| 534 | sysfs_create_link(from, to, kobject_name(to)); | ||
| 535 | } | ||
| 536 | |||
| 537 | static void del_symlink(struct kobject *from, struct kobject *to) | ||
| 538 | { | ||
| 539 | if (!from || !to) | ||
| 540 | return; | ||
| 541 | sysfs_remove_link(from, kobject_name(to)); | ||
| 542 | } | ||
| 543 | |||
| 544 | /* | ||
| 545 | * 'struct bd_holder' contains pointers to kobjects symlinked by | ||
| 546 | * bd_claim_by_kobject. | ||
| 547 | * It's connected to bd_holder_list which is protected by bdev->bd_sem. | ||
| 548 | */ | ||
| 549 | struct bd_holder { | ||
| 550 | struct list_head list; /* chain of holders of the bdev */ | ||
| 551 | int count; /* references from the holder */ | ||
| 552 | struct kobject *sdir; /* holder object, e.g. "/block/dm-0/slaves" */ | ||
| 553 | struct kobject *hdev; /* e.g. "/block/dm-0" */ | ||
| 554 | struct kobject *hdir; /* e.g. "/block/sda/holders" */ | ||
| 555 | struct kobject *sdev; /* e.g. "/block/sda" */ | ||
| 556 | }; | ||
| 557 | |||
| 558 | /* | ||
| 559 | * Get references of related kobjects at once. | ||
| 560 | * Returns 1 on success. 0 on failure. | ||
| 561 | * | ||
| 562 | * Should call bd_holder_release_dirs() after successful use. | ||
| 563 | */ | ||
| 564 | static int bd_holder_grab_dirs(struct block_device *bdev, | ||
| 565 | struct bd_holder *bo) | ||
| 566 | { | ||
| 567 | if (!bdev || !bo) | ||
| 568 | return 0; | ||
| 569 | |||
| 570 | bo->sdir = kobject_get(bo->sdir); | ||
| 571 | if (!bo->sdir) | ||
| 572 | return 0; | ||
| 573 | |||
| 574 | bo->hdev = kobject_get(bo->sdir->parent); | ||
| 575 | if (!bo->hdev) | ||
| 576 | goto fail_put_sdir; | ||
| 577 | |||
| 578 | bo->sdev = bdev_get_kobj(bdev); | ||
| 579 | if (!bo->sdev) | ||
| 580 | goto fail_put_hdev; | ||
| 581 | |||
| 582 | bo->hdir = bdev_get_holder(bdev); | ||
| 583 | if (!bo->hdir) | ||
| 584 | goto fail_put_sdev; | ||
| 585 | |||
| 586 | return 1; | ||
| 587 | |||
| 588 | fail_put_sdev: | ||
| 589 | kobject_put(bo->sdev); | ||
| 590 | fail_put_hdev: | ||
| 591 | kobject_put(bo->hdev); | ||
| 592 | fail_put_sdir: | ||
| 593 | kobject_put(bo->sdir); | ||
| 594 | |||
| 595 | return 0; | ||
| 596 | } | ||
| 597 | |||
| 598 | /* Put references of related kobjects at once. */ | ||
| 599 | static void bd_holder_release_dirs(struct bd_holder *bo) | ||
| 600 | { | ||
| 601 | kobject_put(bo->hdir); | ||
| 602 | kobject_put(bo->sdev); | ||
| 603 | kobject_put(bo->hdev); | ||
| 604 | kobject_put(bo->sdir); | ||
| 605 | } | ||
| 606 | |||
| 607 | static struct bd_holder *alloc_bd_holder(struct kobject *kobj) | ||
| 608 | { | ||
| 609 | struct bd_holder *bo; | ||
| 610 | |||
| 611 | bo = kzalloc(sizeof(*bo), GFP_KERNEL); | ||
| 612 | if (!bo) | ||
| 613 | return NULL; | ||
| 614 | |||
| 615 | bo->count = 1; | ||
| 616 | bo->sdir = kobj; | ||
| 617 | |||
| 618 | return bo; | ||
| 619 | } | ||
| 620 | |||
| 621 | static void free_bd_holder(struct bd_holder *bo) | ||
| 622 | { | ||
| 623 | kfree(bo); | ||
| 624 | } | ||
| 625 | |||
| 626 | /** | ||
| 627 | * add_bd_holder - create sysfs symlinks for bd_claim() relationship | ||
| 628 | * | ||
| 629 | * @bdev: block device to be bd_claimed | ||
| 630 | * @bo: preallocated and initialized by alloc_bd_holder() | ||
| 631 | * | ||
| 632 | * If there is no matching entry with @bo in @bdev->bd_holder_list, | ||
| 633 | * add @bo to the list, create symlinks. | ||
| 634 | * | ||
| 635 | * Returns 1 if @bo was added to the list. | ||
| 636 | * Returns 0 if @bo wasn't used by any reason and should be freed. | ||
| 637 | */ | ||
| 638 | static int add_bd_holder(struct block_device *bdev, struct bd_holder *bo) | ||
| 639 | { | ||
| 640 | struct bd_holder *tmp; | ||
| 641 | |||
| 642 | if (!bo) | ||
| 643 | return 0; | ||
| 644 | |||
| 645 | list_for_each_entry(tmp, &bdev->bd_holder_list, list) { | ||
| 646 | if (tmp->sdir == bo->sdir) { | ||
| 647 | tmp->count++; | ||
| 648 | return 0; | ||
| 649 | } | ||
| 650 | } | ||
| 651 | |||
| 652 | if (!bd_holder_grab_dirs(bdev, bo)) | ||
| 653 | return 0; | ||
| 654 | |||
| 655 | add_symlink(bo->sdir, bo->sdev); | ||
| 656 | add_symlink(bo->hdir, bo->hdev); | ||
| 657 | list_add_tail(&bo->list, &bdev->bd_holder_list); | ||
| 658 | return 1; | ||
| 659 | } | ||
| 660 | |||
| 661 | /** | ||
| 662 | * del_bd_holder - delete sysfs symlinks for bd_claim() relationship | ||
| 663 | * | ||
| 664 | * @bdev: block device to be bd_claimed | ||
| 665 | * @kobj: holder's kobject | ||
| 666 | * | ||
| 667 | * If there is matching entry with @kobj in @bdev->bd_holder_list | ||
| 668 | * and no other bd_claim() from the same kobject, | ||
| 669 | * remove the struct bd_holder from the list, delete symlinks for it. | ||
| 670 | * | ||
| 671 | * Returns a pointer to the struct bd_holder when it's removed from the list | ||
| 672 | * and ready to be freed. | ||
| 673 | * Returns NULL if matching claim isn't found or there is other bd_claim() | ||
| 674 | * by the same kobject. | ||
| 675 | */ | ||
| 676 | static struct bd_holder *del_bd_holder(struct block_device *bdev, | ||
| 677 | struct kobject *kobj) | ||
| 678 | { | ||
| 679 | struct bd_holder *bo; | ||
| 680 | |||
| 681 | list_for_each_entry(bo, &bdev->bd_holder_list, list) { | ||
| 682 | if (bo->sdir == kobj) { | ||
| 683 | bo->count--; | ||
| 684 | BUG_ON(bo->count < 0); | ||
| 685 | if (!bo->count) { | ||
| 686 | list_del(&bo->list); | ||
| 687 | del_symlink(bo->sdir, bo->sdev); | ||
| 688 | del_symlink(bo->hdir, bo->hdev); | ||
| 689 | bd_holder_release_dirs(bo); | ||
| 690 | return bo; | ||
| 691 | } | ||
| 692 | break; | ||
| 693 | } | ||
| 694 | } | ||
| 695 | |||
| 696 | return NULL; | ||
| 697 | } | ||
| 698 | |||
| 699 | /** | ||
| 700 | * bd_claim_by_kobject - bd_claim() with additional kobject signature | ||
| 701 | * | ||
| 702 | * @bdev: block device to be claimed | ||
| 703 | * @holder: holder's signature | ||
| 704 | * @kobj: holder's kobject | ||
| 705 | * | ||
| 706 | * Do bd_claim() and if it succeeds, create sysfs symlinks between | ||
| 707 | * the bdev and the holder's kobject. | ||
| 708 | * Use bd_release_from_kobject() when relesing the claimed bdev. | ||
| 709 | * | ||
| 710 | * Returns 0 on success. (same as bd_claim()) | ||
| 711 | * Returns errno on failure. | ||
| 712 | */ | ||
| 713 | static int bd_claim_by_kobject(struct block_device *bdev, void *holder, | ||
| 714 | struct kobject *kobj) | ||
| 715 | { | ||
| 716 | int res; | ||
| 717 | struct bd_holder *bo; | ||
| 718 | |||
| 719 | if (!kobj) | ||
| 720 | return -EINVAL; | ||
| 721 | |||
| 722 | bo = alloc_bd_holder(kobj); | ||
| 723 | if (!bo) | ||
| 724 | return -ENOMEM; | ||
| 725 | |||
| 726 | mutex_lock(&bdev->bd_mutex); | ||
| 727 | res = bd_claim(bdev, holder); | ||
| 728 | if (res || !add_bd_holder(bdev, bo)) | ||
| 729 | free_bd_holder(bo); | ||
| 730 | mutex_unlock(&bdev->bd_mutex); | ||
| 731 | |||
| 732 | return res; | ||
| 733 | } | ||
| 734 | |||
| 735 | /** | ||
| 736 | * bd_release_from_kobject - bd_release() with additional kobject signature | ||
| 737 | * | ||
| 738 | * @bdev: block device to be released | ||
| 739 | * @kobj: holder's kobject | ||
| 740 | * | ||
| 741 | * Do bd_release() and remove sysfs symlinks created by bd_claim_by_kobject(). | ||
| 742 | */ | ||
| 743 | static void bd_release_from_kobject(struct block_device *bdev, | ||
| 744 | struct kobject *kobj) | ||
| 745 | { | ||
| 746 | struct bd_holder *bo; | ||
| 747 | |||
| 748 | if (!kobj) | ||
| 749 | return; | ||
| 750 | |||
| 751 | mutex_lock(&bdev->bd_mutex); | ||
| 752 | bd_release(bdev); | ||
| 753 | if ((bo = del_bd_holder(bdev, kobj))) | ||
| 754 | free_bd_holder(bo); | ||
| 755 | mutex_unlock(&bdev->bd_mutex); | ||
| 756 | } | ||
| 757 | |||
| 758 | /** | ||
| 759 | * bd_claim_by_disk - wrapper function for bd_claim_by_kobject() | ||
| 760 | * | ||
| 761 | * @bdev: block device to be claimed | ||
| 762 | * @holder: holder's signature | ||
| 763 | * @disk: holder's gendisk | ||
| 764 | * | ||
| 765 | * Call bd_claim_by_kobject() with getting @disk->slave_dir. | ||
| 766 | */ | ||
| 767 | int bd_claim_by_disk(struct block_device *bdev, void *holder, | ||
| 768 | struct gendisk *disk) | ||
| 769 | { | ||
| 770 | return bd_claim_by_kobject(bdev, holder, kobject_get(disk->slave_dir)); | ||
| 771 | } | ||
| 772 | EXPORT_SYMBOL_GPL(bd_claim_by_disk); | ||
| 773 | |||
| 774 | /** | ||
| 775 | * bd_release_from_disk - wrapper function for bd_release_from_kobject() | ||
| 776 | * | ||
| 777 | * @bdev: block device to be claimed | ||
| 778 | * @disk: holder's gendisk | ||
| 779 | * | ||
| 780 | * Call bd_release_from_kobject() and put @disk->slave_dir. | ||
| 781 | */ | ||
| 782 | void bd_release_from_disk(struct block_device *bdev, struct gendisk *disk) | ||
| 783 | { | ||
| 784 | bd_release_from_kobject(bdev, disk->slave_dir); | ||
| 785 | kobject_put(disk->slave_dir); | ||
| 786 | } | ||
| 787 | EXPORT_SYMBOL_GPL(bd_release_from_disk); | ||
| 788 | #endif | ||
| 789 | |||
| 493 | /* | 790 | /* |
| 494 | * Tries to open block device by device number. Use it ONLY if you | 791 | * Tries to open block device by device number. Use it ONLY if you |
| 495 | * really do not have anything better - i.e. when you are behind a | 792 | * really do not have anything better - i.e. when you are behind a |
diff --git a/fs/buffer.c b/fs/buffer.c index d597758dd129..23f1f3a68077 100644 --- a/fs/buffer.c +++ b/fs/buffer.c | |||
| @@ -493,7 +493,7 @@ static void free_more_memory(void) | |||
| 493 | wakeup_pdflush(1024); | 493 | wakeup_pdflush(1024); |
| 494 | yield(); | 494 | yield(); |
| 495 | 495 | ||
| 496 | for_each_pgdat(pgdat) { | 496 | for_each_online_pgdat(pgdat) { |
| 497 | zones = pgdat->node_zonelists[gfp_zone(GFP_NOFS)].zones; | 497 | zones = pgdat->node_zonelists[gfp_zone(GFP_NOFS)].zones; |
| 498 | if (*zones) | 498 | if (*zones) |
| 499 | try_to_free_pages(zones, GFP_NOFS); | 499 | try_to_free_pages(zones, GFP_NOFS); |
diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index b97809deba66..23b7cee72123 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c | |||
| @@ -360,7 +360,6 @@ int do_statfs(char *root, long *bsize_out, long long *blocks_out, | |||
| 360 | spare_out[2] = buf.f_spare[2]; | 360 | spare_out[2] = buf.f_spare[2]; |
| 361 | spare_out[3] = buf.f_spare[3]; | 361 | spare_out[3] = buf.f_spare[3]; |
| 362 | spare_out[4] = buf.f_spare[4]; | 362 | spare_out[4] = buf.f_spare[4]; |
| 363 | spare_out[5] = buf.f_spare[5]; | ||
| 364 | return(0); | 363 | return(0); |
| 365 | } | 364 | } |
| 366 | 365 | ||
diff --git a/fs/namei.c b/fs/namei.c index 98dc2e134362..22f6e8d16aa8 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
| @@ -546,6 +546,22 @@ struct path { | |||
| 546 | struct dentry *dentry; | 546 | struct dentry *dentry; |
| 547 | }; | 547 | }; |
| 548 | 548 | ||
| 549 | static inline void dput_path(struct path *path, struct nameidata *nd) | ||
| 550 | { | ||
| 551 | dput(path->dentry); | ||
| 552 | if (path->mnt != nd->mnt) | ||
| 553 | mntput(path->mnt); | ||
| 554 | } | ||
| 555 | |||
| 556 | static inline void path_to_nameidata(struct path *path, struct nameidata *nd) | ||
| 557 | { | ||
| 558 | dput(nd->dentry); | ||
| 559 | if (nd->mnt != path->mnt) | ||
| 560 | mntput(nd->mnt); | ||
| 561 | nd->mnt = path->mnt; | ||
| 562 | nd->dentry = path->dentry; | ||
| 563 | } | ||
| 564 | |||
| 549 | static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd) | 565 | static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd) |
| 550 | { | 566 | { |
| 551 | int error; | 567 | int error; |
| @@ -555,8 +571,11 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata | |||
| 555 | touch_atime(path->mnt, dentry); | 571 | touch_atime(path->mnt, dentry); |
| 556 | nd_set_link(nd, NULL); | 572 | nd_set_link(nd, NULL); |
| 557 | 573 | ||
| 558 | if (path->mnt == nd->mnt) | 574 | if (path->mnt != nd->mnt) { |
| 559 | mntget(path->mnt); | 575 | path_to_nameidata(path, nd); |
| 576 | dget(dentry); | ||
| 577 | } | ||
| 578 | mntget(path->mnt); | ||
| 560 | cookie = dentry->d_inode->i_op->follow_link(dentry, nd); | 579 | cookie = dentry->d_inode->i_op->follow_link(dentry, nd); |
| 561 | error = PTR_ERR(cookie); | 580 | error = PTR_ERR(cookie); |
| 562 | if (!IS_ERR(cookie)) { | 581 | if (!IS_ERR(cookie)) { |
| @@ -573,22 +592,6 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata | |||
| 573 | return error; | 592 | return error; |
| 574 | } | 593 | } |
| 575 | 594 | ||
| 576 | static inline void dput_path(struct path *path, struct nameidata *nd) | ||
| 577 | { | ||
| 578 | dput(path->dentry); | ||
| 579 | if (path->mnt != nd->mnt) | ||
| 580 | mntput(path->mnt); | ||
| 581 | } | ||
| 582 | |||
| 583 | static inline void path_to_nameidata(struct path *path, struct nameidata *nd) | ||
| 584 | { | ||
| 585 | dput(nd->dentry); | ||
| 586 | if (nd->mnt != path->mnt) | ||
| 587 | mntput(nd->mnt); | ||
| 588 | nd->mnt = path->mnt; | ||
| 589 | nd->dentry = path->dentry; | ||
| 590 | } | ||
| 591 | |||
| 592 | /* | 595 | /* |
| 593 | * This limits recursive symlink follows to 8, while | 596 | * This limits recursive symlink follows to 8, while |
| 594 | * limiting consecutive symlinks to 40. | 597 | * limiting consecutive symlinks to 40. |
diff --git a/fs/namespace.c b/fs/namespace.c index e069a4c5e389..bf478addb852 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
| @@ -459,9 +459,9 @@ int may_umount_tree(struct vfsmount *mnt) | |||
| 459 | spin_unlock(&vfsmount_lock); | 459 | spin_unlock(&vfsmount_lock); |
| 460 | 460 | ||
| 461 | if (actual_refs > minimum_refs) | 461 | if (actual_refs > minimum_refs) |
| 462 | return -EBUSY; | 462 | return 0; |
| 463 | 463 | ||
| 464 | return 0; | 464 | return 1; |
| 465 | } | 465 | } |
| 466 | 466 | ||
| 467 | EXPORT_SYMBOL(may_umount_tree); | 467 | EXPORT_SYMBOL(may_umount_tree); |
| @@ -481,10 +481,10 @@ EXPORT_SYMBOL(may_umount_tree); | |||
| 481 | */ | 481 | */ |
| 482 | int may_umount(struct vfsmount *mnt) | 482 | int may_umount(struct vfsmount *mnt) |
| 483 | { | 483 | { |
| 484 | int ret = 0; | 484 | int ret = 1; |
| 485 | spin_lock(&vfsmount_lock); | 485 | spin_lock(&vfsmount_lock); |
| 486 | if (propagate_mount_busy(mnt, 2)) | 486 | if (propagate_mount_busy(mnt, 2)) |
| 487 | ret = -EBUSY; | 487 | ret = 0; |
| 488 | spin_unlock(&vfsmount_lock); | 488 | spin_unlock(&vfsmount_lock); |
| 489 | return ret; | 489 | return ret; |
| 490 | } | 490 | } |
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 417ec02df44f..c340be0a3f59 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c | |||
| @@ -57,27 +57,17 @@ static int exp_verify_string(char *cp, int max); | |||
| 57 | #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) | 57 | #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) |
| 58 | static struct cache_head *expkey_table[EXPKEY_HASHMAX]; | 58 | static struct cache_head *expkey_table[EXPKEY_HASHMAX]; |
| 59 | 59 | ||
| 60 | static inline int svc_expkey_hash(struct svc_expkey *item) | 60 | static void expkey_put(struct kref *ref) |
| 61 | { | 61 | { |
| 62 | int hash = item->ek_fsidtype; | 62 | struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); |
| 63 | char * cp = (char*)item->ek_fsid; | ||
| 64 | int len = key_len(item->ek_fsidtype); | ||
| 65 | 63 | ||
| 66 | hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); | 64 | if (test_bit(CACHE_VALID, &key->h.flags) && |
| 67 | hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); | 65 | !test_bit(CACHE_NEGATIVE, &key->h.flags)) { |
| 68 | return hash & EXPKEY_HASHMASK; | 66 | dput(key->ek_dentry); |
| 69 | } | 67 | mntput(key->ek_mnt); |
| 70 | |||
| 71 | void expkey_put(struct cache_head *item, struct cache_detail *cd) | ||
| 72 | { | ||
| 73 | if (cache_put(item, cd)) { | ||
| 74 | struct svc_expkey *key = container_of(item, struct svc_expkey, h); | ||
| 75 | if (test_bit(CACHE_VALID, &item->flags) && | ||
| 76 | !test_bit(CACHE_NEGATIVE, &item->flags)) | ||
| 77 | exp_put(key->ek_export); | ||
| 78 | auth_domain_put(key->ek_client); | ||
| 79 | kfree(key); | ||
| 80 | } | 68 | } |
| 69 | auth_domain_put(key->ek_client); | ||
| 70 | kfree(key); | ||
| 81 | } | 71 | } |
| 82 | 72 | ||
| 83 | static void expkey_request(struct cache_detail *cd, | 73 | static void expkey_request(struct cache_detail *cd, |
| @@ -95,7 +85,10 @@ static void expkey_request(struct cache_detail *cd, | |||
| 95 | (*bpp)[-1] = '\n'; | 85 | (*bpp)[-1] = '\n'; |
| 96 | } | 86 | } |
| 97 | 87 | ||
| 98 | static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *, int); | 88 | static struct svc_expkey *svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old); |
| 89 | static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *); | ||
| 90 | static struct cache_detail svc_expkey_cache; | ||
| 91 | |||
| 99 | static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) | 92 | static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) |
| 100 | { | 93 | { |
| 101 | /* client fsidtype fsid [path] */ | 94 | /* client fsidtype fsid [path] */ |
| @@ -106,6 +99,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) | |||
| 106 | int fsidtype; | 99 | int fsidtype; |
| 107 | char *ep; | 100 | char *ep; |
| 108 | struct svc_expkey key; | 101 | struct svc_expkey key; |
| 102 | struct svc_expkey *ek; | ||
| 109 | 103 | ||
| 110 | if (mesg[mlen-1] != '\n') | 104 | if (mesg[mlen-1] != '\n') |
| 111 | return -EINVAL; | 105 | return -EINVAL; |
| @@ -150,40 +144,38 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) | |||
| 150 | key.ek_fsidtype = fsidtype; | 144 | key.ek_fsidtype = fsidtype; |
| 151 | memcpy(key.ek_fsid, buf, len); | 145 | memcpy(key.ek_fsid, buf, len); |
| 152 | 146 | ||
| 147 | ek = svc_expkey_lookup(&key); | ||
| 148 | err = -ENOMEM; | ||
| 149 | if (!ek) | ||
| 150 | goto out; | ||
| 151 | |||
| 153 | /* now we want a pathname, or empty meaning NEGATIVE */ | 152 | /* now we want a pathname, or empty meaning NEGATIVE */ |
| 153 | err = -EINVAL; | ||
| 154 | if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0) | 154 | if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0) |
| 155 | goto out; | 155 | goto out; |
| 156 | dprintk("Path seems to be <%s>\n", buf); | 156 | dprintk("Path seems to be <%s>\n", buf); |
| 157 | err = 0; | 157 | err = 0; |
| 158 | if (len == 0) { | 158 | if (len == 0) { |
| 159 | struct svc_expkey *ek; | ||
| 160 | set_bit(CACHE_NEGATIVE, &key.h.flags); | 159 | set_bit(CACHE_NEGATIVE, &key.h.flags); |
| 161 | ek = svc_expkey_lookup(&key, 1); | 160 | ek = svc_expkey_update(&key, ek); |
| 162 | if (ek) | 161 | if (ek) |
| 163 | expkey_put(&ek->h, &svc_expkey_cache); | 162 | cache_put(&ek->h, &svc_expkey_cache); |
| 163 | else err = -ENOMEM; | ||
| 164 | } else { | 164 | } else { |
| 165 | struct nameidata nd; | 165 | struct nameidata nd; |
| 166 | struct svc_expkey *ek; | ||
| 167 | struct svc_export *exp; | ||
| 168 | err = path_lookup(buf, 0, &nd); | 166 | err = path_lookup(buf, 0, &nd); |
| 169 | if (err) | 167 | if (err) |
| 170 | goto out; | 168 | goto out; |
| 171 | 169 | ||
| 172 | dprintk("Found the path %s\n", buf); | 170 | dprintk("Found the path %s\n", buf); |
| 173 | exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); | 171 | key.ek_mnt = nd.mnt; |
| 174 | 172 | key.ek_dentry = nd.dentry; | |
| 175 | err = -ENOENT; | ||
| 176 | if (!exp) | ||
| 177 | goto out_nd; | ||
| 178 | key.ek_export = exp; | ||
| 179 | dprintk("And found export\n"); | ||
| 180 | 173 | ||
| 181 | ek = svc_expkey_lookup(&key, 1); | 174 | ek = svc_expkey_update(&key, ek); |
| 182 | if (ek) | 175 | if (ek) |
| 183 | expkey_put(&ek->h, &svc_expkey_cache); | 176 | cache_put(&ek->h, &svc_expkey_cache); |
| 184 | exp_put(exp); | 177 | else |
| 185 | err = 0; | 178 | err = -ENOMEM; |
| 186 | out_nd: | ||
| 187 | path_release(&nd); | 179 | path_release(&nd); |
| 188 | } | 180 | } |
| 189 | cache_flush(); | 181 | cache_flush(); |
| @@ -214,35 +206,31 @@ static int expkey_show(struct seq_file *m, | |||
| 214 | if (test_bit(CACHE_VALID, &h->flags) && | 206 | if (test_bit(CACHE_VALID, &h->flags) && |
| 215 | !test_bit(CACHE_NEGATIVE, &h->flags)) { | 207 | !test_bit(CACHE_NEGATIVE, &h->flags)) { |
| 216 | seq_printf(m, " "); | 208 | seq_printf(m, " "); |
| 217 | seq_path(m, ek->ek_export->ex_mnt, ek->ek_export->ex_dentry, "\\ \t\n"); | 209 | seq_path(m, ek->ek_mnt, ek->ek_dentry, "\\ \t\n"); |
| 218 | } | 210 | } |
| 219 | seq_printf(m, "\n"); | 211 | seq_printf(m, "\n"); |
| 220 | return 0; | 212 | return 0; |
| 221 | } | 213 | } |
| 222 | |||
| 223 | struct cache_detail svc_expkey_cache = { | ||
| 224 | .owner = THIS_MODULE, | ||
| 225 | .hash_size = EXPKEY_HASHMAX, | ||
| 226 | .hash_table = expkey_table, | ||
| 227 | .name = "nfsd.fh", | ||
| 228 | .cache_put = expkey_put, | ||
| 229 | .cache_request = expkey_request, | ||
| 230 | .cache_parse = expkey_parse, | ||
| 231 | .cache_show = expkey_show, | ||
| 232 | }; | ||
| 233 | 214 | ||
| 234 | static inline int svc_expkey_match (struct svc_expkey *a, struct svc_expkey *b) | 215 | static inline int expkey_match (struct cache_head *a, struct cache_head *b) |
| 235 | { | 216 | { |
| 236 | if (a->ek_fsidtype != b->ek_fsidtype || | 217 | struct svc_expkey *orig = container_of(a, struct svc_expkey, h); |
| 237 | a->ek_client != b->ek_client || | 218 | struct svc_expkey *new = container_of(b, struct svc_expkey, h); |
| 238 | memcmp(a->ek_fsid, b->ek_fsid, key_len(a->ek_fsidtype)) != 0) | 219 | |
| 220 | if (orig->ek_fsidtype != new->ek_fsidtype || | ||
| 221 | orig->ek_client != new->ek_client || | ||
| 222 | memcmp(orig->ek_fsid, new->ek_fsid, key_len(orig->ek_fsidtype)) != 0) | ||
| 239 | return 0; | 223 | return 0; |
| 240 | return 1; | 224 | return 1; |
| 241 | } | 225 | } |
| 242 | 226 | ||
| 243 | static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *item) | 227 | static inline void expkey_init(struct cache_head *cnew, |
| 228 | struct cache_head *citem) | ||
| 244 | { | 229 | { |
| 245 | cache_get(&item->ek_client->h); | 230 | struct svc_expkey *new = container_of(cnew, struct svc_expkey, h); |
| 231 | struct svc_expkey *item = container_of(citem, struct svc_expkey, h); | ||
| 232 | |||
| 233 | kref_get(&item->ek_client->ref); | ||
| 246 | new->ek_client = item->ek_client; | 234 | new->ek_client = item->ek_client; |
| 247 | new->ek_fsidtype = item->ek_fsidtype; | 235 | new->ek_fsidtype = item->ek_fsidtype; |
| 248 | new->ek_fsid[0] = item->ek_fsid[0]; | 236 | new->ek_fsid[0] = item->ek_fsid[0]; |
| @@ -250,39 +238,94 @@ static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *it | |||
| 250 | new->ek_fsid[2] = item->ek_fsid[2]; | 238 | new->ek_fsid[2] = item->ek_fsid[2]; |
| 251 | } | 239 | } |
| 252 | 240 | ||
| 253 | static inline void svc_expkey_update(struct svc_expkey *new, struct svc_expkey *item) | 241 | static inline void expkey_update(struct cache_head *cnew, |
| 242 | struct cache_head *citem) | ||
| 243 | { | ||
| 244 | struct svc_expkey *new = container_of(cnew, struct svc_expkey, h); | ||
| 245 | struct svc_expkey *item = container_of(citem, struct svc_expkey, h); | ||
| 246 | |||
| 247 | new->ek_mnt = mntget(item->ek_mnt); | ||
| 248 | new->ek_dentry = dget(item->ek_dentry); | ||
| 249 | } | ||
| 250 | |||
| 251 | static struct cache_head *expkey_alloc(void) | ||
| 254 | { | 252 | { |
| 255 | cache_get(&item->ek_export->h); | 253 | struct svc_expkey *i = kmalloc(sizeof(*i), GFP_KERNEL); |
| 256 | new->ek_export = item->ek_export; | 254 | if (i) |
| 255 | return &i->h; | ||
| 256 | else | ||
| 257 | return NULL; | ||
| 257 | } | 258 | } |
| 258 | 259 | ||
| 259 | static DefineSimpleCacheLookup(svc_expkey,0) /* no inplace updates */ | 260 | static struct cache_detail svc_expkey_cache = { |
| 261 | .owner = THIS_MODULE, | ||
| 262 | .hash_size = EXPKEY_HASHMAX, | ||
| 263 | .hash_table = expkey_table, | ||
| 264 | .name = "nfsd.fh", | ||
| 265 | .cache_put = expkey_put, | ||
| 266 | .cache_request = expkey_request, | ||
| 267 | .cache_parse = expkey_parse, | ||
| 268 | .cache_show = expkey_show, | ||
| 269 | .match = expkey_match, | ||
| 270 | .init = expkey_init, | ||
| 271 | .update = expkey_update, | ||
| 272 | .alloc = expkey_alloc, | ||
| 273 | }; | ||
| 260 | 274 | ||
| 261 | #define EXPORT_HASHBITS 8 | 275 | static struct svc_expkey * |
| 262 | #define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) | 276 | svc_expkey_lookup(struct svc_expkey *item) |
| 263 | #define EXPORT_HASHMASK (EXPORT_HASHMAX -1) | 277 | { |
| 278 | struct cache_head *ch; | ||
| 279 | int hash = item->ek_fsidtype; | ||
| 280 | char * cp = (char*)item->ek_fsid; | ||
| 281 | int len = key_len(item->ek_fsidtype); | ||
| 264 | 282 | ||
| 265 | static struct cache_head *export_table[EXPORT_HASHMAX]; | 283 | hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); |
| 284 | hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); | ||
| 285 | hash &= EXPKEY_HASHMASK; | ||
| 266 | 286 | ||
| 267 | static inline int svc_export_hash(struct svc_export *item) | 287 | ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h, |
| 288 | hash); | ||
| 289 | if (ch) | ||
| 290 | return container_of(ch, struct svc_expkey, h); | ||
| 291 | else | ||
| 292 | return NULL; | ||
| 293 | } | ||
| 294 | |||
| 295 | static struct svc_expkey * | ||
| 296 | svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) | ||
| 268 | { | 297 | { |
| 269 | int rv; | 298 | struct cache_head *ch; |
| 299 | int hash = new->ek_fsidtype; | ||
| 300 | char * cp = (char*)new->ek_fsid; | ||
| 301 | int len = key_len(new->ek_fsidtype); | ||
| 270 | 302 | ||
| 271 | rv = hash_ptr(item->ex_client, EXPORT_HASHBITS); | 303 | hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); |
| 272 | rv ^= hash_ptr(item->ex_dentry, EXPORT_HASHBITS); | 304 | hash ^= hash_ptr(new->ek_client, EXPKEY_HASHBITS); |
| 273 | rv ^= hash_ptr(item->ex_mnt, EXPORT_HASHBITS); | 305 | hash &= EXPKEY_HASHMASK; |
| 274 | return rv; | 306 | |
| 307 | ch = sunrpc_cache_update(&svc_expkey_cache, &new->h, | ||
| 308 | &old->h, hash); | ||
| 309 | if (ch) | ||
| 310 | return container_of(ch, struct svc_expkey, h); | ||
| 311 | else | ||
| 312 | return NULL; | ||
| 275 | } | 313 | } |
| 276 | 314 | ||
| 277 | void svc_export_put(struct cache_head *item, struct cache_detail *cd) | 315 | |
| 316 | #define EXPORT_HASHBITS 8 | ||
| 317 | #define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) | ||
| 318 | #define EXPORT_HASHMASK (EXPORT_HASHMAX -1) | ||
| 319 | |||
| 320 | static struct cache_head *export_table[EXPORT_HASHMAX]; | ||
| 321 | |||
| 322 | static void svc_export_put(struct kref *ref) | ||
| 278 | { | 323 | { |
| 279 | if (cache_put(item, cd)) { | 324 | struct svc_export *exp = container_of(ref, struct svc_export, h.ref); |
| 280 | struct svc_export *exp = container_of(item, struct svc_export, h); | 325 | dput(exp->ex_dentry); |
| 281 | dput(exp->ex_dentry); | 326 | mntput(exp->ex_mnt); |
| 282 | mntput(exp->ex_mnt); | 327 | auth_domain_put(exp->ex_client); |
| 283 | auth_domain_put(exp->ex_client); | 328 | kfree(exp); |
| 284 | kfree(exp); | ||
| 285 | } | ||
| 286 | } | 329 | } |
| 287 | 330 | ||
| 288 | static void svc_export_request(struct cache_detail *cd, | 331 | static void svc_export_request(struct cache_detail *cd, |
| @@ -304,7 +347,9 @@ static void svc_export_request(struct cache_detail *cd, | |||
| 304 | (*bpp)[-1] = '\n'; | 347 | (*bpp)[-1] = '\n'; |
| 305 | } | 348 | } |
| 306 | 349 | ||
| 307 | static struct svc_export *svc_export_lookup(struct svc_export *, int); | 350 | static struct svc_export *svc_export_update(struct svc_export *new, |
| 351 | struct svc_export *old); | ||
| 352 | static struct svc_export *svc_export_lookup(struct svc_export *); | ||
| 308 | 353 | ||
| 309 | static int check_export(struct inode *inode, int flags) | 354 | static int check_export(struct inode *inode, int flags) |
| 310 | { | 355 | { |
| @@ -417,11 +462,16 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) | |||
| 417 | if (err) goto out; | 462 | if (err) goto out; |
| 418 | } | 463 | } |
| 419 | 464 | ||
| 420 | expp = svc_export_lookup(&exp, 1); | 465 | expp = svc_export_lookup(&exp); |
| 421 | if (expp) | 466 | if (expp) |
| 422 | exp_put(expp); | 467 | expp = svc_export_update(&exp, expp); |
| 423 | err = 0; | 468 | else |
| 469 | err = -ENOMEM; | ||
| 424 | cache_flush(); | 470 | cache_flush(); |
| 471 | if (expp == NULL) | ||
| 472 | err = -ENOMEM; | ||
| 473 | else | ||
| 474 | exp_put(expp); | ||
| 425 | out: | 475 | out: |
| 426 | if (nd.dentry) | 476 | if (nd.dentry) |
| 427 | path_release(&nd); | 477 | path_release(&nd); |
| @@ -455,6 +505,46 @@ static int svc_export_show(struct seq_file *m, | |||
| 455 | seq_puts(m, ")\n"); | 505 | seq_puts(m, ")\n"); |
| 456 | return 0; | 506 | return 0; |
| 457 | } | 507 | } |
| 508 | static int svc_export_match(struct cache_head *a, struct cache_head *b) | ||
| 509 | { | ||
| 510 | struct svc_export *orig = container_of(a, struct svc_export, h); | ||
| 511 | struct svc_export *new = container_of(b, struct svc_export, h); | ||
| 512 | return orig->ex_client == new->ex_client && | ||
| 513 | orig->ex_dentry == new->ex_dentry && | ||
| 514 | orig->ex_mnt == new->ex_mnt; | ||
| 515 | } | ||
| 516 | |||
| 517 | static void svc_export_init(struct cache_head *cnew, struct cache_head *citem) | ||
| 518 | { | ||
| 519 | struct svc_export *new = container_of(cnew, struct svc_export, h); | ||
| 520 | struct svc_export *item = container_of(citem, struct svc_export, h); | ||
| 521 | |||
| 522 | kref_get(&item->ex_client->ref); | ||
| 523 | new->ex_client = item->ex_client; | ||
| 524 | new->ex_dentry = dget(item->ex_dentry); | ||
| 525 | new->ex_mnt = mntget(item->ex_mnt); | ||
| 526 | } | ||
| 527 | |||
| 528 | static void export_update(struct cache_head *cnew, struct cache_head *citem) | ||
| 529 | { | ||
| 530 | struct svc_export *new = container_of(cnew, struct svc_export, h); | ||
| 531 | struct svc_export *item = container_of(citem, struct svc_export, h); | ||
| 532 | |||
| 533 | new->ex_flags = item->ex_flags; | ||
| 534 | new->ex_anon_uid = item->ex_anon_uid; | ||
| 535 | new->ex_anon_gid = item->ex_anon_gid; | ||
| 536 | new->ex_fsid = item->ex_fsid; | ||
| 537 | } | ||
| 538 | |||
| 539 | static struct cache_head *svc_export_alloc(void) | ||
| 540 | { | ||
| 541 | struct svc_export *i = kmalloc(sizeof(*i), GFP_KERNEL); | ||
| 542 | if (i) | ||
| 543 | return &i->h; | ||
| 544 | else | ||
| 545 | return NULL; | ||
| 546 | } | ||
| 547 | |||
| 458 | struct cache_detail svc_export_cache = { | 548 | struct cache_detail svc_export_cache = { |
| 459 | .owner = THIS_MODULE, | 549 | .owner = THIS_MODULE, |
| 460 | .hash_size = EXPORT_HASHMAX, | 550 | .hash_size = EXPORT_HASHMAX, |
| @@ -464,34 +554,49 @@ struct cache_detail svc_export_cache = { | |||
| 464 | .cache_request = svc_export_request, | 554 | .cache_request = svc_export_request, |
| 465 | .cache_parse = svc_export_parse, | 555 | .cache_parse = svc_export_parse, |
| 466 | .cache_show = svc_export_show, | 556 | .cache_show = svc_export_show, |
| 557 | .match = svc_export_match, | ||
| 558 | .init = svc_export_init, | ||
| 559 | .update = export_update, | ||
| 560 | .alloc = svc_export_alloc, | ||
| 467 | }; | 561 | }; |
| 468 | 562 | ||
| 469 | static inline int svc_export_match(struct svc_export *a, struct svc_export *b) | 563 | static struct svc_export * |
| 564 | svc_export_lookup(struct svc_export *exp) | ||
| 470 | { | 565 | { |
| 471 | return a->ex_client == b->ex_client && | 566 | struct cache_head *ch; |
| 472 | a->ex_dentry == b->ex_dentry && | 567 | int hash; |
| 473 | a->ex_mnt == b->ex_mnt; | 568 | hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS); |
| 474 | } | 569 | hash ^= hash_ptr(exp->ex_dentry, EXPORT_HASHBITS); |
| 475 | static inline void svc_export_init(struct svc_export *new, struct svc_export *item) | 570 | hash ^= hash_ptr(exp->ex_mnt, EXPORT_HASHBITS); |
| 476 | { | 571 | |
| 477 | cache_get(&item->ex_client->h); | 572 | ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h, |
| 478 | new->ex_client = item->ex_client; | 573 | hash); |
| 479 | new->ex_dentry = dget(item->ex_dentry); | 574 | if (ch) |
| 480 | new->ex_mnt = mntget(item->ex_mnt); | 575 | return container_of(ch, struct svc_export, h); |
| 576 | else | ||
| 577 | return NULL; | ||
| 481 | } | 578 | } |
| 482 | 579 | ||
| 483 | static inline void svc_export_update(struct svc_export *new, struct svc_export *item) | 580 | static struct svc_export * |
| 581 | svc_export_update(struct svc_export *new, struct svc_export *old) | ||
| 484 | { | 582 | { |
| 485 | new->ex_flags = item->ex_flags; | 583 | struct cache_head *ch; |
| 486 | new->ex_anon_uid = item->ex_anon_uid; | 584 | int hash; |
| 487 | new->ex_anon_gid = item->ex_anon_gid; | 585 | hash = hash_ptr(old->ex_client, EXPORT_HASHBITS); |
| 488 | new->ex_fsid = item->ex_fsid; | 586 | hash ^= hash_ptr(old->ex_dentry, EXPORT_HASHBITS); |
| 587 | hash ^= hash_ptr(old->ex_mnt, EXPORT_HASHBITS); | ||
| 588 | |||
| 589 | ch = sunrpc_cache_update(&svc_export_cache, &new->h, | ||
| 590 | &old->h, | ||
| 591 | hash); | ||
| 592 | if (ch) | ||
| 593 | return container_of(ch, struct svc_export, h); | ||
| 594 | else | ||
| 595 | return NULL; | ||
| 489 | } | 596 | } |
| 490 | 597 | ||
| 491 | static DefineSimpleCacheLookup(svc_export,1) /* allow inplace updates */ | ||
| 492 | 598 | ||
| 493 | 599 | static struct svc_expkey * | |
| 494 | struct svc_expkey * | ||
| 495 | exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) | 600 | exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) |
| 496 | { | 601 | { |
| 497 | struct svc_expkey key, *ek; | 602 | struct svc_expkey key, *ek; |
| @@ -504,7 +609,7 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) | |||
| 504 | key.ek_fsidtype = fsid_type; | 609 | key.ek_fsidtype = fsid_type; |
| 505 | memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); | 610 | memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); |
| 506 | 611 | ||
| 507 | ek = svc_expkey_lookup(&key, 0); | 612 | ek = svc_expkey_lookup(&key); |
| 508 | if (ek != NULL) | 613 | if (ek != NULL) |
| 509 | if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp))) | 614 | if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp))) |
| 510 | ek = ERR_PTR(err); | 615 | ek = ERR_PTR(err); |
| @@ -519,13 +624,16 @@ static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv, | |||
| 519 | key.ek_client = clp; | 624 | key.ek_client = clp; |
| 520 | key.ek_fsidtype = fsid_type; | 625 | key.ek_fsidtype = fsid_type; |
| 521 | memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); | 626 | memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); |
| 522 | key.ek_export = exp; | 627 | key.ek_mnt = exp->ex_mnt; |
| 628 | key.ek_dentry = exp->ex_dentry; | ||
| 523 | key.h.expiry_time = NEVER; | 629 | key.h.expiry_time = NEVER; |
| 524 | key.h.flags = 0; | 630 | key.h.flags = 0; |
| 525 | 631 | ||
| 526 | ek = svc_expkey_lookup(&key, 1); | 632 | ek = svc_expkey_lookup(&key); |
| 633 | if (ek) | ||
| 634 | ek = svc_expkey_update(&key,ek); | ||
| 527 | if (ek) { | 635 | if (ek) { |
| 528 | expkey_put(&ek->h, &svc_expkey_cache); | 636 | cache_put(&ek->h, &svc_expkey_cache); |
| 529 | return 0; | 637 | return 0; |
| 530 | } | 638 | } |
| 531 | return -ENOMEM; | 639 | return -ENOMEM; |
| @@ -573,7 +681,7 @@ exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, | |||
| 573 | key.ex_mnt = mnt; | 681 | key.ex_mnt = mnt; |
| 574 | key.ex_dentry = dentry; | 682 | key.ex_dentry = dentry; |
| 575 | 683 | ||
| 576 | exp = svc_export_lookup(&key, 0); | 684 | exp = svc_export_lookup(&key); |
| 577 | if (exp != NULL) | 685 | if (exp != NULL) |
| 578 | switch (cache_check(&svc_export_cache, &exp->h, reqp)) { | 686 | switch (cache_check(&svc_export_cache, &exp->h, reqp)) { |
| 579 | case 0: break; | 687 | case 0: break; |
| @@ -654,7 +762,7 @@ static void exp_fsid_unhash(struct svc_export *exp) | |||
| 654 | ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); | 762 | ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); |
| 655 | if (ek && !IS_ERR(ek)) { | 763 | if (ek && !IS_ERR(ek)) { |
| 656 | ek->h.expiry_time = get_seconds()-1; | 764 | ek->h.expiry_time = get_seconds()-1; |
| 657 | expkey_put(&ek->h, &svc_expkey_cache); | 765 | cache_put(&ek->h, &svc_expkey_cache); |
| 658 | } | 766 | } |
| 659 | svc_expkey_cache.nextcheck = get_seconds(); | 767 | svc_expkey_cache.nextcheck = get_seconds(); |
| 660 | } | 768 | } |
| @@ -692,7 +800,7 @@ static void exp_unhash(struct svc_export *exp) | |||
| 692 | ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); | 800 | ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); |
| 693 | if (ek && !IS_ERR(ek)) { | 801 | if (ek && !IS_ERR(ek)) { |
| 694 | ek->h.expiry_time = get_seconds()-1; | 802 | ek->h.expiry_time = get_seconds()-1; |
| 695 | expkey_put(&ek->h, &svc_expkey_cache); | 803 | cache_put(&ek->h, &svc_expkey_cache); |
| 696 | } | 804 | } |
| 697 | svc_expkey_cache.nextcheck = get_seconds(); | 805 | svc_expkey_cache.nextcheck = get_seconds(); |
| 698 | } | 806 | } |
| @@ -741,8 +849,8 @@ exp_export(struct nfsctl_export *nxp) | |||
| 741 | if ((nxp->ex_flags & NFSEXP_FSID) && | 849 | if ((nxp->ex_flags & NFSEXP_FSID) && |
| 742 | (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) && | 850 | (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) && |
| 743 | !IS_ERR(fsid_key) && | 851 | !IS_ERR(fsid_key) && |
| 744 | fsid_key->ek_export && | 852 | fsid_key->ek_mnt && |
| 745 | fsid_key->ek_export != exp) | 853 | (fsid_key->ek_mnt != nd.mnt || fsid_key->ek_dentry != nd.dentry) ) |
| 746 | goto finish; | 854 | goto finish; |
| 747 | 855 | ||
| 748 | if (exp) { | 856 | if (exp) { |
| @@ -775,13 +883,13 @@ exp_export(struct nfsctl_export *nxp) | |||
| 775 | new.ex_anon_gid = nxp->ex_anon_gid; | 883 | new.ex_anon_gid = nxp->ex_anon_gid; |
| 776 | new.ex_fsid = nxp->ex_dev; | 884 | new.ex_fsid = nxp->ex_dev; |
| 777 | 885 | ||
| 778 | exp = svc_export_lookup(&new, 1); | 886 | exp = svc_export_lookup(&new); |
| 887 | if (exp) | ||
| 888 | exp = svc_export_update(&new, exp); | ||
| 779 | 889 | ||
| 780 | if (exp == NULL) | 890 | if (!exp) |
| 781 | goto finish; | 891 | goto finish; |
| 782 | 892 | ||
| 783 | err = 0; | ||
| 784 | |||
| 785 | if (exp_hash(clp, exp) || | 893 | if (exp_hash(clp, exp) || |
| 786 | exp_fsid_hash(clp, exp)) { | 894 | exp_fsid_hash(clp, exp)) { |
| 787 | /* failed to create at least one index */ | 895 | /* failed to create at least one index */ |
| @@ -794,7 +902,7 @@ finish: | |||
| 794 | if (exp) | 902 | if (exp) |
| 795 | exp_put(exp); | 903 | exp_put(exp); |
| 796 | if (fsid_key && !IS_ERR(fsid_key)) | 904 | if (fsid_key && !IS_ERR(fsid_key)) |
| 797 | expkey_put(&fsid_key->h, &svc_expkey_cache); | 905 | cache_put(&fsid_key->h, &svc_expkey_cache); |
| 798 | if (clp) | 906 | if (clp) |
| 799 | auth_domain_put(clp); | 907 | auth_domain_put(clp); |
| 800 | path_release(&nd); | 908 | path_release(&nd); |
| @@ -912,6 +1020,24 @@ out: | |||
| 912 | return err; | 1020 | return err; |
| 913 | } | 1021 | } |
| 914 | 1022 | ||
| 1023 | struct svc_export * | ||
| 1024 | exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, | ||
| 1025 | struct cache_req *reqp) | ||
| 1026 | { | ||
| 1027 | struct svc_export *exp; | ||
| 1028 | struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); | ||
| 1029 | if (!ek || IS_ERR(ek)) | ||
| 1030 | return ERR_PTR(PTR_ERR(ek)); | ||
| 1031 | |||
| 1032 | exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); | ||
| 1033 | cache_put(&ek->h, &svc_expkey_cache); | ||
| 1034 | |||
| 1035 | if (!exp || IS_ERR(exp)) | ||
| 1036 | return ERR_PTR(PTR_ERR(exp)); | ||
| 1037 | return exp; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | |||
| 915 | /* | 1041 | /* |
| 916 | * Called when we need the filehandle for the root of the pseudofs, | 1042 | * Called when we need the filehandle for the root of the pseudofs, |
| 917 | * for a given NFSv4 client. The root is defined to be the | 1043 | * for a given NFSv4 client. The root is defined to be the |
| @@ -922,6 +1048,7 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, | |||
| 922 | struct cache_req *creq) | 1048 | struct cache_req *creq) |
| 923 | { | 1049 | { |
| 924 | struct svc_expkey *fsid_key; | 1050 | struct svc_expkey *fsid_key; |
| 1051 | struct svc_export *exp; | ||
| 925 | int rv; | 1052 | int rv; |
| 926 | u32 fsidv[2]; | 1053 | u32 fsidv[2]; |
| 927 | 1054 | ||
| @@ -933,9 +1060,15 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, | |||
| 933 | if (!fsid_key || IS_ERR(fsid_key)) | 1060 | if (!fsid_key || IS_ERR(fsid_key)) |
| 934 | return nfserr_perm; | 1061 | return nfserr_perm; |
| 935 | 1062 | ||
| 936 | rv = fh_compose(fhp, fsid_key->ek_export, | 1063 | exp = exp_get_by_name(clp, fsid_key->ek_mnt, fsid_key->ek_dentry, creq); |
| 937 | fsid_key->ek_export->ex_dentry, NULL); | 1064 | if (exp == NULL) |
| 938 | expkey_put(&fsid_key->h, &svc_expkey_cache); | 1065 | rv = nfserr_perm; |
| 1066 | else if (IS_ERR(exp)) | ||
| 1067 | rv = nfserrno(PTR_ERR(exp)); | ||
| 1068 | else | ||
| 1069 | rv = fh_compose(fhp, exp, | ||
| 1070 | fsid_key->ek_dentry, NULL); | ||
| 1071 | cache_put(&fsid_key->h, &svc_expkey_cache); | ||
| 939 | return rv; | 1072 | return rv; |
| 940 | } | 1073 | } |
| 941 | 1074 | ||
| @@ -1054,7 +1187,7 @@ static int e_show(struct seq_file *m, void *p) | |||
| 1054 | cache_get(&exp->h); | 1187 | cache_get(&exp->h); |
| 1055 | if (cache_check(&svc_export_cache, &exp->h, NULL)) | 1188 | if (cache_check(&svc_export_cache, &exp->h, NULL)) |
| 1056 | return 0; | 1189 | return 0; |
| 1057 | if (cache_put(&exp->h, &svc_export_cache)) BUG(); | 1190 | cache_put(&exp->h, &svc_export_cache); |
| 1058 | return svc_export_show(m, &svc_export_cache, cp); | 1191 | return svc_export_show(m, &svc_export_cache, cp); |
| 1059 | } | 1192 | } |
| 1060 | 1193 | ||
| @@ -1129,7 +1262,6 @@ exp_delclient(struct nfsctl_client *ncp) | |||
| 1129 | */ | 1262 | */ |
| 1130 | if (dom) { | 1263 | if (dom) { |
| 1131 | err = auth_unix_forget_old(dom); | 1264 | err = auth_unix_forget_old(dom); |
| 1132 | dom->h.expiry_time = get_seconds(); | ||
| 1133 | auth_domain_put(dom); | 1265 | auth_domain_put(dom); |
| 1134 | } | 1266 | } |
| 1135 | 1267 | ||
diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 13369650cdf9..4b6aa60dfceb 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c | |||
| @@ -76,21 +76,18 @@ struct ent { | |||
| 76 | char authname[IDMAP_NAMESZ]; | 76 | char authname[IDMAP_NAMESZ]; |
| 77 | }; | 77 | }; |
| 78 | 78 | ||
| 79 | #define DefineSimpleCacheLookupMap(STRUCT, FUNC) \ | ||
| 80 | DefineCacheLookup(struct STRUCT, h, FUNC##_lookup, \ | ||
| 81 | (struct STRUCT *item, int set), /*no setup */, \ | ||
| 82 | & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ | ||
| 83 | STRUCT##_init(new, item), STRUCT##_update(tmp, item), 0) | ||
| 84 | |||
| 85 | /* Common entry handling */ | 79 | /* Common entry handling */ |
| 86 | 80 | ||
| 87 | #define ENT_HASHBITS 8 | 81 | #define ENT_HASHBITS 8 |
| 88 | #define ENT_HASHMAX (1 << ENT_HASHBITS) | 82 | #define ENT_HASHMAX (1 << ENT_HASHBITS) |
| 89 | #define ENT_HASHMASK (ENT_HASHMAX - 1) | 83 | #define ENT_HASHMASK (ENT_HASHMAX - 1) |
| 90 | 84 | ||
| 91 | static inline void | 85 | static void |
| 92 | ent_init(struct ent *new, struct ent *itm) | 86 | ent_init(struct cache_head *cnew, struct cache_head *citm) |
| 93 | { | 87 | { |
| 88 | struct ent *new = container_of(cnew, struct ent, h); | ||
| 89 | struct ent *itm = container_of(citm, struct ent, h); | ||
| 90 | |||
| 94 | new->id = itm->id; | 91 | new->id = itm->id; |
| 95 | new->type = itm->type; | 92 | new->type = itm->type; |
| 96 | 93 | ||
| @@ -98,19 +95,21 @@ ent_init(struct ent *new, struct ent *itm) | |||
| 98 | strlcpy(new->authname, itm->authname, sizeof(new->name)); | 95 | strlcpy(new->authname, itm->authname, sizeof(new->name)); |
| 99 | } | 96 | } |
| 100 | 97 | ||
| 101 | static inline void | 98 | static void |
| 102 | ent_update(struct ent *new, struct ent *itm) | 99 | ent_put(struct kref *ref) |
| 103 | { | 100 | { |
| 104 | ent_init(new, itm); | 101 | struct ent *map = container_of(ref, struct ent, h.ref); |
| 102 | kfree(map); | ||
| 105 | } | 103 | } |
| 106 | 104 | ||
| 107 | static void | 105 | static struct cache_head * |
| 108 | ent_put(struct cache_head *ch, struct cache_detail *cd) | 106 | ent_alloc(void) |
| 109 | { | 107 | { |
| 110 | if (cache_put(ch, cd)) { | 108 | struct ent *e = kmalloc(sizeof(*e), GFP_KERNEL); |
| 111 | struct ent *map = container_of(ch, struct ent, h); | 109 | if (e) |
| 112 | kfree(map); | 110 | return &e->h; |
| 113 | } | 111 | else |
| 112 | return NULL; | ||
| 114 | } | 113 | } |
| 115 | 114 | ||
| 116 | /* | 115 | /* |
| @@ -149,9 +148,12 @@ idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, | |||
| 149 | (*bpp)[-1] = '\n'; | 148 | (*bpp)[-1] = '\n'; |
| 150 | } | 149 | } |
| 151 | 150 | ||
| 152 | static inline int | 151 | static int |
| 153 | idtoname_match(struct ent *a, struct ent *b) | 152 | idtoname_match(struct cache_head *ca, struct cache_head *cb) |
| 154 | { | 153 | { |
| 154 | struct ent *a = container_of(ca, struct ent, h); | ||
| 155 | struct ent *b = container_of(cb, struct ent, h); | ||
| 156 | |||
| 155 | return (a->id == b->id && a->type == b->type && | 157 | return (a->id == b->id && a->type == b->type && |
| 156 | strcmp(a->authname, b->authname) == 0); | 158 | strcmp(a->authname, b->authname) == 0); |
| 157 | } | 159 | } |
| @@ -184,7 +186,8 @@ warn_no_idmapd(struct cache_detail *detail) | |||
| 184 | 186 | ||
| 185 | 187 | ||
| 186 | static int idtoname_parse(struct cache_detail *, char *, int); | 188 | static int idtoname_parse(struct cache_detail *, char *, int); |
| 187 | static struct ent *idtoname_lookup(struct ent *, int); | 189 | static struct ent *idtoname_lookup(struct ent *); |
| 190 | static struct ent *idtoname_update(struct ent *, struct ent *); | ||
| 188 | 191 | ||
| 189 | static struct cache_detail idtoname_cache = { | 192 | static struct cache_detail idtoname_cache = { |
| 190 | .owner = THIS_MODULE, | 193 | .owner = THIS_MODULE, |
| @@ -196,6 +199,10 @@ static struct cache_detail idtoname_cache = { | |||
| 196 | .cache_parse = idtoname_parse, | 199 | .cache_parse = idtoname_parse, |
| 197 | .cache_show = idtoname_show, | 200 | .cache_show = idtoname_show, |
| 198 | .warn_no_listener = warn_no_idmapd, | 201 | .warn_no_listener = warn_no_idmapd, |
| 202 | .match = idtoname_match, | ||
| 203 | .init = ent_init, | ||
| 204 | .update = ent_init, | ||
| 205 | .alloc = ent_alloc, | ||
| 199 | }; | 206 | }; |
| 200 | 207 | ||
| 201 | int | 208 | int |
| @@ -238,6 +245,11 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen) | |||
| 238 | if (ent.h.expiry_time == 0) | 245 | if (ent.h.expiry_time == 0) |
| 239 | goto out; | 246 | goto out; |
| 240 | 247 | ||
| 248 | error = -ENOMEM; | ||
| 249 | res = idtoname_lookup(&ent); | ||
| 250 | if (!res) | ||
| 251 | goto out; | ||
| 252 | |||
| 241 | /* Name */ | 253 | /* Name */ |
| 242 | error = qword_get(&buf, buf1, PAGE_SIZE); | 254 | error = qword_get(&buf, buf1, PAGE_SIZE); |
| 243 | if (error == -EINVAL) | 255 | if (error == -EINVAL) |
| @@ -252,10 +264,11 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen) | |||
| 252 | memcpy(ent.name, buf1, sizeof(ent.name)); | 264 | memcpy(ent.name, buf1, sizeof(ent.name)); |
| 253 | } | 265 | } |
| 254 | error = -ENOMEM; | 266 | error = -ENOMEM; |
| 255 | if ((res = idtoname_lookup(&ent, 1)) == NULL) | 267 | res = idtoname_update(&ent, res); |
| 268 | if (res == NULL) | ||
| 256 | goto out; | 269 | goto out; |
| 257 | 270 | ||
| 258 | ent_put(&res->h, &idtoname_cache); | 271 | cache_put(&res->h, &idtoname_cache); |
| 259 | 272 | ||
| 260 | error = 0; | 273 | error = 0; |
| 261 | out: | 274 | out: |
| @@ -264,7 +277,31 @@ out: | |||
| 264 | return error; | 277 | return error; |
| 265 | } | 278 | } |
| 266 | 279 | ||
| 267 | static DefineSimpleCacheLookupMap(ent, idtoname); | 280 | |
| 281 | static struct ent * | ||
| 282 | idtoname_lookup(struct ent *item) | ||
| 283 | { | ||
| 284 | struct cache_head *ch = sunrpc_cache_lookup(&idtoname_cache, | ||
| 285 | &item->h, | ||
| 286 | idtoname_hash(item)); | ||
| 287 | if (ch) | ||
| 288 | return container_of(ch, struct ent, h); | ||
| 289 | else | ||
| 290 | return NULL; | ||
| 291 | } | ||
| 292 | |||
| 293 | static struct ent * | ||
| 294 | idtoname_update(struct ent *new, struct ent *old) | ||
| 295 | { | ||
| 296 | struct cache_head *ch = sunrpc_cache_update(&idtoname_cache, | ||
| 297 | &new->h, &old->h, | ||
| 298 | idtoname_hash(new)); | ||
| 299 | if (ch) | ||
| 300 | return container_of(ch, struct ent, h); | ||
| 301 | else | ||
| 302 | return NULL; | ||
| 303 | } | ||
| 304 | |||
| 268 | 305 | ||
| 269 | /* | 306 | /* |
| 270 | * Name -> ID cache | 307 | * Name -> ID cache |
| @@ -291,9 +328,12 @@ nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, | |||
| 291 | (*bpp)[-1] = '\n'; | 328 | (*bpp)[-1] = '\n'; |
| 292 | } | 329 | } |
| 293 | 330 | ||
| 294 | static inline int | 331 | static int |
| 295 | nametoid_match(struct ent *a, struct ent *b) | 332 | nametoid_match(struct cache_head *ca, struct cache_head *cb) |
| 296 | { | 333 | { |
| 334 | struct ent *a = container_of(ca, struct ent, h); | ||
| 335 | struct ent *b = container_of(cb, struct ent, h); | ||
| 336 | |||
| 297 | return (a->type == b->type && strcmp(a->name, b->name) == 0 && | 337 | return (a->type == b->type && strcmp(a->name, b->name) == 0 && |
| 298 | strcmp(a->authname, b->authname) == 0); | 338 | strcmp(a->authname, b->authname) == 0); |
| 299 | } | 339 | } |
| @@ -317,7 +357,8 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) | |||
| 317 | return 0; | 357 | return 0; |
| 318 | } | 358 | } |
| 319 | 359 | ||
| 320 | static struct ent *nametoid_lookup(struct ent *, int); | 360 | static struct ent *nametoid_lookup(struct ent *); |
| 361 | static struct ent *nametoid_update(struct ent *, struct ent *); | ||
| 321 | static int nametoid_parse(struct cache_detail *, char *, int); | 362 | static int nametoid_parse(struct cache_detail *, char *, int); |
| 322 | 363 | ||
| 323 | static struct cache_detail nametoid_cache = { | 364 | static struct cache_detail nametoid_cache = { |
| @@ -330,6 +371,10 @@ static struct cache_detail nametoid_cache = { | |||
| 330 | .cache_parse = nametoid_parse, | 371 | .cache_parse = nametoid_parse, |
| 331 | .cache_show = nametoid_show, | 372 | .cache_show = nametoid_show, |
| 332 | .warn_no_listener = warn_no_idmapd, | 373 | .warn_no_listener = warn_no_idmapd, |
| 374 | .match = nametoid_match, | ||
| 375 | .init = ent_init, | ||
| 376 | .update = ent_init, | ||
| 377 | .alloc = ent_alloc, | ||
| 333 | }; | 378 | }; |
| 334 | 379 | ||
| 335 | static int | 380 | static int |
| @@ -379,10 +424,14 @@ nametoid_parse(struct cache_detail *cd, char *buf, int buflen) | |||
| 379 | set_bit(CACHE_NEGATIVE, &ent.h.flags); | 424 | set_bit(CACHE_NEGATIVE, &ent.h.flags); |
| 380 | 425 | ||
| 381 | error = -ENOMEM; | 426 | error = -ENOMEM; |
| 382 | if ((res = nametoid_lookup(&ent, 1)) == NULL) | 427 | res = nametoid_lookup(&ent); |
| 428 | if (res == NULL) | ||
| 429 | goto out; | ||
| 430 | res = nametoid_update(&ent, res); | ||
| 431 | if (res == NULL) | ||
| 383 | goto out; | 432 | goto out; |
| 384 | 433 | ||
| 385 | ent_put(&res->h, &nametoid_cache); | 434 | cache_put(&res->h, &nametoid_cache); |
| 386 | error = 0; | 435 | error = 0; |
| 387 | out: | 436 | out: |
| 388 | kfree(buf1); | 437 | kfree(buf1); |
| @@ -390,7 +439,30 @@ out: | |||
| 390 | return (error); | 439 | return (error); |
| 391 | } | 440 | } |
| 392 | 441 | ||
| 393 | static DefineSimpleCacheLookupMap(ent, nametoid); | 442 | |
| 443 | static struct ent * | ||
| 444 | nametoid_lookup(struct ent *item) | ||
| 445 | { | ||
| 446 | struct cache_head *ch = sunrpc_cache_lookup(&nametoid_cache, | ||
| 447 | &item->h, | ||
| 448 | nametoid_hash(item)); | ||
| 449 | if (ch) | ||
| 450 | return container_of(ch, struct ent, h); | ||
| 451 | else | ||
| 452 | return NULL; | ||
| 453 | } | ||
| 454 | |||
| 455 | static struct ent * | ||
| 456 | nametoid_update(struct ent *new, struct ent *old) | ||
| 457 | { | ||
| 458 | struct cache_head *ch = sunrpc_cache_update(&nametoid_cache, | ||
| 459 | &new->h, &old->h, | ||
| 460 | nametoid_hash(new)); | ||
| 461 | if (ch) | ||
| 462 | return container_of(ch, struct ent, h); | ||
| 463 | else | ||
| 464 | return NULL; | ||
| 465 | } | ||
| 394 | 466 | ||
| 395 | /* | 467 | /* |
| 396 | * Exported API | 468 | * Exported API |
| @@ -458,24 +530,24 @@ idmap_defer(struct cache_req *req) | |||
| 458 | } | 530 | } |
| 459 | 531 | ||
| 460 | static inline int | 532 | static inline int |
| 461 | do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *, int), struct ent *key, | 533 | do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *), struct ent *key, |
| 462 | struct cache_detail *detail, struct ent **item, | 534 | struct cache_detail *detail, struct ent **item, |
| 463 | struct idmap_defer_req *mdr) | 535 | struct idmap_defer_req *mdr) |
| 464 | { | 536 | { |
| 465 | *item = lookup_fn(key, 0); | 537 | *item = lookup_fn(key); |
| 466 | if (!*item) | 538 | if (!*item) |
| 467 | return -ENOMEM; | 539 | return -ENOMEM; |
| 468 | return cache_check(detail, &(*item)->h, &mdr->req); | 540 | return cache_check(detail, &(*item)->h, &mdr->req); |
| 469 | } | 541 | } |
| 470 | 542 | ||
| 471 | static inline int | 543 | static inline int |
| 472 | do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *, int), | 544 | do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *), |
| 473 | struct ent *key, struct cache_detail *detail, | 545 | struct ent *key, struct cache_detail *detail, |
| 474 | struct ent **item) | 546 | struct ent **item) |
| 475 | { | 547 | { |
| 476 | int ret = -ENOMEM; | 548 | int ret = -ENOMEM; |
| 477 | 549 | ||
| 478 | *item = lookup_fn(key, 0); | 550 | *item = lookup_fn(key); |
| 479 | if (!*item) | 551 | if (!*item) |
| 480 | goto out_err; | 552 | goto out_err; |
| 481 | ret = -ETIMEDOUT; | 553 | ret = -ETIMEDOUT; |
| @@ -488,7 +560,7 @@ do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *, int), | |||
| 488 | goto out_put; | 560 | goto out_put; |
| 489 | return 0; | 561 | return 0; |
| 490 | out_put: | 562 | out_put: |
| 491 | ent_put(&(*item)->h, detail); | 563 | cache_put(&(*item)->h, detail); |
| 492 | out_err: | 564 | out_err: |
| 493 | *item = NULL; | 565 | *item = NULL; |
| 494 | return ret; | 566 | return ret; |
| @@ -496,7 +568,7 @@ out_err: | |||
| 496 | 568 | ||
| 497 | static int | 569 | static int |
| 498 | idmap_lookup(struct svc_rqst *rqstp, | 570 | idmap_lookup(struct svc_rqst *rqstp, |
| 499 | struct ent *(*lookup_fn)(struct ent *, int), struct ent *key, | 571 | struct ent *(*lookup_fn)(struct ent *), struct ent *key, |
| 500 | struct cache_detail *detail, struct ent **item) | 572 | struct cache_detail *detail, struct ent **item) |
| 501 | { | 573 | { |
| 502 | struct idmap_defer_req *mdr; | 574 | struct idmap_defer_req *mdr; |
| @@ -539,7 +611,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen | |||
| 539 | if (ret) | 611 | if (ret) |
| 540 | return ret; | 612 | return ret; |
| 541 | *id = item->id; | 613 | *id = item->id; |
| 542 | ent_put(&item->h, &nametoid_cache); | 614 | cache_put(&item->h, &nametoid_cache); |
| 543 | return 0; | 615 | return 0; |
| 544 | } | 616 | } |
| 545 | 617 | ||
| @@ -561,7 +633,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) | |||
| 561 | ret = strlen(item->name); | 633 | ret = strlen(item->name); |
| 562 | BUG_ON(ret > IDMAP_NAMESZ); | 634 | BUG_ON(ret > IDMAP_NAMESZ); |
| 563 | memcpy(name, item->name, ret); | 635 | memcpy(name, item->name, ret); |
| 564 | ent_put(&item->h, &idtoname_cache); | 636 | cache_put(&item->h, &idtoname_cache); |
| 565 | return ret; | 637 | return ret; |
| 566 | } | 638 | } |
| 567 | 639 | ||
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 7a3e397b4ed3..3f2ec2e6d06c 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c | |||
| @@ -506,7 +506,7 @@ fh_put(struct svc_fh *fhp) | |||
| 506 | nfsd_nr_put++; | 506 | nfsd_nr_put++; |
| 507 | } | 507 | } |
| 508 | if (exp) { | 508 | if (exp) { |
| 509 | svc_export_put(&exp->h, &svc_export_cache); | 509 | cache_put(&exp->h, &svc_export_cache); |
| 510 | fhp->fh_export = NULL; | 510 | fhp->fh_export = NULL; |
| 511 | } | 511 | } |
| 512 | return; | 512 | return; |
diff --git a/fs/partitions/check.c b/fs/partitions/check.c index f924f459bdb8..af0cb4b9e784 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c | |||
| @@ -297,6 +297,25 @@ struct kobj_type ktype_part = { | |||
| 297 | .sysfs_ops = &part_sysfs_ops, | 297 | .sysfs_ops = &part_sysfs_ops, |
| 298 | }; | 298 | }; |
| 299 | 299 | ||
| 300 | static inline void partition_sysfs_add_subdir(struct hd_struct *p) | ||
| 301 | { | ||
| 302 | struct kobject *k; | ||
| 303 | |||
| 304 | k = kobject_get(&p->kobj); | ||
| 305 | p->holder_dir = kobject_add_dir(k, "holders"); | ||
| 306 | kobject_put(k); | ||
| 307 | } | ||
| 308 | |||
| 309 | static inline void disk_sysfs_add_subdirs(struct gendisk *disk) | ||
| 310 | { | ||
| 311 | struct kobject *k; | ||
| 312 | |||
| 313 | k = kobject_get(&disk->kobj); | ||
| 314 | disk->holder_dir = kobject_add_dir(k, "holders"); | ||
| 315 | disk->slave_dir = kobject_add_dir(k, "slaves"); | ||
| 316 | kobject_put(k); | ||
| 317 | } | ||
| 318 | |||
| 300 | void delete_partition(struct gendisk *disk, int part) | 319 | void delete_partition(struct gendisk *disk, int part) |
| 301 | { | 320 | { |
| 302 | struct hd_struct *p = disk->part[part-1]; | 321 | struct hd_struct *p = disk->part[part-1]; |
| @@ -310,6 +329,8 @@ void delete_partition(struct gendisk *disk, int part) | |||
| 310 | p->ios[0] = p->ios[1] = 0; | 329 | p->ios[0] = p->ios[1] = 0; |
| 311 | p->sectors[0] = p->sectors[1] = 0; | 330 | p->sectors[0] = p->sectors[1] = 0; |
| 312 | devfs_remove("%s/part%d", disk->devfs_name, part); | 331 | devfs_remove("%s/part%d", disk->devfs_name, part); |
| 332 | if (p->holder_dir) | ||
| 333 | kobject_unregister(p->holder_dir); | ||
| 313 | kobject_unregister(&p->kobj); | 334 | kobject_unregister(&p->kobj); |
| 314 | } | 335 | } |
| 315 | 336 | ||
| @@ -337,6 +358,7 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) | |||
| 337 | p->kobj.parent = &disk->kobj; | 358 | p->kobj.parent = &disk->kobj; |
| 338 | p->kobj.ktype = &ktype_part; | 359 | p->kobj.ktype = &ktype_part; |
| 339 | kobject_register(&p->kobj); | 360 | kobject_register(&p->kobj); |
| 361 | partition_sysfs_add_subdir(p); | ||
| 340 | disk->part[part-1] = p; | 362 | disk->part[part-1] = p; |
| 341 | } | 363 | } |
| 342 | 364 | ||
| @@ -383,6 +405,7 @@ void register_disk(struct gendisk *disk) | |||
| 383 | if ((err = kobject_add(&disk->kobj))) | 405 | if ((err = kobject_add(&disk->kobj))) |
| 384 | return; | 406 | return; |
| 385 | disk_sysfs_symlinks(disk); | 407 | disk_sysfs_symlinks(disk); |
| 408 | disk_sysfs_add_subdirs(disk); | ||
| 386 | kobject_uevent(&disk->kobj, KOBJ_ADD); | 409 | kobject_uevent(&disk->kobj, KOBJ_ADD); |
| 387 | 410 | ||
| 388 | /* No minors to use for partitions */ | 411 | /* No minors to use for partitions */ |
| @@ -483,6 +506,10 @@ void del_gendisk(struct gendisk *disk) | |||
| 483 | 506 | ||
| 484 | devfs_remove_disk(disk); | 507 | devfs_remove_disk(disk); |
| 485 | 508 | ||
| 509 | if (disk->holder_dir) | ||
| 510 | kobject_unregister(disk->holder_dir); | ||
| 511 | if (disk->slave_dir) | ||
| 512 | kobject_unregister(disk->slave_dir); | ||
| 486 | if (disk->driverfs_dev) { | 513 | if (disk->driverfs_dev) { |
| 487 | char *disk_name = make_block_name(disk); | 514 | char *disk_name = make_block_name(disk); |
| 488 | sysfs_remove_link(&disk->kobj, "device"); | 515 | sysfs_remove_link(&disk->kobj, "device"); |
diff --git a/include/asm-alpha/mmzone.h b/include/asm-alpha/mmzone.h index a011ef4cf3d3..192d80c875b0 100644 --- a/include/asm-alpha/mmzone.h +++ b/include/asm-alpha/mmzone.h | |||
| @@ -59,9 +59,6 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) | |||
| 59 | #define kvaddr_to_nid(kaddr) pa_to_nid(__pa(kaddr)) | 59 | #define kvaddr_to_nid(kaddr) pa_to_nid(__pa(kaddr)) |
| 60 | #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) | 60 | #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) |
| 61 | 61 | ||
| 62 | #define local_mapnr(kvaddr) \ | ||
| 63 | ((__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr))) | ||
| 64 | |||
| 65 | /* | 62 | /* |
| 66 | * Given a kaddr, LOCAL_BASE_ADDR finds the owning node of the memory | 63 | * Given a kaddr, LOCAL_BASE_ADDR finds the owning node of the memory |
| 67 | * and returns the kaddr corresponding to first physical page in the | 64 | * and returns the kaddr corresponding to first physical page in the |
| @@ -86,8 +83,7 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) | |||
| 86 | pte_t pte; \ | 83 | pte_t pte; \ |
| 87 | unsigned long pfn; \ | 84 | unsigned long pfn; \ |
| 88 | \ | 85 | \ |
| 89 | pfn = ((unsigned long)((page)-page_zone(page)->zone_mem_map)) << 32; \ | 86 | pfn = page_to_pfn(page) << 32; \ |
| 90 | pfn += page_zone(page)->zone_start_pfn << 32; \ | ||
| 91 | pte_val(pte) = pfn | pgprot_val(pgprot); \ | 87 | pte_val(pte) = pfn | pgprot_val(pgprot); \ |
| 92 | \ | 88 | \ |
| 93 | pte; \ | 89 | pte; \ |
| @@ -104,19 +100,8 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) | |||
| 104 | __xx; \ | 100 | __xx; \ |
| 105 | }) | 101 | }) |
| 106 | 102 | ||
| 107 | #define pfn_to_page(pfn) \ | ||
| 108 | ({ \ | ||
| 109 | unsigned long kaddr = (unsigned long)__va((pfn) << PAGE_SHIFT); \ | ||
| 110 | (NODE_DATA(kvaddr_to_nid(kaddr))->node_mem_map + local_mapnr(kaddr)); \ | ||
| 111 | }) | ||
| 112 | |||
| 113 | #define page_to_pfn(page) \ | ||
| 114 | ((page) - page_zone(page)->zone_mem_map + \ | ||
| 115 | (page_zone(page)->zone_start_pfn)) | ||
| 116 | |||
| 117 | #define page_to_pa(page) \ | 103 | #define page_to_pa(page) \ |
| 118 | ((( (page) - page_zone(page)->zone_mem_map ) \ | 104 | (page_to_pfn(page) << PAGE_SHIFT) |
| 119 | + page_zone(page)->zone_start_pfn) << PAGE_SHIFT) | ||
| 120 | 105 | ||
| 121 | #define pfn_to_nid(pfn) pa_to_nid(((u64)(pfn) << PAGE_SHIFT)) | 106 | #define pfn_to_nid(pfn) pa_to_nid(((u64)(pfn) << PAGE_SHIFT)) |
| 122 | #define pfn_valid(pfn) \ | 107 | #define pfn_valid(pfn) \ |
diff --git a/include/asm-alpha/page.h b/include/asm-alpha/page.h index fa0b41b164a7..61bcf70b5eac 100644 --- a/include/asm-alpha/page.h +++ b/include/asm-alpha/page.h | |||
| @@ -85,8 +85,6 @@ typedef unsigned long pgprot_t; | |||
| 85 | #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) | 85 | #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) |
| 86 | #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) | 86 | #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) |
| 87 | #ifndef CONFIG_DISCONTIGMEM | 87 | #ifndef CONFIG_DISCONTIGMEM |
| 88 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 89 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) | ||
| 90 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) | 88 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) |
| 91 | 89 | ||
| 92 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 90 | #define pfn_valid(pfn) ((pfn) < max_mapnr) |
| @@ -95,9 +93,9 @@ typedef unsigned long pgprot_t; | |||
| 95 | 93 | ||
| 96 | #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ | 94 | #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ |
| 97 | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) | 95 | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) |
| 98 | |||
| 99 | #endif /* __KERNEL__ */ | 96 | #endif /* __KERNEL__ */ |
| 100 | 97 | ||
| 98 | #include <asm-generic/memory_model.h> | ||
| 101 | #include <asm-generic/page.h> | 99 | #include <asm-generic/page.h> |
| 102 | 100 | ||
| 103 | #endif /* _ALPHA_PAGE_H */ | 101 | #endif /* _ALPHA_PAGE_H */ |
diff --git a/include/asm-arm/memory.h b/include/asm-arm/memory.h index b4e1146ab682..afa5c3ea077c 100644 --- a/include/asm-arm/memory.h +++ b/include/asm-arm/memory.h | |||
| @@ -172,9 +172,7 @@ static inline __deprecated void *bus_to_virt(unsigned long x) | |||
| 172 | * virt_addr_valid(k) indicates whether a virtual address is valid | 172 | * virt_addr_valid(k) indicates whether a virtual address is valid |
| 173 | */ | 173 | */ |
| 174 | #ifndef CONFIG_DISCONTIGMEM | 174 | #ifndef CONFIG_DISCONTIGMEM |
| 175 | 175 | #define ARCH_PFN_OFFSET (PHYS_PFN_OFFSET) | |
| 176 | #define page_to_pfn(page) (((page) - mem_map) + PHYS_PFN_OFFSET) | ||
| 177 | #define pfn_to_page(pfn) ((mem_map + (pfn)) - PHYS_PFN_OFFSET) | ||
| 178 | #define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr)) | 176 | #define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr)) |
| 179 | 177 | ||
| 180 | #define virt_to_page(kaddr) (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) | 178 | #define virt_to_page(kaddr) (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) |
| @@ -189,13 +187,8 @@ static inline __deprecated void *bus_to_virt(unsigned long x) | |||
| 189 | * around in memory. | 187 | * around in memory. |
| 190 | */ | 188 | */ |
| 191 | #include <linux/numa.h> | 189 | #include <linux/numa.h> |
| 192 | 190 | #define arch_pfn_to_nid(pfn) (PFN_TO_NID(pfn)) | |
| 193 | #define page_to_pfn(page) \ | 191 | #define arch_local_page_offset(pfn, nid) (LOCAL_MAP_NR((pfn) << PAGE_OFFSET)) |
| 194 | (( (page) - page_zone(page)->zone_mem_map) \ | ||
| 195 | + page_zone(page)->zone_start_pfn) | ||
| 196 | |||
| 197 | #define pfn_to_page(pfn) \ | ||
| 198 | (PFN_TO_MAPBASE(pfn) + LOCAL_MAP_NR((pfn) << PAGE_SHIFT)) | ||
| 199 | 192 | ||
| 200 | #define pfn_valid(pfn) \ | 193 | #define pfn_valid(pfn) \ |
| 201 | ({ \ | 194 | ({ \ |
| @@ -243,4 +236,6 @@ static inline __deprecated void *bus_to_virt(unsigned long x) | |||
| 243 | 236 | ||
| 244 | #endif | 237 | #endif |
| 245 | 238 | ||
| 239 | #include <asm-generic/memory_model.h> | ||
| 240 | |||
| 246 | #endif | 241 | #endif |
diff --git a/include/asm-arm/rtc.h b/include/asm-arm/rtc.h index 370dfe77589d..1a5c9232a91e 100644 --- a/include/asm-arm/rtc.h +++ b/include/asm-arm/rtc.h | |||
| @@ -25,9 +25,6 @@ struct rtc_ops { | |||
| 25 | int (*proc)(char *buf); | 25 | int (*proc)(char *buf); |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | void rtc_time_to_tm(unsigned long, struct rtc_time *); | ||
| 29 | int rtc_tm_to_time(struct rtc_time *, unsigned long *); | ||
| 30 | int rtc_valid_tm(struct rtc_time *); | ||
| 31 | void rtc_next_alarm_time(struct rtc_time *, struct rtc_time *, struct rtc_time *); | 28 | void rtc_next_alarm_time(struct rtc_time *, struct rtc_time *, struct rtc_time *); |
| 32 | void rtc_update(unsigned long, unsigned long); | 29 | void rtc_update(unsigned long, unsigned long); |
| 33 | int register_rtc(struct rtc_ops *); | 30 | int register_rtc(struct rtc_ops *); |
diff --git a/include/asm-arm26/memory.h b/include/asm-arm26/memory.h index 20d78616f650..a65f10b80dfb 100644 --- a/include/asm-arm26/memory.h +++ b/include/asm-arm26/memory.h | |||
| @@ -81,8 +81,7 @@ static inline void *phys_to_virt(unsigned long x) | |||
| 81 | * virt_to_page(k) convert a _valid_ virtual address to struct page * | 81 | * virt_to_page(k) convert a _valid_ virtual address to struct page * |
| 82 | * virt_addr_valid(k) indicates whether a virtual address is valid | 82 | * virt_addr_valid(k) indicates whether a virtual address is valid |
| 83 | */ | 83 | */ |
| 84 | #define page_to_pfn(page) (((page) - mem_map) + PHYS_PFN_OFFSET) | 84 | #define ARCH_PFN_OFFSET (PHYS_PFN_OFFSET) |
| 85 | #define pfn_to_page(pfn) ((mem_map + (pfn)) - PHYS_PFN_OFFSET) | ||
| 86 | #define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr)) | 85 | #define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr)) |
| 87 | 86 | ||
| 88 | #define virt_to_page(kaddr) (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) | 87 | #define virt_to_page(kaddr) (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) |
| @@ -98,4 +97,5 @@ static inline void *phys_to_virt(unsigned long x) | |||
| 98 | */ | 97 | */ |
| 99 | #define page_to_bus(page) (page_address(page)) | 98 | #define page_to_bus(page) (page_address(page)) |
| 100 | 99 | ||
| 100 | #include <asm-generic/memory_model.h> | ||
| 101 | #endif | 101 | #endif |
diff --git a/include/asm-cris/page.h b/include/asm-cris/page.h index c99c478c482f..3787633e6209 100644 --- a/include/asm-cris/page.h +++ b/include/asm-cris/page.h | |||
| @@ -43,8 +43,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 43 | 43 | ||
| 44 | /* On CRIS the PFN numbers doesn't start at 0 so we have to compensate */ | 44 | /* On CRIS the PFN numbers doesn't start at 0 so we have to compensate */ |
| 45 | /* for that before indexing into the page table starting at mem_map */ | 45 | /* for that before indexing into the page table starting at mem_map */ |
| 46 | #define pfn_to_page(pfn) (mem_map + ((pfn) - (PAGE_OFFSET >> PAGE_SHIFT))) | 46 | #define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) |
| 47 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map) + (PAGE_OFFSET >> PAGE_SHIFT)) | ||
| 48 | #define pfn_valid(pfn) (((pfn) - (PAGE_OFFSET >> PAGE_SHIFT)) < max_mapnr) | 47 | #define pfn_valid(pfn) (((pfn) - (PAGE_OFFSET >> PAGE_SHIFT)) < max_mapnr) |
| 49 | 48 | ||
| 50 | /* to index into the page map. our pages all start at physical addr PAGE_OFFSET so | 49 | /* to index into the page map. our pages all start at physical addr PAGE_OFFSET so |
| @@ -77,6 +76,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 77 | 76 | ||
| 78 | #endif /* __KERNEL__ */ | 77 | #endif /* __KERNEL__ */ |
| 79 | 78 | ||
| 79 | #include <asm-generic/memory_model.h> | ||
| 80 | #include <asm-generic/page.h> | 80 | #include <asm-generic/page.h> |
| 81 | 81 | ||
| 82 | #endif /* _CRIS_PAGE_H */ | 82 | #endif /* _CRIS_PAGE_H */ |
diff --git a/include/asm-frv/futex.h b/include/asm-frv/futex.h index fca9d90e32c9..08b3d1da3583 100644 --- a/include/asm-frv/futex.h +++ b/include/asm-frv/futex.h | |||
| @@ -9,5 +9,11 @@ | |||
| 9 | 9 | ||
| 10 | extern int futex_atomic_op_inuser(int encoded_op, int __user *uaddr); | 10 | extern int futex_atomic_op_inuser(int encoded_op, int __user *uaddr); |
| 11 | 11 | ||
| 12 | static inline int | ||
| 13 | futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) | ||
| 14 | { | ||
| 15 | return -ENOSYS; | ||
| 16 | } | ||
| 17 | |||
| 12 | #endif | 18 | #endif |
| 13 | #endif | 19 | #endif |
diff --git a/include/asm-frv/page.h b/include/asm-frv/page.h index b8221b611b5c..dc0f7e08a4c2 100644 --- a/include/asm-frv/page.h +++ b/include/asm-frv/page.h | |||
| @@ -57,13 +57,9 @@ extern unsigned long min_low_pfn; | |||
| 57 | extern unsigned long max_pfn; | 57 | extern unsigned long max_pfn; |
| 58 | 58 | ||
| 59 | #ifdef CONFIG_MMU | 59 | #ifdef CONFIG_MMU |
| 60 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 61 | #define page_to_pfn(page) ((unsigned long) ((page) - mem_map)) | ||
| 62 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 60 | #define pfn_valid(pfn) ((pfn) < max_mapnr) |
| 63 | |||
| 64 | #else | 61 | #else |
| 65 | #define pfn_to_page(pfn) (&mem_map[(pfn) - (PAGE_OFFSET >> PAGE_SHIFT)]) | 62 | #define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) |
| 66 | #define page_to_pfn(page) ((PAGE_OFFSET >> PAGE_SHIFT) + (unsigned long) ((page) - mem_map)) | ||
| 67 | #define pfn_valid(pfn) ((pfn) >= min_low_pfn && (pfn) < max_low_pfn) | 63 | #define pfn_valid(pfn) ((pfn) >= min_low_pfn && (pfn) < max_low_pfn) |
| 68 | 64 | ||
| 69 | #endif | 65 | #endif |
| @@ -87,6 +83,7 @@ extern unsigned long max_pfn; | |||
| 87 | #define WANT_PAGE_VIRTUAL 1 | 83 | #define WANT_PAGE_VIRTUAL 1 |
| 88 | #endif | 84 | #endif |
| 89 | 85 | ||
| 86 | #include <asm-generic/memory_model.h> | ||
| 90 | #include <asm-generic/page.h> | 87 | #include <asm-generic/page.h> |
| 91 | 88 | ||
| 92 | #endif /* _ASM_PAGE_H */ | 89 | #endif /* _ASM_PAGE_H */ |
diff --git a/include/asm-generic/futex.h b/include/asm-generic/futex.h index 3ae2c7347549..df893c160318 100644 --- a/include/asm-generic/futex.h +++ b/include/asm-generic/futex.h | |||
| @@ -49,5 +49,11 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) | |||
| 49 | return ret; | 49 | return ret; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | static inline int | ||
| 53 | futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) | ||
| 54 | { | ||
| 55 | return -ENOSYS; | ||
| 56 | } | ||
| 57 | |||
| 52 | #endif | 58 | #endif |
| 53 | #endif | 59 | #endif |
diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h new file mode 100644 index 000000000000..0cfb086dd373 --- /dev/null +++ b/include/asm-generic/memory_model.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | #ifndef __ASM_MEMORY_MODEL_H | ||
| 2 | #define __ASM_MEMORY_MODEL_H | ||
| 3 | |||
| 4 | #ifdef __KERNEL__ | ||
| 5 | #ifndef __ASSEMBLY__ | ||
| 6 | |||
| 7 | #if defined(CONFIG_FLATMEM) | ||
| 8 | |||
| 9 | #ifndef ARCH_PFN_OFFSET | ||
| 10 | #define ARCH_PFN_OFFSET (0UL) | ||
| 11 | #endif | ||
| 12 | |||
| 13 | #elif defined(CONFIG_DISCONTIGMEM) | ||
| 14 | |||
| 15 | #ifndef arch_pfn_to_nid | ||
| 16 | #define arch_pfn_to_nid(pfn) pfn_to_nid(pfn) | ||
| 17 | #endif | ||
| 18 | |||
| 19 | #ifndef arch_local_page_offset | ||
| 20 | #define arch_local_page_offset(pfn, nid) \ | ||
| 21 | ((pfn) - NODE_DATA(nid)->node_start_pfn) | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #endif /* CONFIG_DISCONTIGMEM */ | ||
| 25 | |||
| 26 | #ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE | ||
| 27 | struct page; | ||
| 28 | /* this is useful when inlined pfn_to_page is too big */ | ||
| 29 | extern struct page *pfn_to_page(unsigned long pfn); | ||
| 30 | extern unsigned long page_to_pfn(struct page *page); | ||
| 31 | #else | ||
| 32 | /* | ||
| 33 | * supports 3 memory models. | ||
| 34 | */ | ||
| 35 | #if defined(CONFIG_FLATMEM) | ||
| 36 | |||
| 37 | #define pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) | ||
| 38 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map) + \ | ||
| 39 | ARCH_PFN_OFFSET) | ||
| 40 | #elif defined(CONFIG_DISCONTIGMEM) | ||
| 41 | |||
| 42 | #define pfn_to_page(pfn) \ | ||
| 43 | ({ unsigned long __pfn = (pfn); \ | ||
| 44 | unsigned long __nid = arch_pfn_to_nid(pfn); \ | ||
| 45 | NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\ | ||
| 46 | }) | ||
| 47 | |||
| 48 | #define page_to_pfn(pg) \ | ||
| 49 | ({ struct page *__pg = (pg); \ | ||
| 50 | struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg)); \ | ||
| 51 | (unsigned long)(__pg - __pgdat->node_mem_map) + \ | ||
| 52 | __pgdat->node_start_pfn; \ | ||
| 53 | }) | ||
| 54 | |||
| 55 | #elif defined(CONFIG_SPARSEMEM) | ||
| 56 | /* | ||
| 57 | * Note: section's mem_map is encorded to reflect its start_pfn. | ||
| 58 | * section[i].section_mem_map == mem_map's address - start_pfn; | ||
| 59 | */ | ||
| 60 | #define page_to_pfn(pg) \ | ||
| 61 | ({ struct page *__pg = (pg); \ | ||
| 62 | int __sec = page_to_section(__pg); \ | ||
| 63 | __pg - __section_mem_map_addr(__nr_to_section(__sec)); \ | ||
| 64 | }) | ||
| 65 | |||
| 66 | #define pfn_to_page(pfn) \ | ||
| 67 | ({ unsigned long __pfn = (pfn); \ | ||
| 68 | struct mem_section *__sec = __pfn_to_section(__pfn); \ | ||
| 69 | __section_mem_map_addr(__sec) + __pfn; \ | ||
| 70 | }) | ||
| 71 | #endif /* CONFIG_FLATMEM/DISCONTIGMEM/SPARSEMEM */ | ||
| 72 | #endif /* CONFIG_OUT_OF_LINE_PFN_TO_PAGE */ | ||
| 73 | |||
| 74 | #endif /* __ASSEMBLY__ */ | ||
| 75 | #endif /* __KERNEL__ */ | ||
| 76 | |||
| 77 | #endif | ||
diff --git a/include/asm-h8300/page.h b/include/asm-h8300/page.h index cd35b1cc6cde..6472c9f88227 100644 --- a/include/asm-h8300/page.h +++ b/include/asm-h8300/page.h | |||
| @@ -71,8 +71,7 @@ extern unsigned long memory_end; | |||
| 71 | #define page_to_virt(page) ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) | 71 | #define page_to_virt(page) ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) |
| 72 | #define pfn_valid(page) (page < max_mapnr) | 72 | #define pfn_valid(page) (page < max_mapnr) |
| 73 | 73 | ||
| 74 | #define pfn_to_page(pfn) virt_to_page(pfn_to_virt(pfn)) | 74 | #define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) |
| 75 | #define page_to_pfn(page) virt_to_pfn(page_to_virt(page)) | ||
| 76 | 75 | ||
| 77 | #define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ | 76 | #define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ |
| 78 | ((void *)(kaddr) < (void *)memory_end)) | 77 | ((void *)(kaddr) < (void *)memory_end)) |
| @@ -81,6 +80,7 @@ extern unsigned long memory_end; | |||
| 81 | 80 | ||
| 82 | #endif /* __KERNEL__ */ | 81 | #endif /* __KERNEL__ */ |
| 83 | 82 | ||
| 83 | #include <asm-generic/memory_model.h> | ||
| 84 | #include <asm-generic/page.h> | 84 | #include <asm-generic/page.h> |
| 85 | 85 | ||
| 86 | #endif /* _H8300_PAGE_H */ | 86 | #endif /* _H8300_PAGE_H */ |
diff --git a/include/asm-i386/futex.h b/include/asm-i386/futex.h index 44b9db806474..7b8ceefd010f 100644 --- a/include/asm-i386/futex.h +++ b/include/asm-i386/futex.h | |||
| @@ -104,5 +104,32 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) | |||
| 104 | return ret; | 104 | return ret; |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | static inline int | ||
| 108 | futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) | ||
| 109 | { | ||
| 110 | if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) | ||
| 111 | return -EFAULT; | ||
| 112 | |||
| 113 | __asm__ __volatile__( | ||
| 114 | "1: " LOCK_PREFIX "cmpxchgl %3, %1 \n" | ||
| 115 | |||
| 116 | "2: .section .fixup, \"ax\" \n" | ||
| 117 | "3: mov %2, %0 \n" | ||
| 118 | " jmp 2b \n" | ||
| 119 | " .previous \n" | ||
| 120 | |||
| 121 | " .section __ex_table, \"a\" \n" | ||
| 122 | " .align 8 \n" | ||
| 123 | " .long 1b,3b \n" | ||
| 124 | " .previous \n" | ||
| 125 | |||
| 126 | : "=a" (oldval), "=m" (*uaddr) | ||
| 127 | : "i" (-EFAULT), "r" (newval), "0" (oldval) | ||
| 128 | : "memory" | ||
| 129 | ); | ||
| 130 | |||
| 131 | return oldval; | ||
| 132 | } | ||
| 133 | |||
| 107 | #endif | 134 | #endif |
| 108 | #endif | 135 | #endif |
diff --git a/include/asm-i386/kdebug.h b/include/asm-i386/kdebug.h index 316138e89910..96d0828ce096 100644 --- a/include/asm-i386/kdebug.h +++ b/include/asm-i386/kdebug.h | |||
| @@ -17,11 +17,9 @@ struct die_args { | |||
| 17 | int signr; | 17 | int signr; |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | /* Note - you should never unregister because that can race with NMIs. | 20 | extern int register_die_notifier(struct notifier_block *); |
| 21 | If you really want to do it first unregister - then synchronize_sched - then free. | 21 | extern int unregister_die_notifier(struct notifier_block *); |
| 22 | */ | 22 | extern struct atomic_notifier_head i386die_chain; |
| 23 | int register_die_notifier(struct notifier_block *nb); | ||
| 24 | extern struct notifier_block *i386die_chain; | ||
| 25 | 23 | ||
| 26 | 24 | ||
| 27 | /* Grossly misnamed. */ | 25 | /* Grossly misnamed. */ |
| @@ -51,7 +49,7 @@ static inline int notify_die(enum die_val val, const char *str, | |||
| 51 | .trapnr = trap, | 49 | .trapnr = trap, |
| 52 | .signr = sig | 50 | .signr = sig |
| 53 | }; | 51 | }; |
| 54 | return notifier_call_chain(&i386die_chain, val, &args); | 52 | return atomic_notifier_call_chain(&i386die_chain, val, &args); |
| 55 | } | 53 | } |
| 56 | 54 | ||
| 57 | #endif | 55 | #endif |
diff --git a/include/asm-i386/mmzone.h b/include/asm-i386/mmzone.h index 74f595d80579..e33e9f9e4c66 100644 --- a/include/asm-i386/mmzone.h +++ b/include/asm-i386/mmzone.h | |||
| @@ -70,8 +70,6 @@ static inline int pfn_to_nid(unsigned long pfn) | |||
| 70 | #endif | 70 | #endif |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | #define node_localnr(pfn, nid) ((pfn) - node_data[nid]->node_start_pfn) | ||
| 74 | |||
| 75 | /* | 73 | /* |
| 76 | * Following are macros that each numa implmentation must define. | 74 | * Following are macros that each numa implmentation must define. |
| 77 | */ | 75 | */ |
| @@ -86,21 +84,6 @@ static inline int pfn_to_nid(unsigned long pfn) | |||
| 86 | /* XXX: FIXME -- wli */ | 84 | /* XXX: FIXME -- wli */ |
| 87 | #define kern_addr_valid(kaddr) (0) | 85 | #define kern_addr_valid(kaddr) (0) |
| 88 | 86 | ||
| 89 | #define pfn_to_page(pfn) \ | ||
| 90 | ({ \ | ||
| 91 | unsigned long __pfn = pfn; \ | ||
| 92 | int __node = pfn_to_nid(__pfn); \ | ||
| 93 | &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ | ||
| 94 | }) | ||
| 95 | |||
| 96 | #define page_to_pfn(pg) \ | ||
| 97 | ({ \ | ||
| 98 | struct page *__page = pg; \ | ||
| 99 | struct zone *__zone = page_zone(__page); \ | ||
| 100 | (unsigned long)(__page - __zone->zone_mem_map) \ | ||
| 101 | + __zone->zone_start_pfn; \ | ||
| 102 | }) | ||
| 103 | |||
| 104 | #ifdef CONFIG_X86_NUMAQ /* we have contiguous memory on NUMA-Q */ | 87 | #ifdef CONFIG_X86_NUMAQ /* we have contiguous memory on NUMA-Q */ |
| 105 | #define pfn_valid(pfn) ((pfn) < num_physpages) | 88 | #define pfn_valid(pfn) ((pfn) < num_physpages) |
| 106 | #else | 89 | #else |
diff --git a/include/asm-i386/page.h b/include/asm-i386/page.h index 997ca5d17876..30f52a2263ba 100644 --- a/include/asm-i386/page.h +++ b/include/asm-i386/page.h | |||
| @@ -126,8 +126,6 @@ extern int page_is_ram(unsigned long pagenr); | |||
| 126 | #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) | 126 | #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) |
| 127 | #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) | 127 | #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) |
| 128 | #ifdef CONFIG_FLATMEM | 128 | #ifdef CONFIG_FLATMEM |
| 129 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 130 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) | ||
| 131 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 129 | #define pfn_valid(pfn) ((pfn) < max_mapnr) |
| 132 | #endif /* CONFIG_FLATMEM */ | 130 | #endif /* CONFIG_FLATMEM */ |
| 133 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) | 131 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) |
| @@ -141,6 +139,7 @@ extern int page_is_ram(unsigned long pagenr); | |||
| 141 | 139 | ||
| 142 | #endif /* __KERNEL__ */ | 140 | #endif /* __KERNEL__ */ |
| 143 | 141 | ||
| 142 | #include <asm-generic/memory_model.h> | ||
| 144 | #include <asm-generic/page.h> | 143 | #include <asm-generic/page.h> |
| 145 | 144 | ||
| 146 | #endif /* _I386_PAGE_H */ | 145 | #endif /* _I386_PAGE_H */ |
diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index feca5d961e2b..af4bfd012475 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/config.h> | 20 | #include <linux/config.h> |
| 21 | #include <linux/threads.h> | 21 | #include <linux/threads.h> |
| 22 | #include <asm/percpu.h> | 22 | #include <asm/percpu.h> |
| 23 | #include <linux/cpumask.h> | ||
| 23 | 24 | ||
| 24 | /* flag for disabling the tsc */ | 25 | /* flag for disabling the tsc */ |
| 25 | extern int tsc_disable; | 26 | extern int tsc_disable; |
| @@ -67,6 +68,9 @@ struct cpuinfo_x86 { | |||
| 67 | char pad0; | 68 | char pad0; |
| 68 | int x86_power; | 69 | int x86_power; |
| 69 | unsigned long loops_per_jiffy; | 70 | unsigned long loops_per_jiffy; |
| 71 | #ifdef CONFIG_SMP | ||
| 72 | cpumask_t llc_shared_map; /* cpus sharing the last level cache */ | ||
| 73 | #endif | ||
| 70 | unsigned char x86_max_cores; /* cpuid returned max cores value */ | 74 | unsigned char x86_max_cores; /* cpuid returned max cores value */ |
| 71 | unsigned char booted_cores; /* number of cores as seen by OS */ | 75 | unsigned char booted_cores; /* number of cores as seen by OS */ |
| 72 | unsigned char apicid; | 76 | unsigned char apicid; |
| @@ -103,6 +107,7 @@ extern struct cpuinfo_x86 cpu_data[]; | |||
| 103 | 107 | ||
| 104 | extern int phys_proc_id[NR_CPUS]; | 108 | extern int phys_proc_id[NR_CPUS]; |
| 105 | extern int cpu_core_id[NR_CPUS]; | 109 | extern int cpu_core_id[NR_CPUS]; |
| 110 | extern int cpu_llc_id[NR_CPUS]; | ||
| 106 | extern char ignore_fpu_irq; | 111 | extern char ignore_fpu_irq; |
| 107 | 112 | ||
| 108 | extern void identify_cpu(struct cpuinfo_x86 *); | 113 | extern void identify_cpu(struct cpuinfo_x86 *); |
diff --git a/include/asm-i386/setup.h b/include/asm-i386/setup.h index 826a8ca50ac8..ee941457b55d 100644 --- a/include/asm-i386/setup.h +++ b/include/asm-i386/setup.h | |||
| @@ -6,9 +6,7 @@ | |||
| 6 | #ifndef _i386_SETUP_H | 6 | #ifndef _i386_SETUP_H |
| 7 | #define _i386_SETUP_H | 7 | #define _i386_SETUP_H |
| 8 | 8 | ||
| 9 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | 9 | #include <linux/pfn.h> |
| 10 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 11 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 12 | 10 | ||
| 13 | /* | 11 | /* |
| 14 | * Reserved space for vmalloc and iomap - defined in asm/page.h | 12 | * Reserved space for vmalloc and iomap - defined in asm/page.h |
diff --git a/include/asm-i386/topology.h b/include/asm-i386/topology.h index aa958c6ee83e..b94e5eeef917 100644 --- a/include/asm-i386/topology.h +++ b/include/asm-i386/topology.h | |||
| @@ -112,4 +112,6 @@ extern unsigned long node_remap_size[]; | |||
| 112 | 112 | ||
| 113 | #endif /* CONFIG_NUMA */ | 113 | #endif /* CONFIG_NUMA */ |
| 114 | 114 | ||
| 115 | extern cpumask_t cpu_coregroup_map(int cpu); | ||
| 116 | |||
| 115 | #endif /* _ASM_I386_TOPOLOGY_H */ | 117 | #endif /* _ASM_I386_TOPOLOGY_H */ |
diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index d8afd0e3b81a..014e3562895b 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h | |||
| @@ -316,8 +316,10 @@ | |||
| 316 | #define __NR_pselect6 308 | 316 | #define __NR_pselect6 308 |
| 317 | #define __NR_ppoll 309 | 317 | #define __NR_ppoll 309 |
| 318 | #define __NR_unshare 310 | 318 | #define __NR_unshare 310 |
| 319 | #define __NR_set_robust_list 311 | ||
| 320 | #define __NR_get_robust_list 312 | ||
| 319 | 321 | ||
| 320 | #define NR_syscalls 311 | 322 | #define NR_syscalls 313 |
| 321 | 323 | ||
| 322 | /* | 324 | /* |
| 323 | * user-visible error numbers are in the range -1 - -128: see | 325 | * user-visible error numbers are in the range -1 - -128: see |
diff --git a/include/asm-ia64/compat.h b/include/asm-ia64/compat.h index c0b19106665c..40d01d80610d 100644 --- a/include/asm-ia64/compat.h +++ b/include/asm-ia64/compat.h | |||
| @@ -189,6 +189,12 @@ compat_ptr (compat_uptr_t uptr) | |||
| 189 | return (void __user *) (unsigned long) uptr; | 189 | return (void __user *) (unsigned long) uptr; |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | static inline compat_uptr_t | ||
| 193 | ptr_to_compat(void __user *uptr) | ||
| 194 | { | ||
| 195 | return (u32)(unsigned long)uptr; | ||
| 196 | } | ||
| 197 | |||
| 192 | static __inline__ void __user * | 198 | static __inline__ void __user * |
| 193 | compat_alloc_user_space (long len) | 199 | compat_alloc_user_space (long len) |
| 194 | { | 200 | { |
diff --git a/include/asm-ia64/kdebug.h b/include/asm-ia64/kdebug.h index 8b01a083dde6..218c458ab60c 100644 --- a/include/asm-ia64/kdebug.h +++ b/include/asm-ia64/kdebug.h | |||
| @@ -40,7 +40,7 @@ struct die_args { | |||
| 40 | 40 | ||
| 41 | extern int register_die_notifier(struct notifier_block *); | 41 | extern int register_die_notifier(struct notifier_block *); |
| 42 | extern int unregister_die_notifier(struct notifier_block *); | 42 | extern int unregister_die_notifier(struct notifier_block *); |
| 43 | extern struct notifier_block *ia64die_chain; | 43 | extern struct atomic_notifier_head ia64die_chain; |
| 44 | 44 | ||
| 45 | enum die_val { | 45 | enum die_val { |
| 46 | DIE_BREAK = 1, | 46 | DIE_BREAK = 1, |
| @@ -81,7 +81,7 @@ static inline int notify_die(enum die_val val, char *str, struct pt_regs *regs, | |||
| 81 | .signr = sig | 81 | .signr = sig |
| 82 | }; | 82 | }; |
| 83 | 83 | ||
| 84 | return notifier_call_chain(&ia64die_chain, val, &args); | 84 | return atomic_notifier_call_chain(&ia64die_chain, val, &args); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | #endif | 87 | #endif |
diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index 6e9aa23250c4..2087825eefa4 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h | |||
| @@ -106,17 +106,25 @@ extern int ia64_pfn_valid (unsigned long pfn); | |||
| 106 | # define ia64_pfn_valid(pfn) 1 | 106 | # define ia64_pfn_valid(pfn) 1 |
| 107 | #endif | 107 | #endif |
| 108 | 108 | ||
| 109 | #ifdef CONFIG_VIRTUAL_MEM_MAP | ||
| 110 | extern struct page *vmem_map; | ||
| 111 | #ifdef CONFIG_DISCONTIGMEM | ||
| 112 | # define page_to_pfn(page) ((unsigned long) (page - vmem_map)) | ||
| 113 | # define pfn_to_page(pfn) (vmem_map + (pfn)) | ||
| 114 | #endif | ||
| 115 | #endif | ||
| 116 | |||
| 117 | #if defined(CONFIG_FLATMEM) || defined(CONFIG_SPARSEMEM) | ||
| 118 | /* FLATMEM always configures mem_map (mem_map = vmem_map if necessary) */ | ||
| 119 | #include <asm-generic/memory_model.h> | ||
| 120 | #endif | ||
| 121 | |||
| 109 | #ifdef CONFIG_FLATMEM | 122 | #ifdef CONFIG_FLATMEM |
| 110 | # define pfn_valid(pfn) (((pfn) < max_mapnr) && ia64_pfn_valid(pfn)) | 123 | # define pfn_valid(pfn) (((pfn) < max_mapnr) && ia64_pfn_valid(pfn)) |
| 111 | # define page_to_pfn(page) ((unsigned long) (page - mem_map)) | ||
| 112 | # define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 113 | #elif defined(CONFIG_DISCONTIGMEM) | 124 | #elif defined(CONFIG_DISCONTIGMEM) |
| 114 | extern struct page *vmem_map; | ||
| 115 | extern unsigned long min_low_pfn; | 125 | extern unsigned long min_low_pfn; |
| 116 | extern unsigned long max_low_pfn; | 126 | extern unsigned long max_low_pfn; |
| 117 | # define pfn_valid(pfn) (((pfn) >= min_low_pfn) && ((pfn) < max_low_pfn) && ia64_pfn_valid(pfn)) | 127 | # define pfn_valid(pfn) (((pfn) >= min_low_pfn) && ((pfn) < max_low_pfn) && ia64_pfn_valid(pfn)) |
| 118 | # define page_to_pfn(page) ((unsigned long) (page - vmem_map)) | ||
| 119 | # define pfn_to_page(pfn) (vmem_map + (pfn)) | ||
| 120 | #endif | 128 | #endif |
| 121 | 129 | ||
| 122 | #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) | 130 | #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) |
diff --git a/include/asm-m32r/mmzone.h b/include/asm-m32r/mmzone.h index adc7970a77ec..9f3b5accda88 100644 --- a/include/asm-m32r/mmzone.h +++ b/include/asm-m32r/mmzone.h | |||
| @@ -21,20 +21,6 @@ extern struct pglist_data *node_data[]; | |||
| 21 | __pgdat->node_start_pfn + __pgdat->node_spanned_pages - 1; \ | 21 | __pgdat->node_start_pfn + __pgdat->node_spanned_pages - 1; \ |
| 22 | }) | 22 | }) |
| 23 | 23 | ||
| 24 | #define pfn_to_page(pfn) \ | ||
| 25 | ({ \ | ||
| 26 | unsigned long __pfn = pfn; \ | ||
| 27 | int __node = pfn_to_nid(__pfn); \ | ||
| 28 | &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ | ||
| 29 | }) | ||
| 30 | |||
| 31 | #define page_to_pfn(pg) \ | ||
| 32 | ({ \ | ||
| 33 | struct page *__page = pg; \ | ||
| 34 | struct zone *__zone = page_zone(__page); \ | ||
| 35 | (unsigned long)(__page - __zone->zone_mem_map) \ | ||
| 36 | + __zone->zone_start_pfn; \ | ||
| 37 | }) | ||
| 38 | #define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT)) | 24 | #define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT)) |
| 39 | /* | 25 | /* |
| 40 | * pfn_valid should be made as fast as possible, and the current definition | 26 | * pfn_valid should be made as fast as possible, and the current definition |
diff --git a/include/asm-m32r/page.h b/include/asm-m32r/page.h index 4ab578876361..9ddbc087dbc5 100644 --- a/include/asm-m32r/page.h +++ b/include/asm-m32r/page.h | |||
| @@ -76,9 +76,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 76 | 76 | ||
| 77 | #ifndef CONFIG_DISCONTIGMEM | 77 | #ifndef CONFIG_DISCONTIGMEM |
| 78 | #define PFN_BASE (CONFIG_MEMORY_START >> PAGE_SHIFT) | 78 | #define PFN_BASE (CONFIG_MEMORY_START >> PAGE_SHIFT) |
| 79 | #define pfn_to_page(pfn) (mem_map + ((pfn) - PFN_BASE)) | 79 | #define ARCH_PFN_OFFSET PFN_BASE |
| 80 | #define page_to_pfn(page) \ | ||
| 81 | ((unsigned long)((page) - mem_map) + PFN_BASE) | ||
| 82 | #define pfn_valid(pfn) (((pfn) - PFN_BASE) < max_mapnr) | 80 | #define pfn_valid(pfn) (((pfn) - PFN_BASE) < max_mapnr) |
| 83 | #endif /* !CONFIG_DISCONTIGMEM */ | 81 | #endif /* !CONFIG_DISCONTIGMEM */ |
| 84 | 82 | ||
| @@ -92,6 +90,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 92 | 90 | ||
| 93 | #endif /* __KERNEL__ */ | 91 | #endif /* __KERNEL__ */ |
| 94 | 92 | ||
| 93 | #include <asm-generic/memory_model.h> | ||
| 95 | #include <asm-generic/page.h> | 94 | #include <asm-generic/page.h> |
| 96 | 95 | ||
| 97 | #endif /* _ASM_M32R_PAGE_H */ | 96 | #endif /* _ASM_M32R_PAGE_H */ |
diff --git a/include/asm-m32r/setup.h b/include/asm-m32r/setup.h index 5f028dc26a9b..52f4fa29abfc 100644 --- a/include/asm-m32r/setup.h +++ b/include/asm-m32r/setup.h | |||
| @@ -24,10 +24,6 @@ | |||
| 24 | #define RAMDISK_PROMPT_FLAG (0x8000) | 24 | #define RAMDISK_PROMPT_FLAG (0x8000) |
| 25 | #define RAMDISK_LOAD_FLAG (0x4000) | 25 | #define RAMDISK_LOAD_FLAG (0x4000) |
| 26 | 26 | ||
| 27 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 28 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 29 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 30 | |||
| 31 | extern unsigned long memory_start; | 27 | extern unsigned long memory_start; |
| 32 | extern unsigned long memory_end; | 28 | extern unsigned long memory_end; |
| 33 | 29 | ||
diff --git a/include/asm-mips/compat.h b/include/asm-mips/compat.h index 0012bd804d2d..986511db54a6 100644 --- a/include/asm-mips/compat.h +++ b/include/asm-mips/compat.h | |||
| @@ -133,6 +133,11 @@ static inline void __user *compat_ptr(compat_uptr_t uptr) | |||
| 133 | return (void __user *)(long)uptr; | 133 | return (void __user *)(long)uptr; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | static inline compat_uptr_t ptr_to_compat(void __user *uptr) | ||
| 137 | { | ||
| 138 | return (u32)(unsigned long)uptr; | ||
| 139 | } | ||
| 140 | |||
| 136 | static inline void __user *compat_alloc_user_space(long len) | 141 | static inline void __user *compat_alloc_user_space(long len) |
| 137 | { | 142 | { |
| 138 | struct pt_regs *regs = (struct pt_regs *) | 143 | struct pt_regs *regs = (struct pt_regs *) |
diff --git a/include/asm-mips/futex.h b/include/asm-mips/futex.h index 2454c44a8f54..a554089991f2 100644 --- a/include/asm-mips/futex.h +++ b/include/asm-mips/futex.h | |||
| @@ -99,5 +99,11 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) | |||
| 99 | return ret; | 99 | return ret; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | static inline int | ||
| 103 | futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) | ||
| 104 | { | ||
| 105 | return -ENOSYS; | ||
| 106 | } | ||
| 107 | |||
| 102 | #endif | 108 | #endif |
| 103 | #endif | 109 | #endif |
diff --git a/include/asm-mips/mmzone.h b/include/asm-mips/mmzone.h index 011caebac369..7bde4432092b 100644 --- a/include/asm-mips/mmzone.h +++ b/include/asm-mips/mmzone.h | |||
| @@ -22,20 +22,6 @@ | |||
| 22 | NODE_DATA(__n)->node_spanned_pages) : 0);\ | 22 | NODE_DATA(__n)->node_spanned_pages) : 0);\ |
| 23 | }) | 23 | }) |
| 24 | 24 | ||
| 25 | #define pfn_to_page(pfn) \ | ||
| 26 | ({ \ | ||
| 27 | unsigned long __pfn = (pfn); \ | ||
| 28 | pg_data_t *__pg = NODE_DATA(pfn_to_nid(__pfn)); \ | ||
| 29 | __pg->node_mem_map + (__pfn - __pg->node_start_pfn); \ | ||
| 30 | }) | ||
| 31 | |||
| 32 | #define page_to_pfn(p) \ | ||
| 33 | ({ \ | ||
| 34 | struct page *__p = (p); \ | ||
| 35 | struct zone *__z = page_zone(__p); \ | ||
| 36 | ((__p - __z->zone_mem_map) + __z->zone_start_pfn); \ | ||
| 37 | }) | ||
| 38 | |||
| 39 | /* XXX: FIXME -- wli */ | 25 | /* XXX: FIXME -- wli */ |
| 40 | #define kern_addr_valid(addr) (0) | 26 | #define kern_addr_valid(addr) (0) |
| 41 | 27 | ||
diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index ee25a779bf49..a1eab136ff6c 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h | |||
| @@ -140,8 +140,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 140 | #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) | 140 | #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) |
| 141 | 141 | ||
| 142 | #ifndef CONFIG_NEED_MULTIPLE_NODES | 142 | #ifndef CONFIG_NEED_MULTIPLE_NODES |
| 143 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 144 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) | ||
| 145 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 143 | #define pfn_valid(pfn) ((pfn) < max_mapnr) |
| 146 | #endif | 144 | #endif |
| 147 | 145 | ||
| @@ -160,6 +158,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 160 | #define WANT_PAGE_VIRTUAL | 158 | #define WANT_PAGE_VIRTUAL |
| 161 | #endif | 159 | #endif |
| 162 | 160 | ||
| 161 | #include <asm-generic/memory_model.h> | ||
| 163 | #include <asm-generic/page.h> | 162 | #include <asm-generic/page.h> |
| 164 | 163 | ||
| 165 | #endif /* _ASM_PAGE_H */ | 164 | #endif /* _ASM_PAGE_H */ |
diff --git a/include/asm-mips/time.h b/include/asm-mips/time.h index 9cc3564cc2c9..d897c8bb554d 100644 --- a/include/asm-mips/time.h +++ b/include/asm-mips/time.h | |||
| @@ -26,14 +26,14 @@ extern spinlock_t rtc_lock; | |||
| 26 | 26 | ||
| 27 | /* | 27 | /* |
| 28 | * RTC ops. By default, they point to no-RTC functions. | 28 | * RTC ops. By default, they point to no-RTC functions. |
| 29 | * rtc_get_time - mktime(year, mon, day, hour, min, sec) in seconds. | 29 | * rtc_mips_get_time - mktime(year, mon, day, hour, min, sec) in seconds. |
| 30 | * rtc_set_time - reverse the above translation and set time to RTC. | 30 | * rtc_mips_set_time - reverse the above translation and set time to RTC. |
| 31 | * rtc_set_mmss - similar to rtc_set_time, but only min and sec need | 31 | * rtc_mips_set_mmss - similar to rtc_set_time, but only min and sec need |
| 32 | * to be set. Used by RTC sync-up. | 32 | * to be set. Used by RTC sync-up. |
| 33 | */ | 33 | */ |
| 34 | extern unsigned long (*rtc_get_time)(void); | 34 | extern unsigned long (*rtc_mips_get_time)(void); |
| 35 | extern int (*rtc_set_time)(unsigned long); | 35 | extern int (*rtc_mips_set_time)(unsigned long); |
| 36 | extern int (*rtc_set_mmss)(unsigned long); | 36 | extern int (*rtc_mips_set_mmss)(unsigned long); |
| 37 | 37 | ||
| 38 | /* | 38 | /* |
| 39 | * Timer interrupt functions. | 39 | * Timer interrupt functions. |
diff --git a/include/asm-parisc/compat.h b/include/asm-parisc/compat.h index 38b918feead9..289624d8b2d4 100644 --- a/include/asm-parisc/compat.h +++ b/include/asm-parisc/compat.h | |||
| @@ -138,6 +138,11 @@ static inline void __user *compat_ptr(compat_uptr_t uptr) | |||
| 138 | return (void __user *)(unsigned long)uptr; | 138 | return (void __user *)(unsigned long)uptr; |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | static inline compat_uptr_t ptr_to_compat(void __user *uptr) | ||
| 142 | { | ||
| 143 | return (u32)(unsigned long)uptr; | ||
| 144 | } | ||
| 145 | |||
| 141 | static __inline__ void __user *compat_alloc_user_space(long len) | 146 | static __inline__ void __user *compat_alloc_user_space(long len) |
| 142 | { | 147 | { |
| 143 | struct pt_regs *regs = ¤t->thread.regs; | 148 | struct pt_regs *regs = ¤t->thread.regs; |
diff --git a/include/asm-parisc/mmzone.h b/include/asm-parisc/mmzone.h index ae039f4fd711..ceb9b73199d1 100644 --- a/include/asm-parisc/mmzone.h +++ b/include/asm-parisc/mmzone.h | |||
| @@ -25,23 +25,6 @@ extern struct node_map_data node_data[]; | |||
| 25 | pg_data_t *__pgdat = NODE_DATA(nid); \ | 25 | pg_data_t *__pgdat = NODE_DATA(nid); \ |
| 26 | __pgdat->node_start_pfn + __pgdat->node_spanned_pages; \ | 26 | __pgdat->node_start_pfn + __pgdat->node_spanned_pages; \ |
| 27 | }) | 27 | }) |
| 28 | #define node_localnr(pfn, nid) ((pfn) - node_start_pfn(nid)) | ||
| 29 | |||
| 30 | #define pfn_to_page(pfn) \ | ||
| 31 | ({ \ | ||
| 32 | unsigned long __pfn = (pfn); \ | ||
| 33 | int __node = pfn_to_nid(__pfn); \ | ||
| 34 | &NODE_DATA(__node)->node_mem_map[node_localnr(__pfn,__node)]; \ | ||
| 35 | }) | ||
| 36 | |||
| 37 | #define page_to_pfn(pg) \ | ||
| 38 | ({ \ | ||
| 39 | struct page *__page = pg; \ | ||
| 40 | struct zone *__zone = page_zone(__page); \ | ||
| 41 | BUG_ON(__zone == NULL); \ | ||
| 42 | (unsigned long)(__page - __zone->zone_mem_map) \ | ||
| 43 | + __zone->zone_start_pfn; \ | ||
| 44 | }) | ||
| 45 | 28 | ||
| 46 | /* We have these possible memory map layouts: | 29 | /* We have these possible memory map layouts: |
| 47 | * Astro: 0-3.75, 67.75-68, 4-64 | 30 | * Astro: 0-3.75, 67.75-68, 4-64 |
diff --git a/include/asm-parisc/page.h b/include/asm-parisc/page.h index 4a6752b0afed..9f303c0c3cd7 100644 --- a/include/asm-parisc/page.h +++ b/include/asm-parisc/page.h | |||
| @@ -130,8 +130,6 @@ extern int npmem_ranges; | |||
| 130 | #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) | 130 | #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) |
| 131 | 131 | ||
| 132 | #ifndef CONFIG_DISCONTIGMEM | 132 | #ifndef CONFIG_DISCONTIGMEM |
| 133 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 134 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) | ||
| 135 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 133 | #define pfn_valid(pfn) ((pfn) < max_mapnr) |
| 136 | #endif /* CONFIG_DISCONTIGMEM */ | 134 | #endif /* CONFIG_DISCONTIGMEM */ |
| 137 | 135 | ||
| @@ -152,6 +150,7 @@ extern int npmem_ranges; | |||
| 152 | 150 | ||
| 153 | #endif /* __KERNEL__ */ | 151 | #endif /* __KERNEL__ */ |
| 154 | 152 | ||
| 153 | #include <asm-generic/memory_model.h> | ||
| 155 | #include <asm-generic/page.h> | 154 | #include <asm-generic/page.h> |
| 156 | 155 | ||
| 157 | #endif /* _PARISC_PAGE_H */ | 156 | #endif /* _PARISC_PAGE_H */ |
diff --git a/include/asm-powerpc/futex.h b/include/asm-powerpc/futex.h index 39e85f320a76..f1b3c00bc1ce 100644 --- a/include/asm-powerpc/futex.h +++ b/include/asm-powerpc/futex.h | |||
| @@ -81,5 +81,11 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) | |||
| 81 | return ret; | 81 | return ret; |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | static inline int | ||
| 85 | futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) | ||
| 86 | { | ||
| 87 | return -ENOSYS; | ||
| 88 | } | ||
| 89 | |||
| 84 | #endif /* __KERNEL__ */ | 90 | #endif /* __KERNEL__ */ |
| 85 | #endif /* _ASM_POWERPC_FUTEX_H */ | 91 | #endif /* _ASM_POWERPC_FUTEX_H */ |
diff --git a/include/asm-powerpc/kdebug.h b/include/asm-powerpc/kdebug.h index 7c16265568e0..c01786ab5fa6 100644 --- a/include/asm-powerpc/kdebug.h +++ b/include/asm-powerpc/kdebug.h | |||
| @@ -16,13 +16,9 @@ struct die_args { | |||
| 16 | int signr; | 16 | int signr; |
| 17 | }; | 17 | }; |
| 18 | 18 | ||
| 19 | /* | 19 | extern int register_die_notifier(struct notifier_block *); |
| 20 | Note - you should never unregister because that can race with NMIs. | 20 | extern int unregister_die_notifier(struct notifier_block *); |
| 21 | If you really want to do it first unregister - then synchronize_sched - | 21 | extern struct atomic_notifier_head powerpc_die_chain; |
| 22 | then free. | ||
| 23 | */ | ||
| 24 | int register_die_notifier(struct notifier_block *nb); | ||
| 25 | extern struct notifier_block *powerpc_die_chain; | ||
| 26 | 22 | ||
| 27 | /* Grossly misnamed. */ | 23 | /* Grossly misnamed. */ |
| 28 | enum die_val { | 24 | enum die_val { |
| @@ -37,7 +33,7 @@ enum die_val { | |||
| 37 | static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig) | 33 | static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig) |
| 38 | { | 34 | { |
| 39 | struct die_args args = { .regs=regs, .str=str, .err=err, .trapnr=trap,.signr=sig }; | 35 | struct die_args args = { .regs=regs, .str=str, .err=err, .trapnr=trap,.signr=sig }; |
| 40 | return notifier_call_chain(&powerpc_die_chain, val, &args); | 36 | return atomic_notifier_call_chain(&powerpc_die_chain, val, &args); |
| 41 | } | 37 | } |
| 42 | 38 | ||
| 43 | #endif /* __KERNEL__ */ | 39 | #endif /* __KERNEL__ */ |
diff --git a/include/asm-powerpc/page.h b/include/asm-powerpc/page.h index 0b82df483f7f..2fbecebe1c92 100644 --- a/include/asm-powerpc/page.h +++ b/include/asm-powerpc/page.h | |||
| @@ -69,8 +69,6 @@ | |||
| 69 | #endif | 69 | #endif |
| 70 | 70 | ||
| 71 | #ifdef CONFIG_FLATMEM | 71 | #ifdef CONFIG_FLATMEM |
| 72 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 73 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) | ||
| 74 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 72 | #define pfn_valid(pfn) ((pfn) < max_mapnr) |
| 75 | #endif | 73 | #endif |
| 76 | 74 | ||
| @@ -200,6 +198,7 @@ extern void copy_user_page(void *to, void *from, unsigned long vaddr, | |||
| 200 | struct page *p); | 198 | struct page *p); |
| 201 | extern int page_is_ram(unsigned long pfn); | 199 | extern int page_is_ram(unsigned long pfn); |
| 202 | 200 | ||
| 201 | #include <asm-generic/memory_model.h> | ||
| 203 | #endif /* __ASSEMBLY__ */ | 202 | #endif /* __ASSEMBLY__ */ |
| 204 | 203 | ||
| 205 | #endif /* __KERNEL__ */ | 204 | #endif /* __KERNEL__ */ |
diff --git a/include/asm-ppc/page.h b/include/asm-ppc/page.h index 538e0c8ab243..a70ba2ee552d 100644 --- a/include/asm-ppc/page.h +++ b/include/asm-ppc/page.h | |||
| @@ -149,8 +149,7 @@ extern int page_is_ram(unsigned long pfn); | |||
| 149 | #define __pa(x) ___pa((unsigned long)(x)) | 149 | #define __pa(x) ___pa((unsigned long)(x)) |
| 150 | #define __va(x) ((void *)(___va((unsigned long)(x)))) | 150 | #define __va(x) ((void *)(___va((unsigned long)(x)))) |
| 151 | 151 | ||
| 152 | #define pfn_to_page(pfn) (mem_map + ((pfn) - PPC_PGSTART)) | 152 | #define ARCH_PFN_OFFSET (PPC_PGSTART) |
| 153 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map) + PPC_PGSTART) | ||
| 154 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) | 153 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) |
| 155 | #define page_to_virt(page) __va(page_to_pfn(page) << PAGE_SHIFT) | 154 | #define page_to_virt(page) __va(page_to_pfn(page) << PAGE_SHIFT) |
| 156 | 155 | ||
| @@ -175,5 +174,6 @@ extern __inline__ int get_order(unsigned long size) | |||
| 175 | /* We do define AT_SYSINFO_EHDR but don't use the gate mecanism */ | 174 | /* We do define AT_SYSINFO_EHDR but don't use the gate mecanism */ |
| 176 | #define __HAVE_ARCH_GATE_AREA 1 | 175 | #define __HAVE_ARCH_GATE_AREA 1 |
| 177 | 176 | ||
| 177 | #include <asm-generic/memory_model.h> | ||
| 178 | #endif /* __KERNEL__ */ | 178 | #endif /* __KERNEL__ */ |
| 179 | #endif /* _PPC_PAGE_H */ | 179 | #endif /* _PPC_PAGE_H */ |
diff --git a/include/asm-s390/compat.h b/include/asm-s390/compat.h index a007715f4aea..356a0b183539 100644 --- a/include/asm-s390/compat.h +++ b/include/asm-s390/compat.h | |||
| @@ -128,6 +128,11 @@ static inline void __user *compat_ptr(compat_uptr_t uptr) | |||
| 128 | return (void __user *)(unsigned long)(uptr & 0x7fffffffUL); | 128 | return (void __user *)(unsigned long)(uptr & 0x7fffffffUL); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | static inline compat_uptr_t ptr_to_compat(void __user *uptr) | ||
| 132 | { | ||
| 133 | return (u32)(unsigned long)uptr; | ||
| 134 | } | ||
| 135 | |||
| 131 | static inline void __user *compat_alloc_user_space(long len) | 136 | static inline void __user *compat_alloc_user_space(long len) |
| 132 | { | 137 | { |
| 133 | unsigned long stack; | 138 | unsigned long stack; |
diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index 2430c561e021..3b1138ac7e79 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h | |||
| @@ -181,8 +181,6 @@ page_get_storage_key(unsigned long addr) | |||
| 181 | #define PAGE_OFFSET 0x0UL | 181 | #define PAGE_OFFSET 0x0UL |
| 182 | #define __pa(x) (unsigned long)(x) | 182 | #define __pa(x) (unsigned long)(x) |
| 183 | #define __va(x) (void *)(unsigned long)(x) | 183 | #define __va(x) (void *)(unsigned long)(x) |
| 184 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 185 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) | ||
| 186 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) | 184 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) |
| 187 | 185 | ||
| 188 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 186 | #define pfn_valid(pfn) ((pfn) < max_mapnr) |
| @@ -193,6 +191,7 @@ page_get_storage_key(unsigned long addr) | |||
| 193 | 191 | ||
| 194 | #endif /* __KERNEL__ */ | 192 | #endif /* __KERNEL__ */ |
| 195 | 193 | ||
| 194 | #include <asm-generic/memory_model.h> | ||
| 196 | #include <asm-generic/page.h> | 195 | #include <asm-generic/page.h> |
| 197 | 196 | ||
| 198 | #endif /* _S390_PAGE_H */ | 197 | #endif /* _S390_PAGE_H */ |
diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h index 972c3f655b2a..9c89287c3e56 100644 --- a/include/asm-sh/page.h +++ b/include/asm-sh/page.h | |||
| @@ -105,9 +105,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 105 | 105 | ||
| 106 | /* PFN start number, because of __MEMORY_START */ | 106 | /* PFN start number, because of __MEMORY_START */ |
| 107 | #define PFN_START (__MEMORY_START >> PAGE_SHIFT) | 107 | #define PFN_START (__MEMORY_START >> PAGE_SHIFT) |
| 108 | 108 | #define ARCH_PFN_OFFSET (FPN_START) | |
| 109 | #define pfn_to_page(pfn) (mem_map + (pfn) - PFN_START) | ||
| 110 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map) + PFN_START) | ||
| 111 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) | 109 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) |
| 112 | #define pfn_valid(pfn) (((pfn) - PFN_START) < max_mapnr) | 110 | #define pfn_valid(pfn) (((pfn) - PFN_START) < max_mapnr) |
| 113 | #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) | 111 | #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) |
| @@ -117,6 +115,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 117 | 115 | ||
| 118 | #endif /* __KERNEL__ */ | 116 | #endif /* __KERNEL__ */ |
| 119 | 117 | ||
| 118 | #include <asm-generic/memory_model.h> | ||
| 120 | #include <asm-generic/page.h> | 119 | #include <asm-generic/page.h> |
| 121 | 120 | ||
| 122 | #endif /* __ASM_SH_PAGE_H */ | 121 | #endif /* __ASM_SH_PAGE_H */ |
diff --git a/include/asm-sh64/page.h b/include/asm-sh64/page.h index c86df90f7cbd..e4937cdabebd 100644 --- a/include/asm-sh64/page.h +++ b/include/asm-sh64/page.h | |||
| @@ -105,9 +105,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 105 | 105 | ||
| 106 | /* PFN start number, because of __MEMORY_START */ | 106 | /* PFN start number, because of __MEMORY_START */ |
| 107 | #define PFN_START (__MEMORY_START >> PAGE_SHIFT) | 107 | #define PFN_START (__MEMORY_START >> PAGE_SHIFT) |
| 108 | 108 | #define ARCH_PFN_OFFSET (PFN_START) | |
| 109 | #define pfn_to_page(pfn) (mem_map + (pfn) - PFN_START) | ||
| 110 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map) + PFN_START) | ||
| 111 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) | 109 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) |
| 112 | #define pfn_valid(pfn) (((pfn) - PFN_START) < max_mapnr) | 110 | #define pfn_valid(pfn) (((pfn) - PFN_START) < max_mapnr) |
| 113 | #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) | 111 | #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) |
| @@ -117,6 +115,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 117 | 115 | ||
| 118 | #endif /* __KERNEL__ */ | 116 | #endif /* __KERNEL__ */ |
| 119 | 117 | ||
| 118 | #include <asm-generic/memory_model.h> | ||
| 120 | #include <asm-generic/page.h> | 119 | #include <asm-generic/page.h> |
| 121 | 120 | ||
| 122 | #endif /* __ASM_SH64_PAGE_H */ | 121 | #endif /* __ASM_SH64_PAGE_H */ |
diff --git a/include/asm-sh64/platform.h b/include/asm-sh64/platform.h index 7046a9014027..bd0d9c405a80 100644 --- a/include/asm-sh64/platform.h +++ b/include/asm-sh64/platform.h | |||
| @@ -61,9 +61,4 @@ extern int platform_int_priority[NR_INTC_IRQS]; | |||
| 61 | #define code_resource (platform_parms.kram_res_p[STANDARD_KRAM_RESOURCES - 2]) | 61 | #define code_resource (platform_parms.kram_res_p[STANDARD_KRAM_RESOURCES - 2]) |
| 62 | #define data_resource (platform_parms.kram_res_p[STANDARD_KRAM_RESOURCES - 1]) | 62 | #define data_resource (platform_parms.kram_res_p[STANDARD_KRAM_RESOURCES - 1]) |
| 63 | 63 | ||
| 64 | /* Be prepared to 64-bit sign extensions */ | ||
| 65 | #define PFN_UP(x) ((((x) + PAGE_SIZE-1) >> PAGE_SHIFT) & 0x000fffff) | ||
| 66 | #define PFN_DOWN(x) (((x) >> PAGE_SHIFT) & 0x000fffff) | ||
| 67 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 68 | |||
| 69 | #endif /* __ASM_SH64_PLATFORM_H */ | 64 | #endif /* __ASM_SH64_PLATFORM_H */ |
diff --git a/include/asm-sparc/page.h b/include/asm-sparc/page.h index 9122684f6c1e..ec3274b7ddf4 100644 --- a/include/asm-sparc/page.h +++ b/include/asm-sparc/page.h | |||
| @@ -152,8 +152,7 @@ extern unsigned long pfn_base; | |||
| 152 | #define virt_to_phys __pa | 152 | #define virt_to_phys __pa |
| 153 | #define phys_to_virt __va | 153 | #define phys_to_virt __va |
| 154 | 154 | ||
| 155 | #define pfn_to_page(pfn) (mem_map + ((pfn)-(pfn_base))) | 155 | #define ARCH_PFN_OFFSET (pfn_base) |
| 156 | #define page_to_pfn(page) ((unsigned long)(((page) - mem_map) + pfn_base)) | ||
| 157 | #define virt_to_page(kaddr) (mem_map + ((((unsigned long)(kaddr)-PAGE_OFFSET)>>PAGE_SHIFT))) | 156 | #define virt_to_page(kaddr) (mem_map + ((((unsigned long)(kaddr)-PAGE_OFFSET)>>PAGE_SHIFT))) |
| 158 | 157 | ||
| 159 | #define pfn_valid(pfn) (((pfn) >= (pfn_base)) && (((pfn)-(pfn_base)) < max_mapnr)) | 158 | #define pfn_valid(pfn) (((pfn) >= (pfn_base)) && (((pfn)-(pfn_base)) < max_mapnr)) |
| @@ -164,6 +163,7 @@ extern unsigned long pfn_base; | |||
| 164 | 163 | ||
| 165 | #endif /* __KERNEL__ */ | 164 | #endif /* __KERNEL__ */ |
| 166 | 165 | ||
| 166 | #include <asm-generic/memory_model.h> | ||
| 167 | #include <asm-generic/page.h> | 167 | #include <asm-generic/page.h> |
| 168 | 168 | ||
| 169 | #endif /* _SPARC_PAGE_H */ | 169 | #endif /* _SPARC_PAGE_H */ |
diff --git a/include/asm-sparc64/futex.h b/include/asm-sparc64/futex.h index 34c4b43d3f98..cd340a233156 100644 --- a/include/asm-sparc64/futex.h +++ b/include/asm-sparc64/futex.h | |||
| @@ -83,4 +83,10 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) | |||
| 83 | return ret; | 83 | return ret; |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | static inline int | ||
| 87 | futex_atomic_cmpxchg_inuser(int __user *uaddr, int oldval, int newval) | ||
| 88 | { | ||
| 89 | return -ENOSYS; | ||
| 90 | } | ||
| 91 | |||
| 86 | #endif /* !(_SPARC64_FUTEX_H) */ | 92 | #endif /* !(_SPARC64_FUTEX_H) */ |
diff --git a/include/asm-sparc64/kdebug.h b/include/asm-sparc64/kdebug.h index 6321f5a0198d..4040d127ac3e 100644 --- a/include/asm-sparc64/kdebug.h +++ b/include/asm-sparc64/kdebug.h | |||
| @@ -15,12 +15,9 @@ struct die_args { | |||
| 15 | int signr; | 15 | int signr; |
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | /* Note - you should never unregister because that can race with NMIs. | 18 | extern int register_die_notifier(struct notifier_block *); |
| 19 | * If you really want to do it first unregister - then synchronize_sched | 19 | extern int unregister_die_notifier(struct notifier_block *); |
| 20 | * - then free. | 20 | extern struct atomic_notifier_head sparc64die_chain; |
| 21 | */ | ||
| 22 | int register_die_notifier(struct notifier_block *nb); | ||
| 23 | extern struct notifier_block *sparc64die_chain; | ||
| 24 | 21 | ||
| 25 | extern void bad_trap(struct pt_regs *, long); | 22 | extern void bad_trap(struct pt_regs *, long); |
| 26 | 23 | ||
| @@ -46,7 +43,7 @@ static inline int notify_die(enum die_val val,char *str, struct pt_regs *regs, | |||
| 46 | .trapnr = trap, | 43 | .trapnr = trap, |
| 47 | .signr = sig }; | 44 | .signr = sig }; |
| 48 | 45 | ||
| 49 | return notifier_call_chain(&sparc64die_chain, val, &args); | 46 | return atomic_notifier_call_chain(&sparc64die_chain, val, &args); |
| 50 | } | 47 | } |
| 51 | 48 | ||
| 52 | #endif | 49 | #endif |
diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h index 66fe4ac59fd6..aabb21906724 100644 --- a/include/asm-sparc64/page.h +++ b/include/asm-sparc64/page.h | |||
| @@ -111,6 +111,8 @@ typedef unsigned long pgprot_t; | |||
| 111 | (_AC(0x0000000070000000,UL)) : \ | 111 | (_AC(0x0000000070000000,UL)) : \ |
| 112 | (_AC(0xfffff80000000000,UL) + (1UL << 32UL))) | 112 | (_AC(0xfffff80000000000,UL) + (1UL << 32UL))) |
| 113 | 113 | ||
| 114 | #include <asm-generic/memory_model.h> | ||
| 115 | |||
| 114 | #endif /* !(__ASSEMBLY__) */ | 116 | #endif /* !(__ASSEMBLY__) */ |
| 115 | 117 | ||
| 116 | /* to align the pointer to the (next) page boundary */ | 118 | /* to align the pointer to the (next) page boundary */ |
diff --git a/include/asm-um/page.h b/include/asm-um/page.h index 0229814af31e..41364330aff1 100644 --- a/include/asm-um/page.h +++ b/include/asm-um/page.h | |||
| @@ -106,9 +106,6 @@ extern unsigned long uml_physmem; | |||
| 106 | #define __pa(virt) to_phys((void *) (unsigned long) (virt)) | 106 | #define __pa(virt) to_phys((void *) (unsigned long) (virt)) |
| 107 | #define __va(phys) to_virt((unsigned long) (phys)) | 107 | #define __va(phys) to_virt((unsigned long) (phys)) |
| 108 | 108 | ||
| 109 | #define page_to_pfn(page) ((page) - mem_map) | ||
| 110 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 111 | |||
| 112 | #define phys_to_pfn(p) ((p) >> PAGE_SHIFT) | 109 | #define phys_to_pfn(p) ((p) >> PAGE_SHIFT) |
| 113 | #define pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT) | 110 | #define pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT) |
| 114 | 111 | ||
| @@ -121,6 +118,7 @@ extern struct page *arch_validate(struct page *page, gfp_t mask, int order); | |||
| 121 | extern void arch_free_page(struct page *page, int order); | 118 | extern void arch_free_page(struct page *page, int order); |
| 122 | #define HAVE_ARCH_FREE_PAGE | 119 | #define HAVE_ARCH_FREE_PAGE |
| 123 | 120 | ||
| 121 | #include <asm-generic/memory_model.h> | ||
| 124 | #include <asm-generic/page.h> | 122 | #include <asm-generic/page.h> |
| 125 | 123 | ||
| 126 | #endif | 124 | #endif |
diff --git a/include/asm-um/uaccess.h b/include/asm-um/uaccess.h index 2ee028b8de9d..4e460d6f5ac8 100644 --- a/include/asm-um/uaccess.h +++ b/include/asm-um/uaccess.h | |||
| @@ -41,16 +41,16 @@ | |||
| 41 | 41 | ||
| 42 | #define __get_user(x, ptr) \ | 42 | #define __get_user(x, ptr) \ |
| 43 | ({ \ | 43 | ({ \ |
| 44 | const __typeof__(ptr) __private_ptr = ptr; \ | 44 | const __typeof__(ptr) __private_ptr = ptr; \ |
| 45 | __typeof__(*(__private_ptr)) __private_val; \ | 45 | __typeof__(x) __private_val; \ |
| 46 | int __private_ret = -EFAULT; \ | 46 | int __private_ret = -EFAULT; \ |
| 47 | (x) = (__typeof__(*(__private_ptr)))0; \ | 47 | (x) = (__typeof__(*(__private_ptr)))0; \ |
| 48 | if (__copy_from_user(&__private_val, (__private_ptr), \ | 48 | if (__copy_from_user((void *) &__private_val, (__private_ptr), \ |
| 49 | sizeof(*(__private_ptr))) == 0) {\ | 49 | sizeof(*(__private_ptr))) == 0) { \ |
| 50 | (x) = (__typeof__(*(__private_ptr))) __private_val; \ | 50 | (x) = (__typeof__(*(__private_ptr))) __private_val; \ |
| 51 | __private_ret = 0; \ | 51 | __private_ret = 0; \ |
| 52 | } \ | 52 | } \ |
| 53 | __private_ret; \ | 53 | __private_ret; \ |
| 54 | }) | 54 | }) |
| 55 | 55 | ||
| 56 | #define get_user(x, ptr) \ | 56 | #define get_user(x, ptr) \ |
| @@ -89,14 +89,3 @@ struct exception_table_entry | |||
| 89 | }; | 89 | }; |
| 90 | 90 | ||
| 91 | #endif | 91 | #endif |
| 92 | |||
| 93 | /* | ||
| 94 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
| 95 | * Emacs will notice this stuff at the end of the file and automatically | ||
| 96 | * adjust the settings for this buffer only. This must remain at the end | ||
| 97 | * of the file. | ||
| 98 | * --------------------------------------------------------------------------- | ||
| 99 | * Local variables: | ||
| 100 | * c-file-style: "linux" | ||
| 101 | * End: | ||
| 102 | */ | ||
diff --git a/include/asm-v850/page.h b/include/asm-v850/page.h index b4bc85e7b91a..ad03c46a1f92 100644 --- a/include/asm-v850/page.h +++ b/include/asm-v850/page.h | |||
| @@ -111,8 +111,7 @@ typedef unsigned long pgprot_t; | |||
| 111 | #define page_to_virt(page) \ | 111 | #define page_to_virt(page) \ |
| 112 | ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) | 112 | ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) |
| 113 | 113 | ||
| 114 | #define pfn_to_page(pfn) virt_to_page (pfn_to_virt (pfn)) | 114 | #define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) |
| 115 | #define page_to_pfn(page) virt_to_pfn (page_to_virt (page)) | ||
| 116 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 115 | #define pfn_valid(pfn) ((pfn) < max_mapnr) |
| 117 | 116 | ||
| 118 | #define virt_addr_valid(kaddr) \ | 117 | #define virt_addr_valid(kaddr) \ |
| @@ -125,6 +124,7 @@ typedef unsigned long pgprot_t; | |||
| 125 | 124 | ||
| 126 | #endif /* KERNEL */ | 125 | #endif /* KERNEL */ |
| 127 | 126 | ||
| 127 | #include <asm-generic/memory_model.h> | ||
| 128 | #include <asm-generic/page.h> | 128 | #include <asm-generic/page.h> |
| 129 | 129 | ||
| 130 | #endif /* __V850_PAGE_H__ */ | 130 | #endif /* __V850_PAGE_H__ */ |
diff --git a/include/asm-x86_64/futex.h b/include/asm-x86_64/futex.h index 8602c09bf89e..9804bf07b092 100644 --- a/include/asm-x86_64/futex.h +++ b/include/asm-x86_64/futex.h | |||
| @@ -94,5 +94,32 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) | |||
| 94 | return ret; | 94 | return ret; |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | static inline int | ||
| 98 | futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) | ||
| 99 | { | ||
| 100 | if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) | ||
| 101 | return -EFAULT; | ||
| 102 | |||
| 103 | __asm__ __volatile__( | ||
| 104 | "1: " LOCK_PREFIX "cmpxchgl %3, %1 \n" | ||
| 105 | |||
| 106 | "2: .section .fixup, \"ax\" \n" | ||
| 107 | "3: mov %2, %0 \n" | ||
| 108 | " jmp 2b \n" | ||
| 109 | " .previous \n" | ||
| 110 | |||
| 111 | " .section __ex_table, \"a\" \n" | ||
| 112 | " .align 8 \n" | ||
| 113 | " .quad 1b,3b \n" | ||
| 114 | " .previous \n" | ||
| 115 | |||
| 116 | : "=a" (oldval), "=m" (*uaddr) | ||
| 117 | : "i" (-EFAULT), "r" (newval), "0" (oldval) | ||
| 118 | : "memory" | ||
| 119 | ); | ||
| 120 | |||
| 121 | return oldval; | ||
| 122 | } | ||
| 123 | |||
| 97 | #endif | 124 | #endif |
| 98 | #endif | 125 | #endif |
diff --git a/include/asm-x86_64/kdebug.h b/include/asm-x86_64/kdebug.h index b9ed4c0c8783..cf795631d9b4 100644 --- a/include/asm-x86_64/kdebug.h +++ b/include/asm-x86_64/kdebug.h | |||
| @@ -5,21 +5,20 @@ | |||
| 5 | 5 | ||
| 6 | struct pt_regs; | 6 | struct pt_regs; |
| 7 | 7 | ||
| 8 | struct die_args { | 8 | struct die_args { |
| 9 | struct pt_regs *regs; | 9 | struct pt_regs *regs; |
| 10 | const char *str; | 10 | const char *str; |
| 11 | long err; | 11 | long err; |
| 12 | int trapnr; | 12 | int trapnr; |
| 13 | int signr; | 13 | int signr; |
| 14 | }; | 14 | }; |
| 15 | |||
| 16 | extern int register_die_notifier(struct notifier_block *); | ||
| 17 | extern int unregister_die_notifier(struct notifier_block *); | ||
| 18 | extern struct atomic_notifier_head die_chain; | ||
| 15 | 19 | ||
| 16 | /* Note - you should never unregister because that can race with NMIs. | ||
| 17 | If you really want to do it first unregister - then synchronize_sched - then free. | ||
| 18 | */ | ||
| 19 | int register_die_notifier(struct notifier_block *nb); | ||
| 20 | extern struct notifier_block *die_chain; | ||
| 21 | /* Grossly misnamed. */ | 20 | /* Grossly misnamed. */ |
| 22 | enum die_val { | 21 | enum die_val { |
| 23 | DIE_OOPS = 1, | 22 | DIE_OOPS = 1, |
| 24 | DIE_INT3, | 23 | DIE_INT3, |
| 25 | DIE_DEBUG, | 24 | DIE_DEBUG, |
| @@ -33,8 +32,8 @@ enum die_val { | |||
| 33 | DIE_CALL, | 32 | DIE_CALL, |
| 34 | DIE_NMI_IPI, | 33 | DIE_NMI_IPI, |
| 35 | DIE_PAGE_FAULT, | 34 | DIE_PAGE_FAULT, |
| 36 | }; | 35 | }; |
| 37 | 36 | ||
| 38 | static inline int notify_die(enum die_val val, const char *str, | 37 | static inline int notify_die(enum die_val val, const char *str, |
| 39 | struct pt_regs *regs, long err, int trap, int sig) | 38 | struct pt_regs *regs, long err, int trap, int sig) |
| 40 | { | 39 | { |
| @@ -45,7 +44,7 @@ static inline int notify_die(enum die_val val, const char *str, | |||
| 45 | .trapnr = trap, | 44 | .trapnr = trap, |
| 46 | .signr = sig | 45 | .signr = sig |
| 47 | }; | 46 | }; |
| 48 | return notifier_call_chain(&die_chain, val, &args); | 47 | return atomic_notifier_call_chain(&die_chain, val, &args); |
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | extern int printk_address(unsigned long address); | 50 | extern int printk_address(unsigned long address); |
diff --git a/include/asm-x86_64/mmzone.h b/include/asm-x86_64/mmzone.h index 937f99b26883..6b18cd8f293d 100644 --- a/include/asm-x86_64/mmzone.h +++ b/include/asm-x86_64/mmzone.h | |||
| @@ -44,12 +44,8 @@ static inline __attribute__((pure)) int phys_to_nid(unsigned long addr) | |||
| 44 | #define pfn_to_nid(pfn) phys_to_nid((unsigned long)(pfn) << PAGE_SHIFT) | 44 | #define pfn_to_nid(pfn) phys_to_nid((unsigned long)(pfn) << PAGE_SHIFT) |
| 45 | #define kvaddr_to_nid(kaddr) phys_to_nid(__pa(kaddr)) | 45 | #define kvaddr_to_nid(kaddr) phys_to_nid(__pa(kaddr)) |
| 46 | 46 | ||
| 47 | extern struct page *pfn_to_page(unsigned long pfn); | ||
| 48 | extern unsigned long page_to_pfn(struct page *page); | ||
| 49 | extern int pfn_valid(unsigned long pfn); | 47 | extern int pfn_valid(unsigned long pfn); |
| 50 | #endif | 48 | #endif |
| 51 | 49 | ||
| 52 | #define local_mapnr(kvaddr) \ | ||
| 53 | ( (__pa(kvaddr) >> PAGE_SHIFT) - node_start_pfn(kvaddr_to_nid(kvaddr)) ) | ||
| 54 | #endif | 50 | #endif |
| 55 | #endif | 51 | #endif |
diff --git a/include/asm-x86_64/page.h b/include/asm-x86_64/page.h index 615e3e494929..408185bac351 100644 --- a/include/asm-x86_64/page.h +++ b/include/asm-x86_64/page.h | |||
| @@ -123,8 +123,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 123 | #define __boot_va(x) __va(x) | 123 | #define __boot_va(x) __va(x) |
| 124 | #define __boot_pa(x) __pa(x) | 124 | #define __boot_pa(x) __pa(x) |
| 125 | #ifdef CONFIG_FLATMEM | 125 | #ifdef CONFIG_FLATMEM |
| 126 | #define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 127 | #define page_to_pfn(page) ((unsigned long)((page) - mem_map)) | ||
| 128 | #define pfn_valid(pfn) ((pfn) < end_pfn) | 126 | #define pfn_valid(pfn) ((pfn) < end_pfn) |
| 129 | #endif | 127 | #endif |
| 130 | 128 | ||
| @@ -140,6 +138,7 @@ typedef struct { unsigned long pgprot; } pgprot_t; | |||
| 140 | 138 | ||
| 141 | #endif /* __KERNEL__ */ | 139 | #endif /* __KERNEL__ */ |
| 142 | 140 | ||
| 141 | #include <asm-generic/memory_model.h> | ||
| 143 | #include <asm-generic/page.h> | 142 | #include <asm-generic/page.h> |
| 144 | 143 | ||
| 145 | #endif /* _X86_64_PAGE_H */ | 144 | #endif /* _X86_64_PAGE_H */ |
diff --git a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h index 8c8d88c036ed..1aa2cee43344 100644 --- a/include/asm-x86_64/processor.h +++ b/include/asm-x86_64/processor.h | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <asm/mmsegment.h> | 20 | #include <asm/mmsegment.h> |
| 21 | #include <asm/percpu.h> | 21 | #include <asm/percpu.h> |
| 22 | #include <linux/personality.h> | 22 | #include <linux/personality.h> |
| 23 | #include <linux/cpumask.h> | ||
| 23 | 24 | ||
| 24 | #define TF_MASK 0x00000100 | 25 | #define TF_MASK 0x00000100 |
| 25 | #define IF_MASK 0x00000200 | 26 | #define IF_MASK 0x00000200 |
| @@ -65,6 +66,9 @@ struct cpuinfo_x86 { | |||
| 65 | __u32 x86_power; | 66 | __u32 x86_power; |
| 66 | __u32 extended_cpuid_level; /* Max extended CPUID function supported */ | 67 | __u32 extended_cpuid_level; /* Max extended CPUID function supported */ |
| 67 | unsigned long loops_per_jiffy; | 68 | unsigned long loops_per_jiffy; |
| 69 | #ifdef CONFIG_SMP | ||
| 70 | cpumask_t llc_shared_map; /* cpus sharing the last level cache */ | ||
| 71 | #endif | ||
| 68 | __u8 apicid; | 72 | __u8 apicid; |
| 69 | __u8 booted_cores; /* number of cores as seen by OS */ | 73 | __u8 booted_cores; /* number of cores as seen by OS */ |
| 70 | } ____cacheline_aligned; | 74 | } ____cacheline_aligned; |
diff --git a/include/asm-x86_64/smp.h b/include/asm-x86_64/smp.h index 9ccbb2cfd5c0..a4fdaeb5c397 100644 --- a/include/asm-x86_64/smp.h +++ b/include/asm-x86_64/smp.h | |||
| @@ -56,6 +56,7 @@ extern cpumask_t cpu_sibling_map[NR_CPUS]; | |||
| 56 | extern cpumask_t cpu_core_map[NR_CPUS]; | 56 | extern cpumask_t cpu_core_map[NR_CPUS]; |
| 57 | extern u8 phys_proc_id[NR_CPUS]; | 57 | extern u8 phys_proc_id[NR_CPUS]; |
| 58 | extern u8 cpu_core_id[NR_CPUS]; | 58 | extern u8 cpu_core_id[NR_CPUS]; |
| 59 | extern u8 cpu_llc_id[NR_CPUS]; | ||
| 59 | 60 | ||
| 60 | #define SMP_TRAMPOLINE_BASE 0x6000 | 61 | #define SMP_TRAMPOLINE_BASE 0x6000 |
| 61 | 62 | ||
diff --git a/include/asm-x86_64/topology.h b/include/asm-x86_64/topology.h index c642f5d9882d..9db54e9d17bb 100644 --- a/include/asm-x86_64/topology.h +++ b/include/asm-x86_64/topology.h | |||
| @@ -68,4 +68,6 @@ extern int __node_distance(int, int); | |||
| 68 | 68 | ||
| 69 | #include <asm-generic/topology.h> | 69 | #include <asm-generic/topology.h> |
| 70 | 70 | ||
| 71 | extern cpumask_t cpu_coregroup_map(int cpu); | ||
| 72 | |||
| 71 | #endif | 73 | #endif |
diff --git a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h index da0341c57949..fcc516353087 100644 --- a/include/asm-x86_64/unistd.h +++ b/include/asm-x86_64/unistd.h | |||
| @@ -605,8 +605,12 @@ __SYSCALL(__NR_pselect6, sys_ni_syscall) /* for now */ | |||
| 605 | __SYSCALL(__NR_ppoll, sys_ni_syscall) /* for now */ | 605 | __SYSCALL(__NR_ppoll, sys_ni_syscall) /* for now */ |
| 606 | #define __NR_unshare 272 | 606 | #define __NR_unshare 272 |
| 607 | __SYSCALL(__NR_unshare, sys_unshare) | 607 | __SYSCALL(__NR_unshare, sys_unshare) |
| 608 | #define __NR_set_robust_list 273 | ||
| 609 | __SYSCALL(__NR_set_robust_list, sys_set_robust_list) | ||
| 610 | #define __NR_get_robust_list 274 | ||
| 611 | __SYSCALL(__NR_get_robust_list, sys_get_robust_list) | ||
| 608 | 612 | ||
| 609 | #define __NR_syscall_max __NR_unshare | 613 | #define __NR_syscall_max __NR_get_robust_list |
| 610 | 614 | ||
| 611 | #ifndef __NO_STUBS | 615 | #ifndef __NO_STUBS |
| 612 | 616 | ||
diff --git a/include/asm-xtensa/page.h b/include/asm-xtensa/page.h index 8ded36f255a2..992bac5c1258 100644 --- a/include/asm-xtensa/page.h +++ b/include/asm-xtensa/page.h | |||
| @@ -109,10 +109,7 @@ void copy_user_page(void *to,void* from,unsigned long vaddr,struct page* page); | |||
| 109 | #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) | 109 | #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) |
| 110 | #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) | 110 | #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) |
| 111 | #define pfn_valid(pfn) ((unsigned long)pfn < max_mapnr) | 111 | #define pfn_valid(pfn) ((unsigned long)pfn < max_mapnr) |
| 112 | #ifndef CONFIG_DISCONTIGMEM | 112 | #ifdef CONFIG_DISCONTIGMEM |
| 113 | # define pfn_to_page(pfn) (mem_map + (pfn)) | ||
| 114 | # define page_to_pfn(page) ((unsigned long)((page) - mem_map)) | ||
| 115 | #else | ||
| 116 | # error CONFIG_DISCONTIGMEM not supported | 113 | # error CONFIG_DISCONTIGMEM not supported |
| 117 | #endif | 114 | #endif |
| 118 | 115 | ||
| @@ -130,4 +127,5 @@ void copy_user_page(void *to,void* from,unsigned long vaddr,struct page* page); | |||
| 130 | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) | 127 | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) |
| 131 | 128 | ||
| 132 | #endif /* __KERNEL__ */ | 129 | #endif /* __KERNEL__ */ |
| 130 | #include <asm-generic/memory_model.h> | ||
| 133 | #endif /* _XTENSA_PAGE_H */ | 131 | #endif /* _XTENSA_PAGE_H */ |
diff --git a/include/linux/adb.h b/include/linux/adb.h index e9fdc63483c7..b7305b178279 100644 --- a/include/linux/adb.h +++ b/include/linux/adb.h | |||
| @@ -85,7 +85,7 @@ enum adb_message { | |||
| 85 | ADB_MSG_POST_RESET /* Called after resetting the bus (re-do init & register) */ | 85 | ADB_MSG_POST_RESET /* Called after resetting the bus (re-do init & register) */ |
| 86 | }; | 86 | }; |
| 87 | extern struct adb_driver *adb_controller; | 87 | extern struct adb_driver *adb_controller; |
| 88 | extern struct notifier_block *adb_client_list; | 88 | extern struct blocking_notifier_head adb_client_list; |
| 89 | 89 | ||
| 90 | int adb_request(struct adb_request *req, void (*done)(struct adb_request *), | 90 | int adb_request(struct adb_request *req, void (*done)(struct adb_request *), |
| 91 | int flags, int nbytes, ...); | 91 | int flags, int nbytes, ...); |
diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h index 9343c89d843c..0a6bc52ffe88 100644 --- a/include/linux/auto_fs4.h +++ b/include/linux/auto_fs4.h | |||
| @@ -19,18 +19,37 @@ | |||
| 19 | #undef AUTOFS_MIN_PROTO_VERSION | 19 | #undef AUTOFS_MIN_PROTO_VERSION |
| 20 | #undef AUTOFS_MAX_PROTO_VERSION | 20 | #undef AUTOFS_MAX_PROTO_VERSION |
| 21 | 21 | ||
| 22 | #define AUTOFS_PROTO_VERSION 4 | 22 | #define AUTOFS_PROTO_VERSION 5 |
| 23 | #define AUTOFS_MIN_PROTO_VERSION 3 | 23 | #define AUTOFS_MIN_PROTO_VERSION 3 |
| 24 | #define AUTOFS_MAX_PROTO_VERSION 4 | 24 | #define AUTOFS_MAX_PROTO_VERSION 5 |
| 25 | 25 | ||
| 26 | #define AUTOFS_PROTO_SUBVERSION 7 | 26 | #define AUTOFS_PROTO_SUBVERSION 0 |
| 27 | 27 | ||
| 28 | /* Mask for expire behaviour */ | 28 | /* Mask for expire behaviour */ |
| 29 | #define AUTOFS_EXP_IMMEDIATE 1 | 29 | #define AUTOFS_EXP_IMMEDIATE 1 |
| 30 | #define AUTOFS_EXP_LEAVES 2 | 30 | #define AUTOFS_EXP_LEAVES 2 |
| 31 | 31 | ||
| 32 | /* New message type */ | 32 | /* Daemon notification packet types */ |
| 33 | #define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ | 33 | enum autofs_notify { |
| 34 | NFY_NONE, | ||
| 35 | NFY_MOUNT, | ||
| 36 | NFY_EXPIRE | ||
| 37 | }; | ||
| 38 | |||
| 39 | /* Kernel protocol version 4 packet types */ | ||
| 40 | |||
| 41 | /* Expire entry (umount request) */ | ||
| 42 | #define autofs_ptype_expire_multi 2 | ||
| 43 | |||
| 44 | /* Kernel protocol version 5 packet types */ | ||
| 45 | |||
| 46 | /* Indirect mount missing and expire requests. */ | ||
| 47 | #define autofs_ptype_missing_indirect 3 | ||
| 48 | #define autofs_ptype_expire_indirect 4 | ||
| 49 | |||
| 50 | /* Direct mount missing and expire requests */ | ||
| 51 | #define autofs_ptype_missing_direct 5 | ||
| 52 | #define autofs_ptype_expire_direct 6 | ||
| 34 | 53 | ||
| 35 | /* v4 multi expire (via pipe) */ | 54 | /* v4 multi expire (via pipe) */ |
| 36 | struct autofs_packet_expire_multi { | 55 | struct autofs_packet_expire_multi { |
| @@ -40,14 +59,36 @@ struct autofs_packet_expire_multi { | |||
| 40 | char name[NAME_MAX+1]; | 59 | char name[NAME_MAX+1]; |
| 41 | }; | 60 | }; |
| 42 | 61 | ||
| 62 | /* autofs v5 common packet struct */ | ||
| 63 | struct autofs_v5_packet { | ||
| 64 | struct autofs_packet_hdr hdr; | ||
| 65 | autofs_wqt_t wait_queue_token; | ||
| 66 | __u32 dev; | ||
| 67 | __u64 ino; | ||
| 68 | __u32 uid; | ||
| 69 | __u32 gid; | ||
| 70 | __u32 pid; | ||
| 71 | __u32 tgid; | ||
| 72 | __u32 len; | ||
| 73 | char name[NAME_MAX+1]; | ||
| 74 | }; | ||
| 75 | |||
| 76 | typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; | ||
| 77 | typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; | ||
| 78 | typedef struct autofs_v5_packet autofs_packet_missing_direct_t; | ||
| 79 | typedef struct autofs_v5_packet autofs_packet_expire_direct_t; | ||
| 80 | |||
| 43 | union autofs_packet_union { | 81 | union autofs_packet_union { |
| 44 | struct autofs_packet_hdr hdr; | 82 | struct autofs_packet_hdr hdr; |
| 45 | struct autofs_packet_missing missing; | 83 | struct autofs_packet_missing missing; |
| 46 | struct autofs_packet_expire expire; | 84 | struct autofs_packet_expire expire; |
| 47 | struct autofs_packet_expire_multi expire_multi; | 85 | struct autofs_packet_expire_multi expire_multi; |
| 86 | struct autofs_v5_packet v5_packet; | ||
| 48 | }; | 87 | }; |
| 49 | 88 | ||
| 50 | #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) | 89 | #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) |
| 90 | #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI | ||
| 91 | #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI | ||
| 51 | #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) | 92 | #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) |
| 52 | #define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) | 93 | #define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) |
| 53 | #define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) | 94 | #define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) |
diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 7155452fb4a8..de3eb8d8ae26 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h | |||
| @@ -38,6 +38,7 @@ typedef struct bootmem_data { | |||
| 38 | unsigned long last_pos; | 38 | unsigned long last_pos; |
| 39 | unsigned long last_success; /* Previous allocation point. To speed | 39 | unsigned long last_success; /* Previous allocation point. To speed |
| 40 | * up searching */ | 40 | * up searching */ |
| 41 | struct list_head list; | ||
| 41 | } bootmem_data_t; | 42 | } bootmem_data_t; |
| 42 | 43 | ||
| 43 | extern unsigned long __init bootmem_bootmap_pages (unsigned long); | 44 | extern unsigned long __init bootmem_bootmap_pages (unsigned long); |
diff --git a/include/linux/compat.h b/include/linux/compat.h index 24d659cdbafe..6d3a654be1ae 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h | |||
| @@ -147,6 +147,24 @@ typedef struct compat_sigevent { | |||
| 147 | } _sigev_un; | 147 | } _sigev_un; |
| 148 | } compat_sigevent_t; | 148 | } compat_sigevent_t; |
| 149 | 149 | ||
| 150 | struct compat_robust_list { | ||
| 151 | compat_uptr_t next; | ||
| 152 | }; | ||
| 153 | |||
| 154 | struct compat_robust_list_head { | ||
| 155 | struct compat_robust_list list; | ||
| 156 | compat_long_t futex_offset; | ||
| 157 | compat_uptr_t list_op_pending; | ||
| 158 | }; | ||
| 159 | |||
| 160 | extern void compat_exit_robust_list(struct task_struct *curr); | ||
| 161 | |||
| 162 | asmlinkage long | ||
| 163 | compat_sys_set_robust_list(struct compat_robust_list_head __user *head, | ||
| 164 | compat_size_t len); | ||
| 165 | asmlinkage long | ||
| 166 | compat_sys_get_robust_list(int pid, compat_uptr_t *head_ptr, | ||
| 167 | compat_size_t __user *len_ptr); | ||
| 150 | 168 | ||
| 151 | long compat_sys_semctl(int first, int second, int third, void __user *uptr); | 169 | long compat_sys_semctl(int first, int second, int third, void __user *uptr); |
| 152 | long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); | 170 | long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); |
diff --git a/include/linux/compat_ioctl.h b/include/linux/compat_ioctl.h index efb518f16bb3..89ab677cb993 100644 --- a/include/linux/compat_ioctl.h +++ b/include/linux/compat_ioctl.h | |||
| @@ -140,6 +140,7 @@ COMPATIBLE_IOCTL(DM_TABLE_DEPS_32) | |||
| 140 | COMPATIBLE_IOCTL(DM_TABLE_STATUS_32) | 140 | COMPATIBLE_IOCTL(DM_TABLE_STATUS_32) |
| 141 | COMPATIBLE_IOCTL(DM_LIST_VERSIONS_32) | 141 | COMPATIBLE_IOCTL(DM_LIST_VERSIONS_32) |
| 142 | COMPATIBLE_IOCTL(DM_TARGET_MSG_32) | 142 | COMPATIBLE_IOCTL(DM_TARGET_MSG_32) |
| 143 | COMPATIBLE_IOCTL(DM_DEV_SET_GEOMETRY_32) | ||
| 143 | COMPATIBLE_IOCTL(DM_VERSION) | 144 | COMPATIBLE_IOCTL(DM_VERSION) |
| 144 | COMPATIBLE_IOCTL(DM_REMOVE_ALL) | 145 | COMPATIBLE_IOCTL(DM_REMOVE_ALL) |
| 145 | COMPATIBLE_IOCTL(DM_LIST_DEVICES) | 146 | COMPATIBLE_IOCTL(DM_LIST_DEVICES) |
| @@ -155,6 +156,7 @@ COMPATIBLE_IOCTL(DM_TABLE_DEPS) | |||
| 155 | COMPATIBLE_IOCTL(DM_TABLE_STATUS) | 156 | COMPATIBLE_IOCTL(DM_TABLE_STATUS) |
| 156 | COMPATIBLE_IOCTL(DM_LIST_VERSIONS) | 157 | COMPATIBLE_IOCTL(DM_LIST_VERSIONS) |
| 157 | COMPATIBLE_IOCTL(DM_TARGET_MSG) | 158 | COMPATIBLE_IOCTL(DM_TARGET_MSG) |
| 159 | COMPATIBLE_IOCTL(DM_DEV_SET_GEOMETRY) | ||
| 158 | /* Big K */ | 160 | /* Big K */ |
| 159 | COMPATIBLE_IOCTL(PIO_FONT) | 161 | COMPATIBLE_IOCTL(PIO_FONT) |
| 160 | COMPATIBLE_IOCTL(GIO_FONT) | 162 | COMPATIBLE_IOCTL(GIO_FONT) |
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 51e0e95a421a..aee10b2ea4c6 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h | |||
| @@ -97,6 +97,7 @@ struct io_restrictions { | |||
| 97 | unsigned short hardsect_size; | 97 | unsigned short hardsect_size; |
| 98 | unsigned int max_segment_size; | 98 | unsigned int max_segment_size; |
| 99 | unsigned long seg_boundary_mask; | 99 | unsigned long seg_boundary_mask; |
| 100 | unsigned char no_cluster; /* inverted so that 0 is default */ | ||
| 100 | }; | 101 | }; |
| 101 | 102 | ||
| 102 | struct dm_target { | 103 | struct dm_target { |
diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h index fa75ba0d635e..c67c6786612a 100644 --- a/include/linux/dm-ioctl.h +++ b/include/linux/dm-ioctl.h | |||
| @@ -80,6 +80,16 @@ | |||
| 80 | * | 80 | * |
| 81 | * DM_TARGET_MSG: | 81 | * DM_TARGET_MSG: |
| 82 | * Pass a message string to the target at a specific offset of a device. | 82 | * Pass a message string to the target at a specific offset of a device. |
| 83 | * | ||
| 84 | * DM_DEV_SET_GEOMETRY: | ||
| 85 | * Set the geometry of a device by passing in a string in this format: | ||
| 86 | * | ||
| 87 | * "cylinders heads sectors_per_track start_sector" | ||
| 88 | * | ||
| 89 | * Beware that CHS geometry is nearly obsolete and only provided | ||
| 90 | * for compatibility with dm devices that can be booted by a PC | ||
| 91 | * BIOS. See struct hd_geometry for range limits. Also note that | ||
| 92 | * the geometry is erased if the device size changes. | ||
| 83 | */ | 93 | */ |
| 84 | 94 | ||
| 85 | /* | 95 | /* |
| @@ -218,6 +228,7 @@ enum { | |||
| 218 | /* Added later */ | 228 | /* Added later */ |
| 219 | DM_LIST_VERSIONS_CMD, | 229 | DM_LIST_VERSIONS_CMD, |
| 220 | DM_TARGET_MSG_CMD, | 230 | DM_TARGET_MSG_CMD, |
| 231 | DM_DEV_SET_GEOMETRY_CMD | ||
| 221 | }; | 232 | }; |
| 222 | 233 | ||
| 223 | /* | 234 | /* |
| @@ -247,6 +258,7 @@ typedef char ioctl_struct[308]; | |||
| 247 | #define DM_TABLE_STATUS_32 _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, ioctl_struct) | 258 | #define DM_TABLE_STATUS_32 _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, ioctl_struct) |
| 248 | #define DM_LIST_VERSIONS_32 _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, ioctl_struct) | 259 | #define DM_LIST_VERSIONS_32 _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, ioctl_struct) |
| 249 | #define DM_TARGET_MSG_32 _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, ioctl_struct) | 260 | #define DM_TARGET_MSG_32 _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, ioctl_struct) |
| 261 | #define DM_DEV_SET_GEOMETRY_32 _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, ioctl_struct) | ||
| 250 | #endif | 262 | #endif |
| 251 | 263 | ||
| 252 | #define DM_IOCTL 0xfd | 264 | #define DM_IOCTL 0xfd |
| @@ -270,11 +282,12 @@ typedef char ioctl_struct[308]; | |||
| 270 | #define DM_LIST_VERSIONS _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, struct dm_ioctl) | 282 | #define DM_LIST_VERSIONS _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, struct dm_ioctl) |
| 271 | 283 | ||
| 272 | #define DM_TARGET_MSG _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl) | 284 | #define DM_TARGET_MSG _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl) |
| 285 | #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) | ||
| 273 | 286 | ||
| 274 | #define DM_VERSION_MAJOR 4 | 287 | #define DM_VERSION_MAJOR 4 |
| 275 | #define DM_VERSION_MINOR 5 | 288 | #define DM_VERSION_MINOR 6 |
| 276 | #define DM_VERSION_PATCHLEVEL 0 | 289 | #define DM_VERSION_PATCHLEVEL 0 |
| 277 | #define DM_VERSION_EXTRA "-ioctl (2005-10-04)" | 290 | #define DM_VERSION_EXTRA "-ioctl (2006-02-17)" |
| 278 | 291 | ||
| 279 | /* Status bits */ | 292 | /* Status bits */ |
| 280 | #define DM_READONLY_FLAG (1 << 0) /* In/Out */ | 293 | #define DM_READONLY_FLAG (1 << 0) /* In/Out */ |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 9d9674946956..680d913350e7 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
| @@ -410,6 +410,9 @@ struct block_device { | |||
| 410 | struct list_head bd_inodes; | 410 | struct list_head bd_inodes; |
| 411 | void * bd_holder; | 411 | void * bd_holder; |
| 412 | int bd_holders; | 412 | int bd_holders; |
| 413 | #ifdef CONFIG_SYSFS | ||
| 414 | struct list_head bd_holder_list; | ||
| 415 | #endif | ||
| 413 | struct block_device * bd_contains; | 416 | struct block_device * bd_contains; |
| 414 | unsigned bd_block_size; | 417 | unsigned bd_block_size; |
| 415 | struct hd_struct * bd_part; | 418 | struct hd_struct * bd_part; |
| @@ -1399,6 +1402,13 @@ extern int blkdev_get(struct block_device *, mode_t, unsigned); | |||
| 1399 | extern int blkdev_put(struct block_device *); | 1402 | extern int blkdev_put(struct block_device *); |
| 1400 | extern int bd_claim(struct block_device *, void *); | 1403 | extern int bd_claim(struct block_device *, void *); |
| 1401 | extern void bd_release(struct block_device *); | 1404 | extern void bd_release(struct block_device *); |
| 1405 | #ifdef CONFIG_SYSFS | ||
| 1406 | extern int bd_claim_by_disk(struct block_device *, void *, struct gendisk *); | ||
| 1407 | extern void bd_release_from_disk(struct block_device *, struct gendisk *); | ||
| 1408 | #else | ||
| 1409 | #define bd_claim_by_disk(bdev, holder, disk) bd_claim(bdev, holder) | ||
| 1410 | #define bd_release_from_disk(bdev, disk) bd_release(bdev) | ||
| 1411 | #endif | ||
| 1402 | 1412 | ||
| 1403 | /* fs/char_dev.c */ | 1413 | /* fs/char_dev.c */ |
| 1404 | extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); | 1414 | extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); |
diff --git a/include/linux/futex.h b/include/linux/futex.h index 10f96c31971e..966a5b3da439 100644 --- a/include/linux/futex.h +++ b/include/linux/futex.h | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #ifndef _LINUX_FUTEX_H | 1 | #ifndef _LINUX_FUTEX_H |
| 2 | #define _LINUX_FUTEX_H | 2 | #define _LINUX_FUTEX_H |
| 3 | 3 | ||
| 4 | #include <linux/sched.h> | ||
| 5 | |||
| 4 | /* Second argument to futex syscall */ | 6 | /* Second argument to futex syscall */ |
| 5 | 7 | ||
| 6 | 8 | ||
| @@ -11,10 +13,97 @@ | |||
| 11 | #define FUTEX_CMP_REQUEUE 4 | 13 | #define FUTEX_CMP_REQUEUE 4 |
| 12 | #define FUTEX_WAKE_OP 5 | 14 | #define FUTEX_WAKE_OP 5 |
| 13 | 15 | ||
| 16 | /* | ||
| 17 | * Support for robust futexes: the kernel cleans up held futexes at | ||
| 18 | * thread exit time. | ||
| 19 | */ | ||
| 20 | |||
| 21 | /* | ||
| 22 | * Per-lock list entry - embedded in user-space locks, somewhere close | ||
| 23 | * to the futex field. (Note: user-space uses a double-linked list to | ||
| 24 | * achieve O(1) list add and remove, but the kernel only needs to know | ||
| 25 | * about the forward link) | ||
| 26 | * | ||
| 27 | * NOTE: this structure is part of the syscall ABI, and must not be | ||
| 28 | * changed. | ||
| 29 | */ | ||
| 30 | struct robust_list { | ||
| 31 | struct robust_list __user *next; | ||
| 32 | }; | ||
| 33 | |||
| 34 | /* | ||
| 35 | * Per-thread list head: | ||
| 36 | * | ||
| 37 | * NOTE: this structure is part of the syscall ABI, and must only be | ||
| 38 | * changed if the change is first communicated with the glibc folks. | ||
| 39 | * (When an incompatible change is done, we'll increase the structure | ||
| 40 | * size, which glibc will detect) | ||
| 41 | */ | ||
| 42 | struct robust_list_head { | ||
| 43 | /* | ||
| 44 | * The head of the list. Points back to itself if empty: | ||
| 45 | */ | ||
| 46 | struct robust_list list; | ||
| 47 | |||
| 48 | /* | ||
| 49 | * This relative offset is set by user-space, it gives the kernel | ||
| 50 | * the relative position of the futex field to examine. This way | ||
| 51 | * we keep userspace flexible, to freely shape its data-structure, | ||
| 52 | * without hardcoding any particular offset into the kernel: | ||
| 53 | */ | ||
| 54 | long futex_offset; | ||
| 55 | |||
| 56 | /* | ||
| 57 | * The death of the thread may race with userspace setting | ||
| 58 | * up a lock's links. So to handle this race, userspace first | ||
| 59 | * sets this field to the address of the to-be-taken lock, | ||
| 60 | * then does the lock acquire, and then adds itself to the | ||
| 61 | * list, and then clears this field. Hence the kernel will | ||
| 62 | * always have full knowledge of all locks that the thread | ||
| 63 | * _might_ have taken. We check the owner TID in any case, | ||
| 64 | * so only truly owned locks will be handled. | ||
| 65 | */ | ||
| 66 | struct robust_list __user *list_op_pending; | ||
| 67 | }; | ||
| 68 | |||
| 69 | /* | ||
| 70 | * Are there any waiters for this robust futex: | ||
| 71 | */ | ||
| 72 | #define FUTEX_WAITERS 0x80000000 | ||
| 73 | |||
| 74 | /* | ||
| 75 | * The kernel signals via this bit that a thread holding a futex | ||
| 76 | * has exited without unlocking the futex. The kernel also does | ||
| 77 | * a FUTEX_WAKE on such futexes, after setting the bit, to wake | ||
| 78 | * up any possible waiters: | ||
| 79 | */ | ||
| 80 | #define FUTEX_OWNER_DIED 0x40000000 | ||
| 81 | |||
| 82 | /* | ||
| 83 | * The rest of the robust-futex field is for the TID: | ||
| 84 | */ | ||
| 85 | #define FUTEX_TID_MASK 0x3fffffff | ||
| 86 | |||
| 87 | /* | ||
| 88 | * This limit protects against a deliberately circular list. | ||
| 89 | * (Not worth introducing an rlimit for it) | ||
| 90 | */ | ||
| 91 | #define ROBUST_LIST_LIMIT 2048 | ||
| 92 | |||
| 14 | long do_futex(unsigned long uaddr, int op, int val, | 93 | long do_futex(unsigned long uaddr, int op, int val, |
| 15 | unsigned long timeout, unsigned long uaddr2, int val2, | 94 | unsigned long timeout, unsigned long uaddr2, int val2, |
| 16 | int val3); | 95 | int val3); |
| 17 | 96 | ||
| 97 | extern int handle_futex_death(u32 __user *uaddr, struct task_struct *curr); | ||
| 98 | |||
| 99 | #ifdef CONFIG_FUTEX | ||
| 100 | extern void exit_robust_list(struct task_struct *curr); | ||
| 101 | #else | ||
| 102 | static inline void exit_robust_list(struct task_struct *curr) | ||
| 103 | { | ||
| 104 | } | ||
| 105 | #endif | ||
| 106 | |||
| 18 | #define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */ | 107 | #define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */ |
| 19 | #define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */ | 108 | #define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */ |
| 20 | #define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */ | 109 | #define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */ |
diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 179fea53fc81..3c1b0294a742 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h | |||
| @@ -78,6 +78,7 @@ struct hd_struct { | |||
| 78 | sector_t start_sect; | 78 | sector_t start_sect; |
| 79 | sector_t nr_sects; | 79 | sector_t nr_sects; |
| 80 | struct kobject kobj; | 80 | struct kobject kobj; |
| 81 | struct kobject *holder_dir; | ||
| 81 | unsigned ios[2], sectors[2]; /* READs and WRITEs */ | 82 | unsigned ios[2], sectors[2]; /* READs and WRITEs */ |
| 82 | int policy, partno; | 83 | int policy, partno; |
| 83 | }; | 84 | }; |
| @@ -114,6 +115,8 @@ struct gendisk { | |||
| 114 | int number; /* more of the same */ | 115 | int number; /* more of the same */ |
| 115 | struct device *driverfs_dev; | 116 | struct device *driverfs_dev; |
| 116 | struct kobject kobj; | 117 | struct kobject kobj; |
| 118 | struct kobject *holder_dir; | ||
| 119 | struct kobject *slave_dir; | ||
| 117 | 120 | ||
| 118 | struct timer_rand_state *random; | 121 | struct timer_rand_state *random; |
| 119 | int policy; | 122 | int policy; |
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 679b46a6a565..c8b81f419fd8 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h | |||
| @@ -108,6 +108,10 @@ | |||
| 108 | #define I2C_DRIVERID_UPD64083 78 /* upd64083 video processor */ | 108 | #define I2C_DRIVERID_UPD64083 78 /* upd64083 video processor */ |
| 109 | #define I2C_DRIVERID_UPD64031A 79 /* upd64031a video processor */ | 109 | #define I2C_DRIVERID_UPD64031A 79 /* upd64031a video processor */ |
| 110 | #define I2C_DRIVERID_SAA717X 80 /* saa717x video encoder */ | 110 | #define I2C_DRIVERID_SAA717X 80 /* saa717x video encoder */ |
| 111 | #define I2C_DRIVERID_DS1672 81 /* Dallas/Maxim DS1672 RTC */ | ||
| 112 | #define I2C_DRIVERID_X1205 82 /* Xicor/Intersil X1205 RTC */ | ||
| 113 | #define I2C_DRIVERID_PCF8563 83 /* Philips PCF8563 RTC */ | ||
| 114 | #define I2C_DRIVERID_RS5C372 84 /* Ricoh RS5C372 RTC */ | ||
| 111 | 115 | ||
| 112 | #define I2C_DRIVERID_I2CDEV 900 | 116 | #define I2C_DRIVERID_I2CDEV 900 |
| 113 | #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ | 117 | #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 03d6cfaa5b8a..a3720f973ea5 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
| @@ -87,7 +87,7 @@ extern int cond_resched(void); | |||
| 87 | (__x < 0) ? -__x : __x; \ | 87 | (__x < 0) ? -__x : __x; \ |
| 88 | }) | 88 | }) |
| 89 | 89 | ||
| 90 | extern struct notifier_block *panic_notifier_list; | 90 | extern struct atomic_notifier_head panic_notifier_list; |
| 91 | extern long (*panic_blink)(long time); | 91 | extern long (*panic_blink)(long time); |
| 92 | NORET_TYPE void panic(const char * fmt, ...) | 92 | NORET_TYPE void panic(const char * fmt, ...) |
| 93 | __attribute__ ((NORET_AND format (printf, 1, 2))); | 93 | __attribute__ ((NORET_AND format (printf, 1, 2))); |
diff --git a/include/linux/m48t86.h b/include/linux/m48t86.h new file mode 100644 index 000000000000..9065199319d0 --- /dev/null +++ b/include/linux/m48t86.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* | ||
| 2 | * ST M48T86 / Dallas DS12887 RTC driver | ||
| 3 | * Copyright (c) 2006 Tower Technologies | ||
| 4 | * | ||
| 5 | * Author: Alessandro Zummo <a.zummo@towertech.it> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | struct m48t86_ops | ||
| 13 | { | ||
| 14 | void (*writeb)(unsigned char value, unsigned long addr); | ||
| 15 | unsigned char (*readb)(unsigned long addr); | ||
| 16 | }; | ||
diff --git a/include/linux/memory.h b/include/linux/memory.h index e251dc43d0f5..8f04143ca363 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h | |||
| @@ -77,7 +77,6 @@ extern int remove_memory_block(unsigned long, struct mem_section *, int); | |||
| 77 | 77 | ||
| 78 | #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION<<PAGE_SHIFT) | 78 | #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION<<PAGE_SHIFT) |
| 79 | 79 | ||
| 80 | struct notifier_block; | ||
| 81 | 80 | ||
| 82 | #endif /* CONFIG_MEMORY_HOTPLUG */ | 81 | #endif /* CONFIG_MEMORY_HOTPLUG */ |
| 83 | 82 | ||
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index ebfc238cc243..b5c21122c299 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/numa.h> | 13 | #include <linux/numa.h> |
| 14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
| 15 | #include <linux/seqlock.h> | 15 | #include <linux/seqlock.h> |
| 16 | #include <linux/nodemask.h> | ||
| 16 | #include <asm/atomic.h> | 17 | #include <asm/atomic.h> |
| 17 | 18 | ||
| 18 | /* Free memory management - zoned buddy allocator. */ | 19 | /* Free memory management - zoned buddy allocator. */ |
| @@ -225,7 +226,6 @@ struct zone { | |||
| 225 | * Discontig memory support fields. | 226 | * Discontig memory support fields. |
| 226 | */ | 227 | */ |
| 227 | struct pglist_data *zone_pgdat; | 228 | struct pglist_data *zone_pgdat; |
| 228 | struct page *zone_mem_map; | ||
| 229 | /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */ | 229 | /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */ |
| 230 | unsigned long zone_start_pfn; | 230 | unsigned long zone_start_pfn; |
| 231 | 231 | ||
| @@ -307,7 +307,6 @@ typedef struct pglist_data { | |||
| 307 | unsigned long node_spanned_pages; /* total size of physical page | 307 | unsigned long node_spanned_pages; /* total size of physical page |
| 308 | range, including holes */ | 308 | range, including holes */ |
| 309 | int node_id; | 309 | int node_id; |
| 310 | struct pglist_data *pgdat_next; | ||
| 311 | wait_queue_head_t kswapd_wait; | 310 | wait_queue_head_t kswapd_wait; |
| 312 | struct task_struct *kswapd; | 311 | struct task_struct *kswapd; |
| 313 | int kswapd_max_order; | 312 | int kswapd_max_order; |
| @@ -324,8 +323,6 @@ typedef struct pglist_data { | |||
| 324 | 323 | ||
| 325 | #include <linux/memory_hotplug.h> | 324 | #include <linux/memory_hotplug.h> |
| 326 | 325 | ||
| 327 | extern struct pglist_data *pgdat_list; | ||
| 328 | |||
| 329 | void __get_zone_counts(unsigned long *active, unsigned long *inactive, | 326 | void __get_zone_counts(unsigned long *active, unsigned long *inactive, |
| 330 | unsigned long *free, struct pglist_data *pgdat); | 327 | unsigned long *free, struct pglist_data *pgdat); |
| 331 | void get_zone_counts(unsigned long *active, unsigned long *inactive, | 328 | void get_zone_counts(unsigned long *active, unsigned long *inactive, |
| @@ -350,57 +347,6 @@ unsigned long __init node_memmap_size_bytes(int, unsigned long, unsigned long); | |||
| 350 | */ | 347 | */ |
| 351 | #define zone_idx(zone) ((zone) - (zone)->zone_pgdat->node_zones) | 348 | #define zone_idx(zone) ((zone) - (zone)->zone_pgdat->node_zones) |
| 352 | 349 | ||
| 353 | /** | ||
| 354 | * for_each_pgdat - helper macro to iterate over all nodes | ||
| 355 | * @pgdat - pointer to a pg_data_t variable | ||
| 356 | * | ||
| 357 | * Meant to help with common loops of the form | ||
| 358 | * pgdat = pgdat_list; | ||
| 359 | * while(pgdat) { | ||
| 360 | * ... | ||
| 361 | * pgdat = pgdat->pgdat_next; | ||
| 362 | * } | ||
| 363 | */ | ||
| 364 | #define for_each_pgdat(pgdat) \ | ||
| 365 | for (pgdat = pgdat_list; pgdat; pgdat = pgdat->pgdat_next) | ||
| 366 | |||
| 367 | /* | ||
| 368 | * next_zone - helper magic for for_each_zone() | ||
| 369 | * Thanks to William Lee Irwin III for this piece of ingenuity. | ||
| 370 | */ | ||
| 371 | static inline struct zone *next_zone(struct zone *zone) | ||
| 372 | { | ||
| 373 | pg_data_t *pgdat = zone->zone_pgdat; | ||
| 374 | |||
| 375 | if (zone < pgdat->node_zones + MAX_NR_ZONES - 1) | ||
| 376 | zone++; | ||
| 377 | else if (pgdat->pgdat_next) { | ||
| 378 | pgdat = pgdat->pgdat_next; | ||
| 379 | zone = pgdat->node_zones; | ||
| 380 | } else | ||
| 381 | zone = NULL; | ||
| 382 | |||
| 383 | return zone; | ||
| 384 | } | ||
| 385 | |||
| 386 | /** | ||
| 387 | * for_each_zone - helper macro to iterate over all memory zones | ||
| 388 | * @zone - pointer to struct zone variable | ||
| 389 | * | ||
| 390 | * The user only needs to declare the zone variable, for_each_zone | ||
| 391 | * fills it in. This basically means for_each_zone() is an | ||
| 392 | * easier to read version of this piece of code: | ||
| 393 | * | ||
| 394 | * for (pgdat = pgdat_list; pgdat; pgdat = pgdat->node_next) | ||
| 395 | * for (i = 0; i < MAX_NR_ZONES; ++i) { | ||
| 396 | * struct zone * z = pgdat->node_zones + i; | ||
| 397 | * ... | ||
| 398 | * } | ||
| 399 | * } | ||
| 400 | */ | ||
| 401 | #define for_each_zone(zone) \ | ||
| 402 | for (zone = pgdat_list->node_zones; zone; zone = next_zone(zone)) | ||
| 403 | |||
| 404 | static inline int populated_zone(struct zone *zone) | 350 | static inline int populated_zone(struct zone *zone) |
| 405 | { | 351 | { |
| 406 | return (!!zone->present_pages); | 352 | return (!!zone->present_pages); |
| @@ -472,6 +418,30 @@ extern struct pglist_data contig_page_data; | |||
| 472 | 418 | ||
| 473 | #endif /* !CONFIG_NEED_MULTIPLE_NODES */ | 419 | #endif /* !CONFIG_NEED_MULTIPLE_NODES */ |
| 474 | 420 | ||
| 421 | extern struct pglist_data *first_online_pgdat(void); | ||
| 422 | extern struct pglist_data *next_online_pgdat(struct pglist_data *pgdat); | ||
| 423 | extern struct zone *next_zone(struct zone *zone); | ||
| 424 | |||
| 425 | /** | ||
| 426 | * for_each_pgdat - helper macro to iterate over all nodes | ||
| 427 | * @pgdat - pointer to a pg_data_t variable | ||
| 428 | */ | ||
| 429 | #define for_each_online_pgdat(pgdat) \ | ||
| 430 | for (pgdat = first_online_pgdat(); \ | ||
| 431 | pgdat; \ | ||
| 432 | pgdat = next_online_pgdat(pgdat)) | ||
| 433 | /** | ||
| 434 | * for_each_zone - helper macro to iterate over all memory zones | ||
| 435 | * @zone - pointer to struct zone variable | ||
| 436 | * | ||
| 437 | * The user only needs to declare the zone variable, for_each_zone | ||
| 438 | * fills it in. | ||
| 439 | */ | ||
| 440 | #define for_each_zone(zone) \ | ||
| 441 | for (zone = (first_online_pgdat())->node_zones; \ | ||
| 442 | zone; \ | ||
| 443 | zone = next_zone(zone)) | ||
| 444 | |||
| 475 | #ifdef CONFIG_SPARSEMEM | 445 | #ifdef CONFIG_SPARSEMEM |
| 476 | #include <asm/sparsemem.h> | 446 | #include <asm/sparsemem.h> |
| 477 | #endif | 447 | #endif |
| @@ -602,17 +572,6 @@ static inline struct mem_section *__pfn_to_section(unsigned long pfn) | |||
| 602 | return __nr_to_section(pfn_to_section_nr(pfn)); | 572 | return __nr_to_section(pfn_to_section_nr(pfn)); |
| 603 | } | 573 | } |
| 604 | 574 | ||
| 605 | #define pfn_to_page(pfn) \ | ||
| 606 | ({ \ | ||
| 607 | unsigned long __pfn = (pfn); \ | ||
| 608 | __section_mem_map_addr(__pfn_to_section(__pfn)) + __pfn; \ | ||
| 609 | }) | ||
| 610 | #define page_to_pfn(page) \ | ||
| 611 | ({ \ | ||
| 612 | page - __section_mem_map_addr(__nr_to_section( \ | ||
| 613 | page_to_section(page))); \ | ||
| 614 | }) | ||
| 615 | |||
| 616 | static inline int pfn_valid(unsigned long pfn) | 575 | static inline int pfn_valid(unsigned long pfn) |
| 617 | { | 576 | { |
| 618 | if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS) | 577 | if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS) |
diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h index f32d75c4f4cf..d54d7b278e96 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack.h +++ b/include/linux/netfilter_ipv4/ip_conntrack.h | |||
| @@ -308,29 +308,30 @@ DECLARE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache); | |||
| 308 | 308 | ||
| 309 | #define CONNTRACK_ECACHE(x) (__get_cpu_var(ip_conntrack_ecache).x) | 309 | #define CONNTRACK_ECACHE(x) (__get_cpu_var(ip_conntrack_ecache).x) |
| 310 | 310 | ||
| 311 | extern struct notifier_block *ip_conntrack_chain; | 311 | extern struct atomic_notifier_head ip_conntrack_chain; |
| 312 | extern struct notifier_block *ip_conntrack_expect_chain; | 312 | extern struct atomic_notifier_head ip_conntrack_expect_chain; |
| 313 | 313 | ||
| 314 | static inline int ip_conntrack_register_notifier(struct notifier_block *nb) | 314 | static inline int ip_conntrack_register_notifier(struct notifier_block *nb) |
| 315 | { | 315 | { |
| 316 | return notifier_chain_register(&ip_conntrack_chain, nb); | 316 | return atomic_notifier_chain_register(&ip_conntrack_chain, nb); |
| 317 | } | 317 | } |
| 318 | 318 | ||
| 319 | static inline int ip_conntrack_unregister_notifier(struct notifier_block *nb) | 319 | static inline int ip_conntrack_unregister_notifier(struct notifier_block *nb) |
| 320 | { | 320 | { |
| 321 | return notifier_chain_unregister(&ip_conntrack_chain, nb); | 321 | return atomic_notifier_chain_unregister(&ip_conntrack_chain, nb); |
| 322 | } | 322 | } |
| 323 | 323 | ||
| 324 | static inline int | 324 | static inline int |
| 325 | ip_conntrack_expect_register_notifier(struct notifier_block *nb) | 325 | ip_conntrack_expect_register_notifier(struct notifier_block *nb) |
| 326 | { | 326 | { |
| 327 | return notifier_chain_register(&ip_conntrack_expect_chain, nb); | 327 | return atomic_notifier_chain_register(&ip_conntrack_expect_chain, nb); |
| 328 | } | 328 | } |
| 329 | 329 | ||
| 330 | static inline int | 330 | static inline int |
| 331 | ip_conntrack_expect_unregister_notifier(struct notifier_block *nb) | 331 | ip_conntrack_expect_unregister_notifier(struct notifier_block *nb) |
| 332 | { | 332 | { |
| 333 | return notifier_chain_unregister(&ip_conntrack_expect_chain, nb); | 333 | return atomic_notifier_chain_unregister(&ip_conntrack_expect_chain, |
| 334 | nb); | ||
| 334 | } | 335 | } |
| 335 | 336 | ||
| 336 | extern void ip_ct_deliver_cached_events(const struct ip_conntrack *ct); | 337 | extern void ip_ct_deliver_cached_events(const struct ip_conntrack *ct); |
| @@ -355,14 +356,14 @@ static inline void ip_conntrack_event(enum ip_conntrack_events event, | |||
| 355 | struct ip_conntrack *ct) | 356 | struct ip_conntrack *ct) |
| 356 | { | 357 | { |
| 357 | if (is_confirmed(ct) && !is_dying(ct)) | 358 | if (is_confirmed(ct) && !is_dying(ct)) |
| 358 | notifier_call_chain(&ip_conntrack_chain, event, ct); | 359 | atomic_notifier_call_chain(&ip_conntrack_chain, event, ct); |
| 359 | } | 360 | } |
| 360 | 361 | ||
| 361 | static inline void | 362 | static inline void |
| 362 | ip_conntrack_expect_event(enum ip_conntrack_expect_events event, | 363 | ip_conntrack_expect_event(enum ip_conntrack_expect_events event, |
| 363 | struct ip_conntrack_expect *exp) | 364 | struct ip_conntrack_expect *exp) |
| 364 | { | 365 | { |
| 365 | notifier_call_chain(&ip_conntrack_expect_chain, event, exp); | 366 | atomic_notifier_call_chain(&ip_conntrack_expect_chain, event, exp); |
| 366 | } | 367 | } |
| 367 | #else /* CONFIG_IP_NF_CONNTRACK_EVENTS */ | 368 | #else /* CONFIG_IP_NF_CONNTRACK_EVENTS */ |
| 368 | static inline void ip_conntrack_event_cache(enum ip_conntrack_events event, | 369 | static inline void ip_conntrack_event_cache(enum ip_conntrack_events event, |
diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 6bad4766d3d9..d2a8abb5011a 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h | |||
| @@ -67,7 +67,8 @@ struct svc_expkey { | |||
| 67 | int ek_fsidtype; | 67 | int ek_fsidtype; |
| 68 | u32 ek_fsid[3]; | 68 | u32 ek_fsid[3]; |
| 69 | 69 | ||
| 70 | struct svc_export * ek_export; | 70 | struct vfsmount * ek_mnt; |
| 71 | struct dentry * ek_dentry; | ||
| 71 | }; | 72 | }; |
| 72 | 73 | ||
| 73 | #define EX_SECURE(exp) (!((exp)->ex_flags & NFSEXP_INSECURE_PORT)) | 74 | #define EX_SECURE(exp) (!((exp)->ex_flags & NFSEXP_INSECURE_PORT)) |
| @@ -85,9 +86,6 @@ void nfsd_export_shutdown(void); | |||
| 85 | void nfsd_export_flush(void); | 86 | void nfsd_export_flush(void); |
| 86 | void exp_readlock(void); | 87 | void exp_readlock(void); |
| 87 | void exp_readunlock(void); | 88 | void exp_readunlock(void); |
| 88 | struct svc_expkey * exp_find_key(struct auth_domain *clp, | ||
| 89 | int fsid_type, u32 *fsidv, | ||
| 90 | struct cache_req *reqp); | ||
| 91 | struct svc_export * exp_get_by_name(struct auth_domain *clp, | 89 | struct svc_export * exp_get_by_name(struct auth_domain *clp, |
| 92 | struct vfsmount *mnt, | 90 | struct vfsmount *mnt, |
| 93 | struct dentry *dentry, | 91 | struct dentry *dentry, |
| @@ -101,35 +99,20 @@ int exp_rootfh(struct auth_domain *, | |||
| 101 | int exp_pseudoroot(struct auth_domain *, struct svc_fh *fhp, struct cache_req *creq); | 99 | int exp_pseudoroot(struct auth_domain *, struct svc_fh *fhp, struct cache_req *creq); |
| 102 | int nfserrno(int errno); | 100 | int nfserrno(int errno); |
| 103 | 101 | ||
| 104 | extern void expkey_put(struct cache_head *item, struct cache_detail *cd); | 102 | extern struct cache_detail svc_export_cache; |
| 105 | extern void svc_export_put(struct cache_head *item, struct cache_detail *cd); | ||
| 106 | extern struct cache_detail svc_export_cache, svc_expkey_cache; | ||
| 107 | 103 | ||
| 108 | static inline void exp_put(struct svc_export *exp) | 104 | static inline void exp_put(struct svc_export *exp) |
| 109 | { | 105 | { |
| 110 | svc_export_put(&exp->h, &svc_export_cache); | 106 | cache_put(&exp->h, &svc_export_cache); |
| 111 | } | 107 | } |
| 112 | 108 | ||
| 113 | static inline void exp_get(struct svc_export *exp) | 109 | static inline void exp_get(struct svc_export *exp) |
| 114 | { | 110 | { |
| 115 | cache_get(&exp->h); | 111 | cache_get(&exp->h); |
| 116 | } | 112 | } |
| 117 | static inline struct svc_export * | 113 | extern struct svc_export * |
| 118 | exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, | 114 | exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, |
| 119 | struct cache_req *reqp) | 115 | struct cache_req *reqp); |
| 120 | { | ||
| 121 | struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); | ||
| 122 | if (ek && !IS_ERR(ek)) { | ||
| 123 | struct svc_export *exp = ek->ek_export; | ||
| 124 | int err; | ||
| 125 | exp_get(exp); | ||
| 126 | expkey_put(&ek->h, &svc_expkey_cache); | ||
| 127 | if ((err = cache_check(&svc_export_cache, &exp->h, reqp))) | ||
| 128 | exp = ERR_PTR(err); | ||
| 129 | return exp; | ||
| 130 | } else | ||
| 131 | return ERR_PTR(PTR_ERR(ek)); | ||
| 132 | } | ||
| 133 | 116 | ||
| 134 | #endif /* __KERNEL__ */ | 117 | #endif /* __KERNEL__ */ |
| 135 | 118 | ||
diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h index b959a4525cbd..1a9ef3e627d1 100644 --- a/include/linux/nodemask.h +++ b/include/linux/nodemask.h | |||
| @@ -350,11 +350,15 @@ extern nodemask_t node_possible_map; | |||
| 350 | #define num_possible_nodes() nodes_weight(node_possible_map) | 350 | #define num_possible_nodes() nodes_weight(node_possible_map) |
| 351 | #define node_online(node) node_isset((node), node_online_map) | 351 | #define node_online(node) node_isset((node), node_online_map) |
| 352 | #define node_possible(node) node_isset((node), node_possible_map) | 352 | #define node_possible(node) node_isset((node), node_possible_map) |
| 353 | #define first_online_node first_node(node_online_map) | ||
| 354 | #define next_online_node(nid) next_node((nid), node_online_map) | ||
| 353 | #else | 355 | #else |
| 354 | #define num_online_nodes() 1 | 356 | #define num_online_nodes() 1 |
| 355 | #define num_possible_nodes() 1 | 357 | #define num_possible_nodes() 1 |
| 356 | #define node_online(node) ((node) == 0) | 358 | #define node_online(node) ((node) == 0) |
| 357 | #define node_possible(node) ((node) == 0) | 359 | #define node_possible(node) ((node) == 0) |
| 360 | #define first_online_node 0 | ||
| 361 | #define next_online_node(nid) (MAX_NUMNODES) | ||
| 358 | #endif | 362 | #endif |
| 359 | 363 | ||
| 360 | #define any_online_node(mask) \ | 364 | #define any_online_node(mask) \ |
diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 5937dd6053c3..51dbab9710c7 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h | |||
| @@ -10,25 +10,107 @@ | |||
| 10 | #ifndef _LINUX_NOTIFIER_H | 10 | #ifndef _LINUX_NOTIFIER_H |
| 11 | #define _LINUX_NOTIFIER_H | 11 | #define _LINUX_NOTIFIER_H |
| 12 | #include <linux/errno.h> | 12 | #include <linux/errno.h> |
| 13 | #include <linux/mutex.h> | ||
| 14 | #include <linux/rwsem.h> | ||
| 13 | 15 | ||
| 14 | struct notifier_block | 16 | /* |
| 15 | { | 17 | * Notifier chains are of three types: |
| 16 | int (*notifier_call)(struct notifier_block *self, unsigned long, void *); | 18 | * |
| 19 | * Atomic notifier chains: Chain callbacks run in interrupt/atomic | ||
| 20 | * context. Callouts are not allowed to block. | ||
| 21 | * Blocking notifier chains: Chain callbacks run in process context. | ||
| 22 | * Callouts are allowed to block. | ||
| 23 | * Raw notifier chains: There are no restrictions on callbacks, | ||
| 24 | * registration, or unregistration. All locking and protection | ||
| 25 | * must be provided by the caller. | ||
| 26 | * | ||
| 27 | * atomic_notifier_chain_register() may be called from an atomic context, | ||
| 28 | * but blocking_notifier_chain_register() must be called from a process | ||
| 29 | * context. Ditto for the corresponding _unregister() routines. | ||
| 30 | * | ||
| 31 | * atomic_notifier_chain_unregister() and blocking_notifier_chain_unregister() | ||
| 32 | * _must not_ be called from within the call chain. | ||
| 33 | */ | ||
| 34 | |||
| 35 | struct notifier_block { | ||
| 36 | int (*notifier_call)(struct notifier_block *, unsigned long, void *); | ||
| 17 | struct notifier_block *next; | 37 | struct notifier_block *next; |
| 18 | int priority; | 38 | int priority; |
| 19 | }; | 39 | }; |
| 20 | 40 | ||
| 41 | struct atomic_notifier_head { | ||
| 42 | spinlock_t lock; | ||
| 43 | struct notifier_block *head; | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct blocking_notifier_head { | ||
| 47 | struct rw_semaphore rwsem; | ||
| 48 | struct notifier_block *head; | ||
| 49 | }; | ||
| 50 | |||
| 51 | struct raw_notifier_head { | ||
| 52 | struct notifier_block *head; | ||
| 53 | }; | ||
| 54 | |||
| 55 | #define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \ | ||
| 56 | spin_lock_init(&(name)->lock); \ | ||
| 57 | (name)->head = NULL; \ | ||
| 58 | } while (0) | ||
| 59 | #define BLOCKING_INIT_NOTIFIER_HEAD(name) do { \ | ||
| 60 | init_rwsem(&(name)->rwsem); \ | ||
| 61 | (name)->head = NULL; \ | ||
| 62 | } while (0) | ||
| 63 | #define RAW_INIT_NOTIFIER_HEAD(name) do { \ | ||
| 64 | (name)->head = NULL; \ | ||
| 65 | } while (0) | ||
| 66 | |||
| 67 | #define ATOMIC_NOTIFIER_INIT(name) { \ | ||
| 68 | .lock = SPIN_LOCK_UNLOCKED, \ | ||
| 69 | .head = NULL } | ||
| 70 | #define BLOCKING_NOTIFIER_INIT(name) { \ | ||
| 71 | .rwsem = __RWSEM_INITIALIZER((name).rwsem), \ | ||
| 72 | .head = NULL } | ||
| 73 | #define RAW_NOTIFIER_INIT(name) { \ | ||
| 74 | .head = NULL } | ||
| 75 | |||
| 76 | #define ATOMIC_NOTIFIER_HEAD(name) \ | ||
| 77 | struct atomic_notifier_head name = \ | ||
| 78 | ATOMIC_NOTIFIER_INIT(name) | ||
| 79 | #define BLOCKING_NOTIFIER_HEAD(name) \ | ||
| 80 | struct blocking_notifier_head name = \ | ||
| 81 | BLOCKING_NOTIFIER_INIT(name) | ||
| 82 | #define RAW_NOTIFIER_HEAD(name) \ | ||
| 83 | struct raw_notifier_head name = \ | ||
| 84 | RAW_NOTIFIER_INIT(name) | ||
| 21 | 85 | ||
| 22 | #ifdef __KERNEL__ | 86 | #ifdef __KERNEL__ |
| 23 | 87 | ||
| 24 | extern int notifier_chain_register(struct notifier_block **list, struct notifier_block *n); | 88 | extern int atomic_notifier_chain_register(struct atomic_notifier_head *, |
| 25 | extern int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n); | 89 | struct notifier_block *); |
| 26 | extern int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v); | 90 | extern int blocking_notifier_chain_register(struct blocking_notifier_head *, |
| 91 | struct notifier_block *); | ||
| 92 | extern int raw_notifier_chain_register(struct raw_notifier_head *, | ||
| 93 | struct notifier_block *); | ||
| 94 | |||
| 95 | extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, | ||
| 96 | struct notifier_block *); | ||
| 97 | extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *, | ||
| 98 | struct notifier_block *); | ||
| 99 | extern int raw_notifier_chain_unregister(struct raw_notifier_head *, | ||
| 100 | struct notifier_block *); | ||
| 101 | |||
| 102 | extern int atomic_notifier_call_chain(struct atomic_notifier_head *, | ||
| 103 | unsigned long val, void *v); | ||
| 104 | extern int blocking_notifier_call_chain(struct blocking_notifier_head *, | ||
| 105 | unsigned long val, void *v); | ||
| 106 | extern int raw_notifier_call_chain(struct raw_notifier_head *, | ||
| 107 | unsigned long val, void *v); | ||
| 27 | 108 | ||
| 28 | #define NOTIFY_DONE 0x0000 /* Don't care */ | 109 | #define NOTIFY_DONE 0x0000 /* Don't care */ |
| 29 | #define NOTIFY_OK 0x0001 /* Suits me */ | 110 | #define NOTIFY_OK 0x0001 /* Suits me */ |
| 30 | #define NOTIFY_STOP_MASK 0x8000 /* Don't call further */ | 111 | #define NOTIFY_STOP_MASK 0x8000 /* Don't call further */ |
| 31 | #define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) /* Bad/Veto action */ | 112 | #define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) |
| 113 | /* Bad/Veto action */ | ||
| 32 | /* | 114 | /* |
| 33 | * Clean way to return from the notifier and stop further calls. | 115 | * Clean way to return from the notifier and stop further calls. |
| 34 | */ | 116 | */ |
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 6f080ae59286..02f6cf20b141 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h | |||
| @@ -1052,6 +1052,7 @@ | |||
| 1052 | #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2 0x00f2 | 1052 | #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2 0x00f2 |
| 1053 | #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1 0x00f3 | 1053 | #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1 0x00f3 |
| 1054 | #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x00f9 | 1054 | #define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x00f9 |
| 1055 | #define PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280 0x00fd | ||
| 1055 | #define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR 0x0100 | 1056 | #define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR 0x0100 |
| 1056 | #define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR 0x0101 | 1057 | #define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR 0x0101 |
| 1057 | #define PCI_DEVICE_ID_NVIDIA_QUADRO 0x0103 | 1058 | #define PCI_DEVICE_ID_NVIDIA_QUADRO 0x0103 |
diff --git a/include/linux/pfn.h b/include/linux/pfn.h new file mode 100644 index 000000000000..bb01f8b92b56 --- /dev/null +++ b/include/linux/pfn.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef _LINUX_PFN_H_ | ||
| 2 | #define _LINUX_PFN_H_ | ||
| 3 | |||
| 4 | #define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) | ||
| 5 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
| 6 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
| 7 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
| 8 | |||
| 9 | #endif | ||
diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index b6e0bcad84e1..66b44e5e0d6e 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h | |||
| @@ -92,7 +92,10 @@ extern void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, | |||
| 92 | extern void md_super_wait(mddev_t *mddev); | 92 | extern void md_super_wait(mddev_t *mddev); |
| 93 | extern int sync_page_io(struct block_device *bdev, sector_t sector, int size, | 93 | extern int sync_page_io(struct block_device *bdev, sector_t sector, int size, |
| 94 | struct page *page, int rw); | 94 | struct page *page, int rw); |
| 95 | extern void md_do_sync(mddev_t *mddev); | ||
| 96 | extern void md_new_event(mddev_t *mddev); | ||
| 95 | 97 | ||
| 98 | extern void md_update_sb(mddev_t * mddev); | ||
| 96 | 99 | ||
| 97 | #define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); } | 100 | #define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); } |
| 98 | 101 | ||
diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 617b9506c760..e2df61f5b09a 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h | |||
| @@ -132,6 +132,14 @@ struct mddev_s | |||
| 132 | 132 | ||
| 133 | char uuid[16]; | 133 | char uuid[16]; |
| 134 | 134 | ||
| 135 | /* If the array is being reshaped, we need to record the | ||
| 136 | * new shape and an indication of where we are up to. | ||
| 137 | * This is written to the superblock. | ||
| 138 | * If reshape_position is MaxSector, then no reshape is happening (yet). | ||
| 139 | */ | ||
| 140 | sector_t reshape_position; | ||
| 141 | int delta_disks, new_level, new_layout, new_chunk; | ||
| 142 | |||
| 135 | struct mdk_thread_s *thread; /* management thread */ | 143 | struct mdk_thread_s *thread; /* management thread */ |
| 136 | struct mdk_thread_s *sync_thread; /* doing resync or reconstruct */ | 144 | struct mdk_thread_s *sync_thread; /* doing resync or reconstruct */ |
| 137 | sector_t curr_resync; /* blocks scheduled */ | 145 | sector_t curr_resync; /* blocks scheduled */ |
| @@ -143,6 +151,10 @@ struct mddev_s | |||
| 143 | sector_t resync_mismatches; /* count of sectors where | 151 | sector_t resync_mismatches; /* count of sectors where |
| 144 | * parity/replica mismatch found | 152 | * parity/replica mismatch found |
| 145 | */ | 153 | */ |
| 154 | |||
| 155 | /* allow user-space to request suspension of IO to regions of the array */ | ||
| 156 | sector_t suspend_lo; | ||
| 157 | sector_t suspend_hi; | ||
| 146 | /* if zero, use the system-wide default */ | 158 | /* if zero, use the system-wide default */ |
| 147 | int sync_speed_min; | 159 | int sync_speed_min; |
| 148 | int sync_speed_max; | 160 | int sync_speed_max; |
| @@ -157,6 +169,9 @@ struct mddev_s | |||
| 157 | * DONE: thread is done and is waiting to be reaped | 169 | * DONE: thread is done and is waiting to be reaped |
| 158 | * REQUEST: user-space has requested a sync (used with SYNC) | 170 | * REQUEST: user-space has requested a sync (used with SYNC) |
| 159 | * CHECK: user-space request for for check-only, no repair | 171 | * CHECK: user-space request for for check-only, no repair |
| 172 | * RESHAPE: A reshape is happening | ||
| 173 | * | ||
| 174 | * If neither SYNC or RESHAPE are set, then it is a recovery. | ||
| 160 | */ | 175 | */ |
| 161 | #define MD_RECOVERY_RUNNING 0 | 176 | #define MD_RECOVERY_RUNNING 0 |
| 162 | #define MD_RECOVERY_SYNC 1 | 177 | #define MD_RECOVERY_SYNC 1 |
| @@ -166,10 +181,11 @@ struct mddev_s | |||
| 166 | #define MD_RECOVERY_NEEDED 5 | 181 | #define MD_RECOVERY_NEEDED 5 |
| 167 | #define MD_RECOVERY_REQUESTED 6 | 182 | #define MD_RECOVERY_REQUESTED 6 |
| 168 | #define MD_RECOVERY_CHECK 7 | 183 | #define MD_RECOVERY_CHECK 7 |
| 184 | #define MD_RECOVERY_RESHAPE 8 | ||
| 169 | unsigned long recovery; | 185 | unsigned long recovery; |
| 170 | 186 | ||
| 171 | int in_sync; /* know to not need resync */ | 187 | int in_sync; /* know to not need resync */ |
| 172 | struct semaphore reconfig_sem; | 188 | struct mutex reconfig_mutex; |
| 173 | atomic_t active; | 189 | atomic_t active; |
| 174 | 190 | ||
| 175 | int changed; /* true if we might need to reread partition info */ | 191 | int changed; /* true if we might need to reread partition info */ |
| @@ -249,7 +265,8 @@ struct mdk_personality | |||
| 249 | int (*spare_active) (mddev_t *mddev); | 265 | int (*spare_active) (mddev_t *mddev); |
| 250 | sector_t (*sync_request)(mddev_t *mddev, sector_t sector_nr, int *skipped, int go_faster); | 266 | sector_t (*sync_request)(mddev_t *mddev, sector_t sector_nr, int *skipped, int go_faster); |
| 251 | int (*resize) (mddev_t *mddev, sector_t sectors); | 267 | int (*resize) (mddev_t *mddev, sector_t sectors); |
| 252 | int (*reshape) (mddev_t *mddev, int raid_disks); | 268 | int (*check_reshape) (mddev_t *mddev); |
| 269 | int (*start_reshape) (mddev_t *mddev); | ||
| 253 | int (*reconfig) (mddev_t *mddev, int layout, int chunk_size); | 270 | int (*reconfig) (mddev_t *mddev, int layout, int chunk_size); |
| 254 | /* quiesce moves between quiescence states | 271 | /* quiesce moves between quiescence states |
| 255 | * 0 - fully active | 272 | * 0 - fully active |
diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h index c100fa5d4bfa..774e1acfb8c4 100644 --- a/include/linux/raid/md_p.h +++ b/include/linux/raid/md_p.h | |||
| @@ -102,6 +102,18 @@ typedef struct mdp_device_descriptor_s { | |||
| 102 | #define MD_SB_ERRORS 1 | 102 | #define MD_SB_ERRORS 1 |
| 103 | 103 | ||
| 104 | #define MD_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ | 104 | #define MD_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ |
| 105 | |||
| 106 | /* | ||
| 107 | * Notes: | ||
| 108 | * - if an array is being reshaped (restriped) in order to change the | ||
| 109 | * the number of active devices in the array, 'raid_disks' will be | ||
| 110 | * the larger of the old and new numbers. 'delta_disks' will | ||
| 111 | * be the "new - old". So if +ve, raid_disks is the new value, and | ||
| 112 | * "raid_disks-delta_disks" is the old. If -ve, raid_disks is the | ||
| 113 | * old value and "raid_disks+delta_disks" is the new (smaller) value. | ||
| 114 | */ | ||
| 115 | |||
| 116 | |||
| 105 | typedef struct mdp_superblock_s { | 117 | typedef struct mdp_superblock_s { |
| 106 | /* | 118 | /* |
| 107 | * Constant generic information | 119 | * Constant generic information |
| @@ -146,7 +158,13 @@ typedef struct mdp_superblock_s { | |||
| 146 | __u32 cp_events_hi; /* 10 high-order of checkpoint update count */ | 158 | __u32 cp_events_hi; /* 10 high-order of checkpoint update count */ |
| 147 | #endif | 159 | #endif |
| 148 | __u32 recovery_cp; /* 11 recovery checkpoint sector count */ | 160 | __u32 recovery_cp; /* 11 recovery checkpoint sector count */ |
| 149 | __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 12]; | 161 | /* There are only valid for minor_version > 90 */ |
| 162 | __u64 reshape_position; /* 12,13 next address in array-space for reshape */ | ||
| 163 | __u32 new_level; /* 14 new level we are reshaping to */ | ||
| 164 | __u32 delta_disks; /* 15 change in number of raid_disks */ | ||
| 165 | __u32 new_layout; /* 16 new layout */ | ||
| 166 | __u32 new_chunk; /* 17 new chunk size (bytes) */ | ||
| 167 | __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 18]; | ||
| 150 | 168 | ||
| 151 | /* | 169 | /* |
| 152 | * Personality information | 170 | * Personality information |
| @@ -207,7 +225,14 @@ struct mdp_superblock_1 { | |||
| 207 | * NOTE: signed, so bitmap can be before superblock | 225 | * NOTE: signed, so bitmap can be before superblock |
| 208 | * only meaningful of feature_map[0] is set. | 226 | * only meaningful of feature_map[0] is set. |
| 209 | */ | 227 | */ |
| 210 | __u8 pad1[128-100]; /* set to 0 when written */ | 228 | |
| 229 | /* These are only valid with feature bit '4' */ | ||
| 230 | __u64 reshape_position; /* next address in array-space for reshape */ | ||
| 231 | __u32 new_level; /* new level we are reshaping to */ | ||
| 232 | __u32 delta_disks; /* change in number of raid_disks */ | ||
| 233 | __u32 new_layout; /* new layout */ | ||
| 234 | __u32 new_chunk; /* new chunk size (bytes) */ | ||
| 235 | __u8 pad1[128-124]; /* set to 0 when written */ | ||
| 211 | 236 | ||
| 212 | /* constant this-device information - 64 bytes */ | 237 | /* constant this-device information - 64 bytes */ |
| 213 | __u64 data_offset; /* sector start of data, often 0 */ | 238 | __u64 data_offset; /* sector start of data, often 0 */ |
| @@ -240,8 +265,9 @@ struct mdp_superblock_1 { | |||
| 240 | 265 | ||
| 241 | /* feature_map bits */ | 266 | /* feature_map bits */ |
| 242 | #define MD_FEATURE_BITMAP_OFFSET 1 | 267 | #define MD_FEATURE_BITMAP_OFFSET 1 |
| 268 | #define MD_FEATURE_RESHAPE_ACTIVE 4 | ||
| 243 | 269 | ||
| 244 | #define MD_FEATURE_ALL 1 | 270 | #define MD_FEATURE_ALL 5 |
| 245 | 271 | ||
| 246 | #endif | 272 | #endif |
| 247 | 273 | ||
diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 394da8207b34..914af667044f 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h | |||
| @@ -135,6 +135,7 @@ struct stripe_head { | |||
| 135 | atomic_t count; /* nr of active thread/requests */ | 135 | atomic_t count; /* nr of active thread/requests */ |
| 136 | spinlock_t lock; | 136 | spinlock_t lock; |
| 137 | int bm_seq; /* sequence number for bitmap flushes */ | 137 | int bm_seq; /* sequence number for bitmap flushes */ |
| 138 | int disks; /* disks in stripe */ | ||
| 138 | struct r5dev { | 139 | struct r5dev { |
| 139 | struct bio req; | 140 | struct bio req; |
| 140 | struct bio_vec vec; | 141 | struct bio_vec vec; |
| @@ -156,6 +157,7 @@ struct stripe_head { | |||
| 156 | #define R5_ReadError 8 /* seen a read error here recently */ | 157 | #define R5_ReadError 8 /* seen a read error here recently */ |
| 157 | #define R5_ReWrite 9 /* have tried to over-write the readerror */ | 158 | #define R5_ReWrite 9 /* have tried to over-write the readerror */ |
| 158 | 159 | ||
| 160 | #define R5_Expanded 10 /* This block now has post-expand data */ | ||
| 159 | /* | 161 | /* |
| 160 | * Write method | 162 | * Write method |
| 161 | */ | 163 | */ |
| @@ -174,7 +176,9 @@ struct stripe_head { | |||
| 174 | #define STRIPE_DELAYED 6 | 176 | #define STRIPE_DELAYED 6 |
| 175 | #define STRIPE_DEGRADED 7 | 177 | #define STRIPE_DEGRADED 7 |
| 176 | #define STRIPE_BIT_DELAY 8 | 178 | #define STRIPE_BIT_DELAY 8 |
| 177 | 179 | #define STRIPE_EXPANDING 9 | |
| 180 | #define STRIPE_EXPAND_SOURCE 10 | ||
| 181 | #define STRIPE_EXPAND_READY 11 | ||
| 178 | /* | 182 | /* |
| 179 | * Plugging: | 183 | * Plugging: |
| 180 | * | 184 | * |
| @@ -211,12 +215,24 @@ struct raid5_private_data { | |||
| 211 | int raid_disks, working_disks, failed_disks; | 215 | int raid_disks, working_disks, failed_disks; |
| 212 | int max_nr_stripes; | 216 | int max_nr_stripes; |
| 213 | 217 | ||
| 218 | /* used during an expand */ | ||
| 219 | sector_t expand_progress; /* MaxSector when no expand happening */ | ||
| 220 | sector_t expand_lo; /* from here up to expand_progress it out-of-bounds | ||
| 221 | * as we haven't flushed the metadata yet | ||
| 222 | */ | ||
| 223 | int previous_raid_disks; | ||
| 224 | |||
| 214 | struct list_head handle_list; /* stripes needing handling */ | 225 | struct list_head handle_list; /* stripes needing handling */ |
| 215 | struct list_head delayed_list; /* stripes that have plugged requests */ | 226 | struct list_head delayed_list; /* stripes that have plugged requests */ |
| 216 | struct list_head bitmap_list; /* stripes delaying awaiting bitmap update */ | 227 | struct list_head bitmap_list; /* stripes delaying awaiting bitmap update */ |
| 217 | atomic_t preread_active_stripes; /* stripes with scheduled io */ | 228 | atomic_t preread_active_stripes; /* stripes with scheduled io */ |
| 218 | 229 | ||
| 219 | char cache_name[20]; | 230 | atomic_t reshape_stripes; /* stripes with pending writes for reshape */ |
| 231 | /* unfortunately we need two cache names as we temporarily have | ||
| 232 | * two caches. | ||
| 233 | */ | ||
| 234 | int active_name; | ||
| 235 | char cache_name[2][20]; | ||
| 220 | kmem_cache_t *slab_cache; /* for allocating stripes */ | 236 | kmem_cache_t *slab_cache; /* for allocating stripes */ |
| 221 | 237 | ||
| 222 | int seq_flush, seq_write; | 238 | int seq_flush, seq_write; |
| @@ -238,9 +254,10 @@ struct raid5_private_data { | |||
| 238 | wait_queue_head_t wait_for_overlap; | 254 | wait_queue_head_t wait_for_overlap; |
| 239 | int inactive_blocked; /* release of inactive stripes blocked, | 255 | int inactive_blocked; /* release of inactive stripes blocked, |
| 240 | * waiting for 25% to be free | 256 | * waiting for 25% to be free |
| 241 | */ | 257 | */ |
| 258 | int pool_size; /* number of disks in stripeheads in pool */ | ||
| 242 | spinlock_t device_lock; | 259 | spinlock_t device_lock; |
| 243 | struct disk_info disks[0]; | 260 | struct disk_info *disks; |
| 244 | }; | 261 | }; |
| 245 | 262 | ||
| 246 | typedef struct raid5_private_data raid5_conf_t; | 263 | typedef struct raid5_private_data raid5_conf_t; |
diff --git a/include/linux/rtc.h b/include/linux/rtc.h index b739ac1f7ca0..ab61cd1199f2 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h | |||
| @@ -91,10 +91,102 @@ struct rtc_pll_info { | |||
| 91 | #define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */ | 91 | #define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */ |
| 92 | #define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */ | 92 | #define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */ |
| 93 | 93 | ||
| 94 | /* interrupt flags */ | ||
| 95 | #define RTC_IRQF 0x80 /* any of the following is active */ | ||
| 96 | #define RTC_PF 0x40 | ||
| 97 | #define RTC_AF 0x20 | ||
| 98 | #define RTC_UF 0x10 | ||
| 99 | |||
| 94 | #ifdef __KERNEL__ | 100 | #ifdef __KERNEL__ |
| 95 | 101 | ||
| 96 | #include <linux/interrupt.h> | 102 | #include <linux/interrupt.h> |
| 97 | 103 | ||
| 104 | extern int rtc_month_days(unsigned int month, unsigned int year); | ||
| 105 | extern int rtc_valid_tm(struct rtc_time *tm); | ||
| 106 | extern int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time); | ||
| 107 | extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm); | ||
| 108 | |||
| 109 | #include <linux/device.h> | ||
| 110 | #include <linux/seq_file.h> | ||
| 111 | #include <linux/cdev.h> | ||
| 112 | #include <linux/poll.h> | ||
| 113 | #include <linux/mutex.h> | ||
| 114 | |||
| 115 | extern struct class *rtc_class; | ||
| 116 | |||
| 117 | struct rtc_class_ops { | ||
| 118 | int (*open)(struct device *); | ||
| 119 | void (*release)(struct device *); | ||
| 120 | int (*ioctl)(struct device *, unsigned int, unsigned long); | ||
| 121 | int (*read_time)(struct device *, struct rtc_time *); | ||
| 122 | int (*set_time)(struct device *, struct rtc_time *); | ||
| 123 | int (*read_alarm)(struct device *, struct rtc_wkalrm *); | ||
| 124 | int (*set_alarm)(struct device *, struct rtc_wkalrm *); | ||
| 125 | int (*proc)(struct device *, struct seq_file *); | ||
| 126 | int (*set_mmss)(struct device *, unsigned long secs); | ||
| 127 | int (*irq_set_state)(struct device *, int enabled); | ||
| 128 | int (*irq_set_freq)(struct device *, int freq); | ||
| 129 | int (*read_callback)(struct device *, int data); | ||
| 130 | }; | ||
| 131 | |||
| 132 | #define RTC_DEVICE_NAME_SIZE 20 | ||
| 133 | struct rtc_task; | ||
| 134 | |||
| 135 | struct rtc_device | ||
| 136 | { | ||
| 137 | struct class_device class_dev; | ||
| 138 | struct module *owner; | ||
| 139 | |||
| 140 | int id; | ||
| 141 | char name[RTC_DEVICE_NAME_SIZE]; | ||
| 142 | |||
| 143 | struct rtc_class_ops *ops; | ||
| 144 | struct mutex ops_lock; | ||
| 145 | |||
| 146 | struct class_device *rtc_dev; | ||
| 147 | struct cdev char_dev; | ||
| 148 | struct mutex char_lock; | ||
| 149 | |||
| 150 | unsigned long irq_data; | ||
| 151 | spinlock_t irq_lock; | ||
| 152 | wait_queue_head_t irq_queue; | ||
| 153 | struct fasync_struct *async_queue; | ||
| 154 | |||
| 155 | struct rtc_task *irq_task; | ||
| 156 | spinlock_t irq_task_lock; | ||
| 157 | int irq_freq; | ||
| 158 | }; | ||
| 159 | #define to_rtc_device(d) container_of(d, struct rtc_device, class_dev) | ||
| 160 | |||
| 161 | extern struct rtc_device *rtc_device_register(const char *name, | ||
| 162 | struct device *dev, | ||
| 163 | struct rtc_class_ops *ops, | ||
| 164 | struct module *owner); | ||
| 165 | extern void rtc_device_unregister(struct rtc_device *rdev); | ||
| 166 | extern int rtc_interface_register(struct class_interface *intf); | ||
| 167 | |||
| 168 | extern int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm); | ||
| 169 | extern int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm); | ||
| 170 | extern int rtc_set_mmss(struct class_device *class_dev, unsigned long secs); | ||
| 171 | extern int rtc_read_alarm(struct class_device *class_dev, | ||
| 172 | struct rtc_wkalrm *alrm); | ||
| 173 | extern int rtc_set_alarm(struct class_device *class_dev, | ||
| 174 | struct rtc_wkalrm *alrm); | ||
| 175 | extern void rtc_update_irq(struct class_device *class_dev, | ||
| 176 | unsigned long num, unsigned long events); | ||
| 177 | |||
| 178 | extern struct class_device *rtc_class_open(char *name); | ||
| 179 | extern void rtc_class_close(struct class_device *class_dev); | ||
| 180 | |||
| 181 | extern int rtc_irq_register(struct class_device *class_dev, | ||
| 182 | struct rtc_task *task); | ||
| 183 | extern void rtc_irq_unregister(struct class_device *class_dev, | ||
| 184 | struct rtc_task *task); | ||
| 185 | extern int rtc_irq_set_state(struct class_device *class_dev, | ||
| 186 | struct rtc_task *task, int enabled); | ||
| 187 | extern int rtc_irq_set_freq(struct class_device *class_dev, | ||
| 188 | struct rtc_task *task, int freq); | ||
| 189 | |||
| 98 | typedef struct rtc_task { | 190 | typedef struct rtc_task { |
| 99 | void (*func)(void *private_data); | 191 | void (*func)(void *private_data); |
| 100 | void *private_data; | 192 | void *private_data; |
diff --git a/include/linux/sched.h b/include/linux/sched.h index 036d14d2bf90..20b4f0372e44 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include <linux/topology.h> | 35 | #include <linux/topology.h> |
| 36 | #include <linux/seccomp.h> | 36 | #include <linux/seccomp.h> |
| 37 | #include <linux/rcupdate.h> | 37 | #include <linux/rcupdate.h> |
| 38 | #include <linux/futex.h> | ||
| 38 | 39 | ||
| 39 | #include <linux/auxvec.h> /* For AT_VECTOR_SIZE */ | 40 | #include <linux/auxvec.h> /* For AT_VECTOR_SIZE */ |
| 40 | 41 | ||
| @@ -872,6 +873,11 @@ struct task_struct { | |||
| 872 | int cpuset_mems_generation; | 873 | int cpuset_mems_generation; |
| 873 | int cpuset_mem_spread_rotor; | 874 | int cpuset_mem_spread_rotor; |
| 874 | #endif | 875 | #endif |
| 876 | struct robust_list_head __user *robust_list; | ||
| 877 | #ifdef CONFIG_COMPAT | ||
| 878 | struct compat_robust_list_head __user *compat_robust_list; | ||
| 879 | #endif | ||
| 880 | |||
| 875 | atomic_t fs_excl; /* holding fs exclusive resources */ | 881 | atomic_t fs_excl; /* holding fs exclusive resources */ |
| 876 | struct rcu_head rcu; | 882 | struct rcu_head rcu; |
| 877 | }; | 883 | }; |
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index c4e3ea7cf154..b5612c958cce 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h | |||
| @@ -50,7 +50,7 @@ struct cache_head { | |||
| 50 | time_t last_refresh; /* If CACHE_PENDING, this is when upcall | 50 | time_t last_refresh; /* If CACHE_PENDING, this is when upcall |
| 51 | * was sent, else this is when update was received | 51 | * was sent, else this is when update was received |
| 52 | */ | 52 | */ |
| 53 | atomic_t refcnt; | 53 | struct kref ref; |
| 54 | unsigned long flags; | 54 | unsigned long flags; |
| 55 | }; | 55 | }; |
| 56 | #define CACHE_VALID 0 /* Entry contains valid data */ | 56 | #define CACHE_VALID 0 /* Entry contains valid data */ |
| @@ -68,8 +68,7 @@ struct cache_detail { | |||
| 68 | atomic_t inuse; /* active user-space update or lookup */ | 68 | atomic_t inuse; /* active user-space update or lookup */ |
| 69 | 69 | ||
| 70 | char *name; | 70 | char *name; |
| 71 | void (*cache_put)(struct cache_head *, | 71 | void (*cache_put)(struct kref *); |
| 72 | struct cache_detail*); | ||
| 73 | 72 | ||
| 74 | void (*cache_request)(struct cache_detail *cd, | 73 | void (*cache_request)(struct cache_detail *cd, |
| 75 | struct cache_head *h, | 74 | struct cache_head *h, |
| @@ -81,6 +80,11 @@ struct cache_detail { | |||
| 81 | struct cache_detail *cd, | 80 | struct cache_detail *cd, |
| 82 | struct cache_head *h); | 81 | struct cache_head *h); |
| 83 | 82 | ||
| 83 | struct cache_head * (*alloc)(void); | ||
| 84 | int (*match)(struct cache_head *orig, struct cache_head *new); | ||
| 85 | void (*init)(struct cache_head *orig, struct cache_head *new); | ||
| 86 | void (*update)(struct cache_head *orig, struct cache_head *new); | ||
| 87 | |||
| 84 | /* fields below this comment are for internal use | 88 | /* fields below this comment are for internal use |
| 85 | * and should not be touched by cache owners | 89 | * and should not be touched by cache owners |
| 86 | */ | 90 | */ |
| @@ -123,126 +127,14 @@ struct cache_deferred_req { | |||
| 123 | int too_many); | 127 | int too_many); |
| 124 | }; | 128 | }; |
| 125 | 129 | ||
| 126 | /* | ||
| 127 | * just like a template in C++, this macro does cache lookup | ||
| 128 | * for us. | ||
| 129 | * The function is passed some sort of HANDLE from which a cache_detail | ||
| 130 | * structure can be determined (via SETUP, DETAIL), a template | ||
| 131 | * cache entry (type RTN*), and a "set" flag. Using the HASHFN and the | ||
| 132 | * TEST, the function will try to find a matching cache entry in the cache. | ||
| 133 | * If "set" == 0 : | ||
| 134 | * If an entry is found, it is returned | ||
| 135 | * If no entry is found, a new non-VALID entry is created. | ||
| 136 | * If "set" == 1 and INPLACE == 0 : | ||
| 137 | * If no entry is found a new one is inserted with data from "template" | ||
| 138 | * If a non-CACHE_VALID entry is found, it is updated from template using UPDATE | ||
| 139 | * If a CACHE_VALID entry is found, a new entry is swapped in with data | ||
| 140 | * from "template" | ||
| 141 | * If set == 1, and INPLACE == 1 : | ||
| 142 | * As above, except that if a CACHE_VALID entry is found, we UPDATE in place | ||
| 143 | * instead of swapping in a new entry. | ||
| 144 | * | ||
| 145 | * If the passed handle has the CACHE_NEGATIVE flag set, then UPDATE is not | ||
| 146 | * run but insteead CACHE_NEGATIVE is set in any new item. | ||
| 147 | 130 | ||
| 148 | * In any case, the new entry is returned with a reference count. | 131 | extern struct cache_head * |
| 149 | * | 132 | sunrpc_cache_lookup(struct cache_detail *detail, |
| 150 | * | 133 | struct cache_head *key, int hash); |
| 151 | * RTN is a struct type for a cache entry | 134 | extern struct cache_head * |
| 152 | * MEMBER is the member of the cache which is cache_head, which must be first | 135 | sunrpc_cache_update(struct cache_detail *detail, |
| 153 | * FNAME is the name for the function | 136 | struct cache_head *new, struct cache_head *old, int hash); |
| 154 | * ARGS are arguments to function and must contain RTN *item, int set. May | ||
| 155 | * also contain something to be usedby SETUP or DETAIL to find cache_detail. | ||
| 156 | * SETUP locates the cache detail and makes it available as... | ||
| 157 | * DETAIL identifies the cache detail, possibly set up by SETUP | ||
| 158 | * HASHFN returns a hash value of the cache entry "item" | ||
| 159 | * TEST tests if "tmp" matches "item" | ||
| 160 | * INIT copies key information from "item" to "new" | ||
| 161 | * UPDATE copies content information from "item" to "tmp" | ||
| 162 | * INPLACE is true if updates can happen inplace rather than allocating a new structure | ||
| 163 | * | ||
| 164 | * WARNING: any substantial changes to this must be reflected in | ||
| 165 | * net/sunrpc/svcauth.c(auth_domain_lookup) | ||
| 166 | * which is a similar routine that is open-coded. | ||
| 167 | */ | ||
| 168 | #define DefineCacheLookup(RTN,MEMBER,FNAME,ARGS,SETUP,DETAIL,HASHFN,TEST,INIT,UPDATE,INPLACE) \ | ||
| 169 | RTN *FNAME ARGS \ | ||
| 170 | { \ | ||
| 171 | RTN *tmp, *new=NULL; \ | ||
| 172 | struct cache_head **hp, **head; \ | ||
| 173 | SETUP; \ | ||
| 174 | head = &(DETAIL)->hash_table[HASHFN]; \ | ||
| 175 | retry: \ | ||
| 176 | if (set||new) write_lock(&(DETAIL)->hash_lock); \ | ||
| 177 | else read_lock(&(DETAIL)->hash_lock); \ | ||
| 178 | for(hp=head; *hp != NULL; hp = &tmp->MEMBER.next) { \ | ||
| 179 | tmp = container_of(*hp, RTN, MEMBER); \ | ||
| 180 | if (TEST) { /* found a match */ \ | ||
| 181 | \ | ||
| 182 | if (set && !INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags) && !new) \ | ||
| 183 | break; \ | ||
| 184 | \ | ||
| 185 | if (new) \ | ||
| 186 | {INIT;} \ | ||
| 187 | if (set) { \ | ||
| 188 | if (!INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags))\ | ||
| 189 | { /* need to swap in new */ \ | ||
| 190 | RTN *t2; \ | ||
| 191 | \ | ||
| 192 | new->MEMBER.next = tmp->MEMBER.next; \ | ||
| 193 | *hp = &new->MEMBER; \ | ||
| 194 | tmp->MEMBER.next = NULL; \ | ||
| 195 | t2 = tmp; tmp = new; new = t2; \ | ||
| 196 | } \ | ||
| 197 | if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags)) \ | ||
| 198 | set_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \ | ||
| 199 | else { \ | ||
| 200 | UPDATE; \ | ||
| 201 | clear_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \ | ||
| 202 | } \ | ||
| 203 | } \ | ||
| 204 | cache_get(&tmp->MEMBER); \ | ||
| 205 | if (set||new) write_unlock(&(DETAIL)->hash_lock); \ | ||
| 206 | else read_unlock(&(DETAIL)->hash_lock); \ | ||
| 207 | if (set) \ | ||
| 208 | cache_fresh(DETAIL, &tmp->MEMBER, item->MEMBER.expiry_time); \ | ||
| 209 | if (set && !INPLACE && new) cache_fresh(DETAIL, &new->MEMBER, 0); \ | ||
| 210 | if (new) (DETAIL)->cache_put(&new->MEMBER, DETAIL); \ | ||
| 211 | return tmp; \ | ||
| 212 | } \ | ||
| 213 | } \ | ||
| 214 | /* Didn't find anything */ \ | ||
| 215 | if (new) { \ | ||
| 216 | INIT; \ | ||
| 217 | new->MEMBER.next = *head; \ | ||
| 218 | *head = &new->MEMBER; \ | ||
| 219 | (DETAIL)->entries ++; \ | ||
| 220 | cache_get(&new->MEMBER); \ | ||
| 221 | if (set) { \ | ||
| 222 | tmp = new; \ | ||
| 223 | if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags)) \ | ||
| 224 | set_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \ | ||
| 225 | else {UPDATE;} \ | ||
| 226 | } \ | ||
| 227 | } \ | ||
| 228 | if (set||new) write_unlock(&(DETAIL)->hash_lock); \ | ||
| 229 | else read_unlock(&(DETAIL)->hash_lock); \ | ||
| 230 | if (new && set) \ | ||
| 231 | cache_fresh(DETAIL, &new->MEMBER, item->MEMBER.expiry_time); \ | ||
| 232 | if (new) \ | ||
| 233 | return new; \ | ||
| 234 | new = kmalloc(sizeof(*new), GFP_KERNEL); \ | ||
| 235 | if (new) { \ | ||
| 236 | cache_init(&new->MEMBER); \ | ||
| 237 | goto retry; \ | ||
| 238 | } \ | ||
| 239 | return NULL; \ | ||
| 240 | } | ||
| 241 | 137 | ||
| 242 | #define DefineSimpleCacheLookup(STRUCT,INPLACE) \ | ||
| 243 | DefineCacheLookup(struct STRUCT, h, STRUCT##_lookup, (struct STRUCT *item, int set), /*no setup */, \ | ||
| 244 | & STRUCT##_cache, STRUCT##_hash(item), STRUCT##_match(item, tmp),\ | ||
| 245 | STRUCT##_init(new, item), STRUCT##_update(tmp, item),INPLACE) | ||
| 246 | 138 | ||
| 247 | #define cache_for_each(pos, detail, index, member) \ | 139 | #define cache_for_each(pos, detail, index, member) \ |
| 248 | for (({read_lock(&(detail)->hash_lock); index = (detail)->hash_size;}) ; \ | 140 | for (({read_lock(&(detail)->hash_lock); index = (detail)->hash_size;}) ; \ |
| @@ -258,22 +150,19 @@ extern void cache_clean_deferred(void *owner); | |||
| 258 | 150 | ||
| 259 | static inline struct cache_head *cache_get(struct cache_head *h) | 151 | static inline struct cache_head *cache_get(struct cache_head *h) |
| 260 | { | 152 | { |
| 261 | atomic_inc(&h->refcnt); | 153 | kref_get(&h->ref); |
| 262 | return h; | 154 | return h; |
| 263 | } | 155 | } |
| 264 | 156 | ||
| 265 | 157 | ||
| 266 | static inline int cache_put(struct cache_head *h, struct cache_detail *cd) | 158 | static inline void cache_put(struct cache_head *h, struct cache_detail *cd) |
| 267 | { | 159 | { |
| 268 | if (atomic_read(&h->refcnt) <= 2 && | 160 | if (atomic_read(&h->ref.refcount) <= 2 && |
| 269 | h->expiry_time < cd->nextcheck) | 161 | h->expiry_time < cd->nextcheck) |
| 270 | cd->nextcheck = h->expiry_time; | 162 | cd->nextcheck = h->expiry_time; |
| 271 | return atomic_dec_and_test(&h->refcnt); | 163 | kref_put(&h->ref, cd->cache_put); |
| 272 | } | 164 | } |
| 273 | 165 | ||
| 274 | extern void cache_init(struct cache_head *h); | ||
| 275 | extern void cache_fresh(struct cache_detail *detail, | ||
| 276 | struct cache_head *head, time_t expiry); | ||
| 277 | extern int cache_check(struct cache_detail *detail, | 166 | extern int cache_check(struct cache_detail *detail, |
| 278 | struct cache_head *h, struct cache_req *rqstp); | 167 | struct cache_head *h, struct cache_req *rqstp); |
| 279 | extern void cache_flush(void); | 168 | extern void cache_flush(void); |
diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index c119ce7cbd22..2fe2087edd66 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h | |||
| @@ -45,9 +45,10 @@ struct svc_rqst; /* forward decl */ | |||
| 45 | * of ip addresses to the given client. | 45 | * of ip addresses to the given client. |
| 46 | */ | 46 | */ |
| 47 | struct auth_domain { | 47 | struct auth_domain { |
| 48 | struct cache_head h; | 48 | struct kref ref; |
| 49 | struct hlist_node hash; | ||
| 49 | char *name; | 50 | char *name; |
| 50 | int flavour; | 51 | struct auth_ops *flavour; |
| 51 | }; | 52 | }; |
| 52 | 53 | ||
| 53 | /* | 54 | /* |
| @@ -86,6 +87,9 @@ struct auth_domain { | |||
| 86 | * | 87 | * |
| 87 | * domain_release() | 88 | * domain_release() |
| 88 | * This call releases a domain. | 89 | * This call releases a domain. |
| 90 | * set_client() | ||
| 91 | * Givens a pending request (struct svc_rqst), finds and assigns | ||
| 92 | * an appropriate 'auth_domain' as the client. | ||
| 89 | */ | 93 | */ |
| 90 | struct auth_ops { | 94 | struct auth_ops { |
| 91 | char * name; | 95 | char * name; |
| @@ -117,7 +121,7 @@ extern void svc_auth_unregister(rpc_authflavor_t flavor); | |||
| 117 | extern struct auth_domain *unix_domain_find(char *name); | 121 | extern struct auth_domain *unix_domain_find(char *name); |
| 118 | extern void auth_domain_put(struct auth_domain *item); | 122 | extern void auth_domain_put(struct auth_domain *item); |
| 119 | extern int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom); | 123 | extern int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom); |
| 120 | extern struct auth_domain *auth_domain_lookup(struct auth_domain *item, int set); | 124 | extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *new); |
| 121 | extern struct auth_domain *auth_domain_find(char *name); | 125 | extern struct auth_domain *auth_domain_find(char *name); |
| 122 | extern struct auth_domain *auth_unix_lookup(struct in_addr addr); | 126 | extern struct auth_domain *auth_unix_lookup(struct in_addr addr); |
| 123 | extern int auth_unix_forget_old(struct auth_domain *dom); | 127 | extern int auth_unix_forget_old(struct auth_domain *dom); |
| @@ -160,8 +164,6 @@ static inline unsigned long hash_mem(char *buf, int length, int bits) | |||
| 160 | return hash >> (BITS_PER_LONG - bits); | 164 | return hash >> (BITS_PER_LONG - bits); |
| 161 | } | 165 | } |
| 162 | 166 | ||
| 163 | extern struct cache_detail auth_domain_cache, ip_map_cache; | ||
| 164 | |||
| 165 | #endif /* __KERNEL__ */ | 167 | #endif /* __KERNEL__ */ |
| 166 | 168 | ||
| 167 | #endif /* _LINUX_SUNRPC_SVCAUTH_H_ */ | 169 | #endif /* _LINUX_SUNRPC_SVCAUTH_H_ */ |
diff --git a/include/linux/threads.h b/include/linux/threads.h index b59738ac6197..e646bcdf2614 100644 --- a/include/linux/threads.h +++ b/include/linux/threads.h | |||
| @@ -28,7 +28,8 @@ | |||
| 28 | #define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000) | 28 | #define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000) |
| 29 | 29 | ||
| 30 | /* | 30 | /* |
| 31 | * A maximum of 4 million PIDs should be enough for a while: | 31 | * A maximum of 4 million PIDs should be enough for a while. |
| 32 | * [NOTE: PID/TIDs are limited to 2^29 ~= 500+ million, see futex.h.] | ||
| 32 | */ | 33 | */ |
| 33 | #define PID_MAX_LIMIT (CONFIG_BASE_SMALL ? PAGE_SIZE * 8 : \ | 34 | #define PID_MAX_LIMIT (CONFIG_BASE_SMALL ? PAGE_SIZE * 8 : \ |
| 34 | (sizeof(long) > 4 ? 4 * 1024 * 1024 : PID_MAX_DEFAULT)) | 35 | (sizeof(long) > 4 ? 4 * 1024 * 1024 : PID_MAX_DEFAULT)) |
diff --git a/include/linux/topology.h b/include/linux/topology.h index e8eb0040ce3a..a305ae2e44b6 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h | |||
| @@ -164,6 +164,15 @@ | |||
| 164 | .nr_balance_failed = 0, \ | 164 | .nr_balance_failed = 0, \ |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | #ifdef CONFIG_SCHED_MC | ||
| 168 | #ifndef SD_MC_INIT | ||
| 169 | /* for now its same as SD_CPU_INIT. | ||
| 170 | * TBD: Tune Domain parameters! | ||
| 171 | */ | ||
| 172 | #define SD_MC_INIT SD_CPU_INIT | ||
| 173 | #endif | ||
| 174 | #endif | ||
| 175 | |||
| 167 | #ifdef CONFIG_NUMA | 176 | #ifdef CONFIG_NUMA |
| 168 | #ifndef SD_NODE_INIT | 177 | #ifndef SD_NODE_INIT |
| 169 | #error Please define an appropriate SD_NODE_INIT in include/asm/topology.h!!! | 178 | #error Please define an appropriate SD_NODE_INIT in include/asm/topology.h!!! |
diff --git a/include/linux/x1205.h b/include/linux/x1205.h deleted file mode 100644 index 64fd3af894a5..000000000000 --- a/include/linux/x1205.h +++ /dev/null | |||
| @@ -1,31 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * x1205.h - defines for drivers/i2c/chips/x1205.c | ||
| 3 | * Copyright 2004 Karen Spearel | ||
| 4 | * Copyright 2005 Alessandro Zummo | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef __LINUX_X1205_H__ | ||
| 13 | #define __LINUX_X1205_H__ | ||
| 14 | |||
| 15 | /* commands */ | ||
| 16 | |||
| 17 | #define X1205_CMD_GETDATETIME 0 | ||
| 18 | #define X1205_CMD_SETTIME 1 | ||
| 19 | #define X1205_CMD_SETDATETIME 2 | ||
| 20 | #define X1205_CMD_GETALARM 3 | ||
| 21 | #define X1205_CMD_SETALARM 4 | ||
| 22 | #define X1205_CMD_GETDTRIM 5 | ||
| 23 | #define X1205_CMD_SETDTRIM 6 | ||
| 24 | #define X1205_CMD_GETATRIM 7 | ||
| 25 | #define X1205_CMD_SETATRIM 8 | ||
| 26 | |||
| 27 | extern int x1205_do_command(unsigned int cmd, void *arg); | ||
| 28 | extern int x1205_direct_attach(int adapter_id, | ||
| 29 | struct i2c_client_address_data *address_data); | ||
| 30 | |||
| 31 | #endif /* __LINUX_X1205_H__ */ | ||
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index b6f0905a4ee2..916013ca4a5c 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h | |||
| @@ -300,29 +300,30 @@ DECLARE_PER_CPU(struct nf_conntrack_ecache, nf_conntrack_ecache); | |||
| 300 | 300 | ||
| 301 | #define CONNTRACK_ECACHE(x) (__get_cpu_var(nf_conntrack_ecache).x) | 301 | #define CONNTRACK_ECACHE(x) (__get_cpu_var(nf_conntrack_ecache).x) |
| 302 | 302 | ||
| 303 | extern struct notifier_block *nf_conntrack_chain; | 303 | extern struct atomic_notifier_head nf_conntrack_chain; |
| 304 | extern struct notifier_block *nf_conntrack_expect_chain; | 304 | extern struct atomic_notifier_head nf_conntrack_expect_chain; |
| 305 | 305 | ||
| 306 | static inline int nf_conntrack_register_notifier(struct notifier_block *nb) | 306 | static inline int nf_conntrack_register_notifier(struct notifier_block *nb) |
| 307 | { | 307 | { |
| 308 | return notifier_chain_register(&nf_conntrack_chain, nb); | 308 | return atomic_notifier_chain_register(&nf_conntrack_chain, nb); |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | static inline int nf_conntrack_unregister_notifier(struct notifier_block *nb) | 311 | static inline int nf_conntrack_unregister_notifier(struct notifier_block *nb) |
| 312 | { | 312 | { |
| 313 | return notifier_chain_unregister(&nf_conntrack_chain, nb); | 313 | return atomic_notifier_chain_unregister(&nf_conntrack_chain, nb); |
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | static inline int | 316 | static inline int |
| 317 | nf_conntrack_expect_register_notifier(struct notifier_block *nb) | 317 | nf_conntrack_expect_register_notifier(struct notifier_block *nb) |
| 318 | { | 318 | { |
| 319 | return notifier_chain_register(&nf_conntrack_expect_chain, nb); | 319 | return atomic_notifier_chain_register(&nf_conntrack_expect_chain, nb); |
| 320 | } | 320 | } |
| 321 | 321 | ||
| 322 | static inline int | 322 | static inline int |
| 323 | nf_conntrack_expect_unregister_notifier(struct notifier_block *nb) | 323 | nf_conntrack_expect_unregister_notifier(struct notifier_block *nb) |
| 324 | { | 324 | { |
| 325 | return notifier_chain_unregister(&nf_conntrack_expect_chain, nb); | 325 | return atomic_notifier_chain_unregister(&nf_conntrack_expect_chain, |
| 326 | nb); | ||
| 326 | } | 327 | } |
| 327 | 328 | ||
| 328 | extern void nf_ct_deliver_cached_events(const struct nf_conn *ct); | 329 | extern void nf_ct_deliver_cached_events(const struct nf_conn *ct); |
| @@ -347,14 +348,14 @@ static inline void nf_conntrack_event(enum ip_conntrack_events event, | |||
| 347 | struct nf_conn *ct) | 348 | struct nf_conn *ct) |
| 348 | { | 349 | { |
| 349 | if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) | 350 | if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) |
| 350 | notifier_call_chain(&nf_conntrack_chain, event, ct); | 351 | atomic_notifier_call_chain(&nf_conntrack_chain, event, ct); |
| 351 | } | 352 | } |
| 352 | 353 | ||
| 353 | static inline void | 354 | static inline void |
| 354 | nf_conntrack_expect_event(enum ip_conntrack_expect_events event, | 355 | nf_conntrack_expect_event(enum ip_conntrack_expect_events event, |
| 355 | struct nf_conntrack_expect *exp) | 356 | struct nf_conntrack_expect *exp) |
| 356 | { | 357 | { |
| 357 | notifier_call_chain(&nf_conntrack_expect_chain, event, exp); | 358 | atomic_notifier_call_chain(&nf_conntrack_expect_chain, event, exp); |
| 358 | } | 359 | } |
| 359 | #else /* CONFIG_NF_CONNTRACK_EVENTS */ | 360 | #else /* CONFIG_NF_CONNTRACK_EVENTS */ |
| 360 | static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, | 361 | static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, |
diff --git a/kernel/Makefile b/kernel/Makefile index ff1c11dc12cf..58908f9d156a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
| @@ -12,6 +12,9 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ | |||
| 12 | 12 | ||
| 13 | obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o | 13 | obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o |
| 14 | obj-$(CONFIG_FUTEX) += futex.o | 14 | obj-$(CONFIG_FUTEX) += futex.o |
| 15 | ifeq ($(CONFIG_COMPAT),y) | ||
| 16 | obj-$(CONFIG_FUTEX) += futex_compat.o | ||
| 17 | endif | ||
| 15 | obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o | 18 | obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o |
| 16 | obj-$(CONFIG_SMP) += cpu.o spinlock.o | 19 | obj-$(CONFIG_SMP) += cpu.o spinlock.o |
| 17 | obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o | 20 | obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o |
diff --git a/kernel/compat.c b/kernel/compat.c index b9bdd1271f44..c1601a84f8d8 100644 --- a/kernel/compat.c +++ b/kernel/compat.c | |||
| @@ -17,7 +17,6 @@ | |||
| 17 | #include <linux/time.h> | 17 | #include <linux/time.h> |
| 18 | #include <linux/signal.h> | 18 | #include <linux/signal.h> |
| 19 | #include <linux/sched.h> /* for MAX_SCHEDULE_TIMEOUT */ | 19 | #include <linux/sched.h> /* for MAX_SCHEDULE_TIMEOUT */ |
| 20 | #include <linux/futex.h> /* for FUTEX_WAIT */ | ||
| 21 | #include <linux/syscalls.h> | 20 | #include <linux/syscalls.h> |
| 22 | #include <linux/unistd.h> | 21 | #include <linux/unistd.h> |
| 23 | #include <linux/security.h> | 22 | #include <linux/security.h> |
| @@ -239,28 +238,6 @@ asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t __user *set, | |||
| 239 | return ret; | 238 | return ret; |
| 240 | } | 239 | } |
| 241 | 240 | ||
| 242 | #ifdef CONFIG_FUTEX | ||
| 243 | asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, int val, | ||
| 244 | struct compat_timespec __user *utime, u32 __user *uaddr2, | ||
| 245 | int val3) | ||
| 246 | { | ||
| 247 | struct timespec t; | ||
| 248 | unsigned long timeout = MAX_SCHEDULE_TIMEOUT; | ||
| 249 | int val2 = 0; | ||
| 250 | |||
| 251 | if ((op == FUTEX_WAIT) && utime) { | ||
| 252 | if (get_compat_timespec(&t, utime)) | ||
| 253 | return -EFAULT; | ||
| 254 | timeout = timespec_to_jiffies(&t) + 1; | ||
| 255 | } | ||
| 256 | if (op >= FUTEX_REQUEUE) | ||
| 257 | val2 = (int) (unsigned long) utime; | ||
| 258 | |||
| 259 | return do_futex((unsigned long)uaddr, op, val, timeout, | ||
| 260 | (unsigned long)uaddr2, val2, val3); | ||
| 261 | } | ||
| 262 | #endif | ||
| 263 | |||
| 264 | asmlinkage long compat_sys_setrlimit(unsigned int resource, | 241 | asmlinkage long compat_sys_setrlimit(unsigned int resource, |
| 265 | struct compat_rlimit __user *rlim) | 242 | struct compat_rlimit __user *rlim) |
| 266 | { | 243 | { |
diff --git a/kernel/cpu.c b/kernel/cpu.c index 8be22bd80933..fe2b8d0bfe4c 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
| @@ -18,7 +18,7 @@ | |||
| 18 | /* This protects CPUs going up and down... */ | 18 | /* This protects CPUs going up and down... */ |
| 19 | static DECLARE_MUTEX(cpucontrol); | 19 | static DECLARE_MUTEX(cpucontrol); |
| 20 | 20 | ||
| 21 | static struct notifier_block *cpu_chain; | 21 | static BLOCKING_NOTIFIER_HEAD(cpu_chain); |
| 22 | 22 | ||
| 23 | #ifdef CONFIG_HOTPLUG_CPU | 23 | #ifdef CONFIG_HOTPLUG_CPU |
| 24 | static struct task_struct *lock_cpu_hotplug_owner; | 24 | static struct task_struct *lock_cpu_hotplug_owner; |
| @@ -71,21 +71,13 @@ EXPORT_SYMBOL_GPL(lock_cpu_hotplug_interruptible); | |||
| 71 | /* Need to know about CPUs going up/down? */ | 71 | /* Need to know about CPUs going up/down? */ |
| 72 | int register_cpu_notifier(struct notifier_block *nb) | 72 | int register_cpu_notifier(struct notifier_block *nb) |
| 73 | { | 73 | { |
| 74 | int ret; | 74 | return blocking_notifier_chain_register(&cpu_chain, nb); |
| 75 | |||
| 76 | if ((ret = lock_cpu_hotplug_interruptible()) != 0) | ||
| 77 | return ret; | ||
| 78 | ret = notifier_chain_register(&cpu_chain, nb); | ||
| 79 | unlock_cpu_hotplug(); | ||
| 80 | return ret; | ||
| 81 | } | 75 | } |
| 82 | EXPORT_SYMBOL(register_cpu_notifier); | 76 | EXPORT_SYMBOL(register_cpu_notifier); |
| 83 | 77 | ||
| 84 | void unregister_cpu_notifier(struct notifier_block *nb) | 78 | void unregister_cpu_notifier(struct notifier_block *nb) |
| 85 | { | 79 | { |
| 86 | lock_cpu_hotplug(); | 80 | blocking_notifier_chain_unregister(&cpu_chain, nb); |
| 87 | notifier_chain_unregister(&cpu_chain, nb); | ||
| 88 | unlock_cpu_hotplug(); | ||
| 89 | } | 81 | } |
| 90 | EXPORT_SYMBOL(unregister_cpu_notifier); | 82 | EXPORT_SYMBOL(unregister_cpu_notifier); |
| 91 | 83 | ||
| @@ -141,7 +133,7 @@ int cpu_down(unsigned int cpu) | |||
| 141 | goto out; | 133 | goto out; |
| 142 | } | 134 | } |
| 143 | 135 | ||
| 144 | err = notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, | 136 | err = blocking_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, |
| 145 | (void *)(long)cpu); | 137 | (void *)(long)cpu); |
| 146 | if (err == NOTIFY_BAD) { | 138 | if (err == NOTIFY_BAD) { |
| 147 | printk("%s: attempt to take down CPU %u failed\n", | 139 | printk("%s: attempt to take down CPU %u failed\n", |
| @@ -159,7 +151,7 @@ int cpu_down(unsigned int cpu) | |||
| 159 | p = __stop_machine_run(take_cpu_down, NULL, cpu); | 151 | p = __stop_machine_run(take_cpu_down, NULL, cpu); |
| 160 | if (IS_ERR(p)) { | 152 | if (IS_ERR(p)) { |
| 161 | /* CPU didn't die: tell everyone. Can't complain. */ | 153 | /* CPU didn't die: tell everyone. Can't complain. */ |
| 162 | if (notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED, | 154 | if (blocking_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED, |
| 163 | (void *)(long)cpu) == NOTIFY_BAD) | 155 | (void *)(long)cpu) == NOTIFY_BAD) |
| 164 | BUG(); | 156 | BUG(); |
| 165 | 157 | ||
| @@ -182,8 +174,8 @@ int cpu_down(unsigned int cpu) | |||
| 182 | put_cpu(); | 174 | put_cpu(); |
| 183 | 175 | ||
| 184 | /* CPU is completely dead: tell everyone. Too late to complain. */ | 176 | /* CPU is completely dead: tell everyone. Too late to complain. */ |
| 185 | if (notifier_call_chain(&cpu_chain, CPU_DEAD, (void *)(long)cpu) | 177 | if (blocking_notifier_call_chain(&cpu_chain, CPU_DEAD, |
| 186 | == NOTIFY_BAD) | 178 | (void *)(long)cpu) == NOTIFY_BAD) |
| 187 | BUG(); | 179 | BUG(); |
| 188 | 180 | ||
| 189 | check_for_tasks(cpu); | 181 | check_for_tasks(cpu); |
| @@ -211,7 +203,7 @@ int __devinit cpu_up(unsigned int cpu) | |||
| 211 | goto out; | 203 | goto out; |
| 212 | } | 204 | } |
| 213 | 205 | ||
| 214 | ret = notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu); | 206 | ret = blocking_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu); |
| 215 | if (ret == NOTIFY_BAD) { | 207 | if (ret == NOTIFY_BAD) { |
| 216 | printk("%s: attempt to bring up CPU %u failed\n", | 208 | printk("%s: attempt to bring up CPU %u failed\n", |
| 217 | __FUNCTION__, cpu); | 209 | __FUNCTION__, cpu); |
| @@ -226,11 +218,12 @@ int __devinit cpu_up(unsigned int cpu) | |||
| 226 | BUG_ON(!cpu_online(cpu)); | 218 | BUG_ON(!cpu_online(cpu)); |
| 227 | 219 | ||
| 228 | /* Now call notifier in preparation. */ | 220 | /* Now call notifier in preparation. */ |
| 229 | notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu); | 221 | blocking_notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu); |
| 230 | 222 | ||
| 231 | out_notify: | 223 | out_notify: |
| 232 | if (ret != 0) | 224 | if (ret != 0) |
| 233 | notifier_call_chain(&cpu_chain, CPU_UP_CANCELED, hcpu); | 225 | blocking_notifier_call_chain(&cpu_chain, |
| 226 | CPU_UP_CANCELED, hcpu); | ||
| 234 | out: | 227 | out: |
| 235 | unlock_cpu_hotplug(); | 228 | unlock_cpu_hotplug(); |
| 236 | return ret; | 229 | return ret; |
diff --git a/kernel/exit.c b/kernel/exit.c index 8037405e136e..a8c7efc7a681 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
| @@ -31,6 +31,8 @@ | |||
| 31 | #include <linux/signal.h> | 31 | #include <linux/signal.h> |
| 32 | #include <linux/cn_proc.h> | 32 | #include <linux/cn_proc.h> |
| 33 | #include <linux/mutex.h> | 33 | #include <linux/mutex.h> |
| 34 | #include <linux/futex.h> | ||
| 35 | #include <linux/compat.h> | ||
| 34 | 36 | ||
| 35 | #include <asm/uaccess.h> | 37 | #include <asm/uaccess.h> |
| 36 | #include <asm/unistd.h> | 38 | #include <asm/unistd.h> |
| @@ -852,6 +854,12 @@ fastcall NORET_TYPE void do_exit(long code) | |||
| 852 | exit_itimers(tsk->signal); | 854 | exit_itimers(tsk->signal); |
| 853 | acct_process(code); | 855 | acct_process(code); |
| 854 | } | 856 | } |
| 857 | if (unlikely(tsk->robust_list)) | ||
| 858 | exit_robust_list(tsk); | ||
| 859 | #ifdef CONFIG_COMPAT | ||
| 860 | if (unlikely(tsk->compat_robust_list)) | ||
| 861 | compat_exit_robust_list(tsk); | ||
| 862 | #endif | ||
| 855 | exit_mm(tsk); | 863 | exit_mm(tsk); |
| 856 | 864 | ||
| 857 | exit_sem(tsk); | 865 | exit_sem(tsk); |
diff --git a/kernel/fork.c b/kernel/fork.c index e0a2b449dea6..c49bd193b058 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -1061,7 +1061,10 @@ static task_t *copy_process(unsigned long clone_flags, | |||
| 1061 | * Clear TID on mm_release()? | 1061 | * Clear TID on mm_release()? |
| 1062 | */ | 1062 | */ |
| 1063 | p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL; | 1063 | p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL; |
| 1064 | 1064 | p->robust_list = NULL; | |
| 1065 | #ifdef CONFIG_COMPAT | ||
| 1066 | p->compat_robust_list = NULL; | ||
| 1067 | #endif | ||
| 1065 | /* | 1068 | /* |
| 1066 | * sigaltstack should be cleared when sharing the same VM | 1069 | * sigaltstack should be cleared when sharing the same VM |
| 1067 | */ | 1070 | */ |
diff --git a/kernel/futex.c b/kernel/futex.c index 5efa2f978032..9c9b2b6b22dd 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | * Removed page pinning, fix privately mapped COW pages and other cleanups | 8 | * Removed page pinning, fix privately mapped COW pages and other cleanups |
| 9 | * (C) Copyright 2003, 2004 Jamie Lokier | 9 | * (C) Copyright 2003, 2004 Jamie Lokier |
| 10 | * | 10 | * |
| 11 | * Robust futex support started by Ingo Molnar | ||
| 12 | * (C) Copyright 2006 Red Hat Inc, All Rights Reserved | ||
| 13 | * Thanks to Thomas Gleixner for suggestions, analysis and fixes. | ||
| 14 | * | ||
| 11 | * Thanks to Ben LaHaise for yelling "hashed waitqueues" loudly | 15 | * Thanks to Ben LaHaise for yelling "hashed waitqueues" loudly |
| 12 | * enough at me, Linus for the original (flawed) idea, Matthew | 16 | * enough at me, Linus for the original (flawed) idea, Matthew |
| 13 | * Kirkwood for proof-of-concept implementation. | 17 | * Kirkwood for proof-of-concept implementation. |
| @@ -829,6 +833,172 @@ error: | |||
| 829 | goto out; | 833 | goto out; |
| 830 | } | 834 | } |
| 831 | 835 | ||
| 836 | /* | ||
| 837 | * Support for robust futexes: the kernel cleans up held futexes at | ||
| 838 | * thread exit time. | ||
| 839 | * | ||
| 840 | * Implementation: user-space maintains a per-thread list of locks it | ||
| 841 | * is holding. Upon do_exit(), the kernel carefully walks this list, | ||
| 842 | * and marks all locks that are owned by this thread with the | ||
| 843 | * FUTEX_OWNER_DEAD bit, and wakes up a waiter (if any). The list is | ||
| 844 | * always manipulated with the lock held, so the list is private and | ||
| 845 | * per-thread. Userspace also maintains a per-thread 'list_op_pending' | ||
| 846 | * field, to allow the kernel to clean up if the thread dies after | ||
| 847 | * acquiring the lock, but just before it could have added itself to | ||
| 848 | * the list. There can only be one such pending lock. | ||
| 849 | */ | ||
| 850 | |||
| 851 | /** | ||
| 852 | * sys_set_robust_list - set the robust-futex list head of a task | ||
| 853 | * @head: pointer to the list-head | ||
| 854 | * @len: length of the list-head, as userspace expects | ||
| 855 | */ | ||
| 856 | asmlinkage long | ||
| 857 | sys_set_robust_list(struct robust_list_head __user *head, | ||
| 858 | size_t len) | ||
| 859 | { | ||
| 860 | /* | ||
| 861 | * The kernel knows only one size for now: | ||
| 862 | */ | ||
| 863 | if (unlikely(len != sizeof(*head))) | ||
| 864 | return -EINVAL; | ||
| 865 | |||
| 866 | current->robust_list = head; | ||
| 867 | |||
| 868 | return 0; | ||
| 869 | } | ||
| 870 | |||
| 871 | /** | ||
| 872 | * sys_get_robust_list - get the robust-futex list head of a task | ||
| 873 | * @pid: pid of the process [zero for current task] | ||
| 874 | * @head_ptr: pointer to a list-head pointer, the kernel fills it in | ||
| 875 | * @len_ptr: pointer to a length field, the kernel fills in the header size | ||
| 876 | */ | ||
| 877 | asmlinkage long | ||
| 878 | sys_get_robust_list(int pid, struct robust_list_head __user **head_ptr, | ||
| 879 | size_t __user *len_ptr) | ||
| 880 | { | ||
| 881 | struct robust_list_head *head; | ||
| 882 | unsigned long ret; | ||
| 883 | |||
| 884 | if (!pid) | ||
| 885 | head = current->robust_list; | ||
| 886 | else { | ||
| 887 | struct task_struct *p; | ||
| 888 | |||
| 889 | ret = -ESRCH; | ||
| 890 | read_lock(&tasklist_lock); | ||
| 891 | p = find_task_by_pid(pid); | ||
| 892 | if (!p) | ||
| 893 | goto err_unlock; | ||
| 894 | ret = -EPERM; | ||
| 895 | if ((current->euid != p->euid) && (current->euid != p->uid) && | ||
| 896 | !capable(CAP_SYS_PTRACE)) | ||
| 897 | goto err_unlock; | ||
| 898 | head = p->robust_list; | ||
| 899 | read_unlock(&tasklist_lock); | ||
| 900 | } | ||
| 901 | |||
| 902 | if (put_user(sizeof(*head), len_ptr)) | ||
| 903 | return -EFAULT; | ||
| 904 | return put_user(head, head_ptr); | ||
| 905 | |||
| 906 | err_unlock: | ||
| 907 | read_unlock(&tasklist_lock); | ||
| 908 | |||
| 909 | return ret; | ||
| 910 | } | ||
| 911 | |||
| 912 | /* | ||
| 913 | * Process a futex-list entry, check whether it's owned by the | ||
| 914 | * dying task, and do notification if so: | ||
| 915 | */ | ||
| 916 | int handle_futex_death(u32 __user *uaddr, struct task_struct *curr) | ||
| 917 | { | ||
| 918 | u32 uval; | ||
| 919 | |||
| 920 | retry: | ||
| 921 | if (get_user(uval, uaddr)) | ||
| 922 | return -1; | ||
| 923 | |||
| 924 | if ((uval & FUTEX_TID_MASK) == curr->pid) { | ||
| 925 | /* | ||
| 926 | * Ok, this dying thread is truly holding a futex | ||
| 927 | * of interest. Set the OWNER_DIED bit atomically | ||
| 928 | * via cmpxchg, and if the value had FUTEX_WAITERS | ||
| 929 | * set, wake up a waiter (if any). (We have to do a | ||
| 930 | * futex_wake() even if OWNER_DIED is already set - | ||
| 931 | * to handle the rare but possible case of recursive | ||
| 932 | * thread-death.) The rest of the cleanup is done in | ||
| 933 | * userspace. | ||
| 934 | */ | ||
| 935 | if (futex_atomic_cmpxchg_inatomic(uaddr, uval, | ||
| 936 | uval | FUTEX_OWNER_DIED) != uval) | ||
| 937 | goto retry; | ||
| 938 | |||
| 939 | if (uval & FUTEX_WAITERS) | ||
| 940 | futex_wake((unsigned long)uaddr, 1); | ||
| 941 | } | ||
| 942 | return 0; | ||
| 943 | } | ||
| 944 | |||
| 945 | /* | ||
| 946 | * Walk curr->robust_list (very carefully, it's a userspace list!) | ||
| 947 | * and mark any locks found there dead, and notify any waiters. | ||
| 948 | * | ||
| 949 | * We silently return on any sign of list-walking problem. | ||
| 950 | */ | ||
| 951 | void exit_robust_list(struct task_struct *curr) | ||
| 952 | { | ||
| 953 | struct robust_list_head __user *head = curr->robust_list; | ||
| 954 | struct robust_list __user *entry, *pending; | ||
| 955 | unsigned int limit = ROBUST_LIST_LIMIT; | ||
| 956 | unsigned long futex_offset; | ||
| 957 | |||
| 958 | /* | ||
| 959 | * Fetch the list head (which was registered earlier, via | ||
| 960 | * sys_set_robust_list()): | ||
| 961 | */ | ||
| 962 | if (get_user(entry, &head->list.next)) | ||
| 963 | return; | ||
| 964 | /* | ||
| 965 | * Fetch the relative futex offset: | ||
| 966 | */ | ||
| 967 | if (get_user(futex_offset, &head->futex_offset)) | ||
| 968 | return; | ||
| 969 | /* | ||
| 970 | * Fetch any possibly pending lock-add first, and handle it | ||
| 971 | * if it exists: | ||
| 972 | */ | ||
| 973 | if (get_user(pending, &head->list_op_pending)) | ||
| 974 | return; | ||
| 975 | if (pending) | ||
| 976 | handle_futex_death((void *)pending + futex_offset, curr); | ||
| 977 | |||
| 978 | while (entry != &head->list) { | ||
| 979 | /* | ||
| 980 | * A pending lock might already be on the list, so | ||
| 981 | * dont process it twice: | ||
| 982 | */ | ||
| 983 | if (entry != pending) | ||
| 984 | if (handle_futex_death((void *)entry + futex_offset, | ||
| 985 | curr)) | ||
| 986 | return; | ||
| 987 | /* | ||
| 988 | * Fetch the next entry in the list: | ||
| 989 | */ | ||
| 990 | if (get_user(entry, &entry->next)) | ||
| 991 | return; | ||
| 992 | /* | ||
| 993 | * Avoid excessively long or circular lists: | ||
| 994 | */ | ||
| 995 | if (!--limit) | ||
| 996 | break; | ||
| 997 | |||
| 998 | cond_resched(); | ||
| 999 | } | ||
| 1000 | } | ||
| 1001 | |||
| 832 | long do_futex(unsigned long uaddr, int op, int val, unsigned long timeout, | 1002 | long do_futex(unsigned long uaddr, int op, int val, unsigned long timeout, |
| 833 | unsigned long uaddr2, int val2, int val3) | 1003 | unsigned long uaddr2, int val2, int val3) |
| 834 | { | 1004 | { |
diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c new file mode 100644 index 000000000000..9c077cf9aa84 --- /dev/null +++ b/kernel/futex_compat.c | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | /* | ||
| 2 | * linux/kernel/futex_compat.c | ||
| 3 | * | ||
| 4 | * Futex compatibililty routines. | ||
| 5 | * | ||
| 6 | * Copyright 2006, Red Hat, Inc., Ingo Molnar | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/linkage.h> | ||
| 10 | #include <linux/compat.h> | ||
| 11 | #include <linux/futex.h> | ||
| 12 | |||
| 13 | #include <asm/uaccess.h> | ||
| 14 | |||
| 15 | /* | ||
| 16 | * Walk curr->robust_list (very carefully, it's a userspace list!) | ||
| 17 | * and mark any locks found there dead, and notify any waiters. | ||
| 18 | * | ||
| 19 | * We silently return on any sign of list-walking problem. | ||
| 20 | */ | ||
| 21 | void compat_exit_robust_list(struct task_struct *curr) | ||
| 22 | { | ||
| 23 | struct compat_robust_list_head __user *head = curr->compat_robust_list; | ||
| 24 | struct robust_list __user *entry, *pending; | ||
| 25 | compat_uptr_t uentry, upending; | ||
| 26 | unsigned int limit = ROBUST_LIST_LIMIT; | ||
| 27 | compat_long_t futex_offset; | ||
| 28 | |||
| 29 | /* | ||
| 30 | * Fetch the list head (which was registered earlier, via | ||
| 31 | * sys_set_robust_list()): | ||
| 32 | */ | ||
| 33 | if (get_user(uentry, &head->list.next)) | ||
| 34 | return; | ||
| 35 | entry = compat_ptr(uentry); | ||
| 36 | /* | ||
| 37 | * Fetch the relative futex offset: | ||
| 38 | */ | ||
| 39 | if (get_user(futex_offset, &head->futex_offset)) | ||
| 40 | return; | ||
| 41 | /* | ||
| 42 | * Fetch any possibly pending lock-add first, and handle it | ||
| 43 | * if it exists: | ||
| 44 | */ | ||
| 45 | if (get_user(upending, &head->list_op_pending)) | ||
| 46 | return; | ||
| 47 | pending = compat_ptr(upending); | ||
| 48 | if (upending) | ||
| 49 | handle_futex_death((void *)pending + futex_offset, curr); | ||
| 50 | |||
| 51 | while (compat_ptr(uentry) != &head->list) { | ||
| 52 | /* | ||
| 53 | * A pending lock might already be on the list, so | ||
| 54 | * dont process it twice: | ||
| 55 | */ | ||
| 56 | if (entry != pending) | ||
| 57 | if (handle_futex_death((void *)entry + futex_offset, | ||
| 58 | curr)) | ||
| 59 | return; | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Fetch the next entry in the list: | ||
| 63 | */ | ||
| 64 | if (get_user(uentry, (compat_uptr_t *)&entry->next)) | ||
| 65 | return; | ||
| 66 | entry = compat_ptr(uentry); | ||
| 67 | /* | ||
| 68 | * Avoid excessively long or circular lists: | ||
| 69 | */ | ||
| 70 | if (!--limit) | ||
| 71 | break; | ||
| 72 | |||
| 73 | cond_resched(); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | asmlinkage long | ||
| 78 | compat_sys_set_robust_list(struct compat_robust_list_head __user *head, | ||
| 79 | compat_size_t len) | ||
| 80 | { | ||
| 81 | if (unlikely(len != sizeof(*head))) | ||
| 82 | return -EINVAL; | ||
| 83 | |||
| 84 | current->compat_robust_list = head; | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | asmlinkage long | ||
| 90 | compat_sys_get_robust_list(int pid, compat_uptr_t *head_ptr, | ||
| 91 | compat_size_t __user *len_ptr) | ||
| 92 | { | ||
| 93 | struct compat_robust_list_head *head; | ||
| 94 | unsigned long ret; | ||
| 95 | |||
| 96 | if (!pid) | ||
| 97 | head = current->compat_robust_list; | ||
| 98 | else { | ||
| 99 | struct task_struct *p; | ||
| 100 | |||
| 101 | ret = -ESRCH; | ||
| 102 | read_lock(&tasklist_lock); | ||
| 103 | p = find_task_by_pid(pid); | ||
| 104 | if (!p) | ||
| 105 | goto err_unlock; | ||
| 106 | ret = -EPERM; | ||
| 107 | if ((current->euid != p->euid) && (current->euid != p->uid) && | ||
| 108 | !capable(CAP_SYS_PTRACE)) | ||
| 109 | goto err_unlock; | ||
| 110 | head = p->compat_robust_list; | ||
| 111 | read_unlock(&tasklist_lock); | ||
| 112 | } | ||
| 113 | |||
| 114 | if (put_user(sizeof(*head), len_ptr)) | ||
| 115 | return -EFAULT; | ||
| 116 | return put_user(ptr_to_compat(head), head_ptr); | ||
| 117 | |||
| 118 | err_unlock: | ||
| 119 | read_unlock(&tasklist_lock); | ||
| 120 | |||
| 121 | return ret; | ||
| 122 | } | ||
| 123 | |||
| 124 | asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val, | ||
| 125 | struct compat_timespec __user *utime, u32 __user *uaddr2, | ||
| 126 | u32 val3) | ||
| 127 | { | ||
| 128 | struct timespec t; | ||
| 129 | unsigned long timeout = MAX_SCHEDULE_TIMEOUT; | ||
| 130 | int val2 = 0; | ||
| 131 | |||
| 132 | if ((op == FUTEX_WAIT) && utime) { | ||
| 133 | if (get_compat_timespec(&t, utime)) | ||
| 134 | return -EFAULT; | ||
| 135 | timeout = timespec_to_jiffies(&t) + 1; | ||
| 136 | } | ||
| 137 | if (op >= FUTEX_REQUEUE) | ||
| 138 | val2 = (int) (unsigned long) utime; | ||
| 139 | |||
| 140 | return do_futex(uaddr, op, val, timeout, uaddr2, val2, val3); | ||
| 141 | } | ||
diff --git a/kernel/module.c b/kernel/module.c index ddfe45ac2fd1..4fafd58038a0 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
| @@ -64,26 +64,17 @@ static DEFINE_SPINLOCK(modlist_lock); | |||
| 64 | static DEFINE_MUTEX(module_mutex); | 64 | static DEFINE_MUTEX(module_mutex); |
| 65 | static LIST_HEAD(modules); | 65 | static LIST_HEAD(modules); |
| 66 | 66 | ||
| 67 | static DEFINE_MUTEX(notify_mutex); | 67 | static BLOCKING_NOTIFIER_HEAD(module_notify_list); |
| 68 | static struct notifier_block * module_notify_list; | ||
| 69 | 68 | ||
| 70 | int register_module_notifier(struct notifier_block * nb) | 69 | int register_module_notifier(struct notifier_block * nb) |
| 71 | { | 70 | { |
| 72 | int err; | 71 | return blocking_notifier_chain_register(&module_notify_list, nb); |
| 73 | mutex_lock(¬ify_mutex); | ||
| 74 | err = notifier_chain_register(&module_notify_list, nb); | ||
| 75 | mutex_unlock(¬ify_mutex); | ||
| 76 | return err; | ||
| 77 | } | 72 | } |
| 78 | EXPORT_SYMBOL(register_module_notifier); | 73 | EXPORT_SYMBOL(register_module_notifier); |
| 79 | 74 | ||
| 80 | int unregister_module_notifier(struct notifier_block * nb) | 75 | int unregister_module_notifier(struct notifier_block * nb) |
| 81 | { | 76 | { |
| 82 | int err; | 77 | return blocking_notifier_chain_unregister(&module_notify_list, nb); |
| 83 | mutex_lock(¬ify_mutex); | ||
| 84 | err = notifier_chain_unregister(&module_notify_list, nb); | ||
| 85 | mutex_unlock(¬ify_mutex); | ||
| 86 | return err; | ||
| 87 | } | 78 | } |
| 88 | EXPORT_SYMBOL(unregister_module_notifier); | 79 | EXPORT_SYMBOL(unregister_module_notifier); |
| 89 | 80 | ||
| @@ -1816,9 +1807,8 @@ sys_init_module(void __user *umod, | |||
| 1816 | /* Drop lock so they can recurse */ | 1807 | /* Drop lock so they can recurse */ |
| 1817 | mutex_unlock(&module_mutex); | 1808 | mutex_unlock(&module_mutex); |
| 1818 | 1809 | ||
| 1819 | mutex_lock(¬ify_mutex); | 1810 | blocking_notifier_call_chain(&module_notify_list, |
| 1820 | notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod); | 1811 | MODULE_STATE_COMING, mod); |
| 1821 | mutex_unlock(¬ify_mutex); | ||
| 1822 | 1812 | ||
| 1823 | /* Start the module */ | 1813 | /* Start the module */ |
| 1824 | if (mod->init != NULL) | 1814 | if (mod->init != NULL) |
diff --git a/kernel/panic.c b/kernel/panic.c index acd95adddb93..f895c7c01d5b 100644 --- a/kernel/panic.c +++ b/kernel/panic.c | |||
| @@ -29,7 +29,7 @@ static DEFINE_SPINLOCK(pause_on_oops_lock); | |||
| 29 | int panic_timeout; | 29 | int panic_timeout; |
| 30 | EXPORT_SYMBOL(panic_timeout); | 30 | EXPORT_SYMBOL(panic_timeout); |
| 31 | 31 | ||
| 32 | struct notifier_block *panic_notifier_list; | 32 | ATOMIC_NOTIFIER_HEAD(panic_notifier_list); |
| 33 | 33 | ||
| 34 | EXPORT_SYMBOL(panic_notifier_list); | 34 | EXPORT_SYMBOL(panic_notifier_list); |
| 35 | 35 | ||
| @@ -97,7 +97,7 @@ NORET_TYPE void panic(const char * fmt, ...) | |||
| 97 | smp_send_stop(); | 97 | smp_send_stop(); |
| 98 | #endif | 98 | #endif |
| 99 | 99 | ||
| 100 | notifier_call_chain(&panic_notifier_list, 0, buf); | 100 | atomic_notifier_call_chain(&panic_notifier_list, 0, buf); |
| 101 | 101 | ||
| 102 | if (!panic_blink) | 102 | if (!panic_blink) |
| 103 | panic_blink = no_blink; | 103 | panic_blink = no_blink; |
diff --git a/kernel/profile.c b/kernel/profile.c index ad81f799a9b4..5a730fdb1a2c 100644 --- a/kernel/profile.c +++ b/kernel/profile.c | |||
| @@ -87,72 +87,52 @@ void __init profile_init(void) | |||
| 87 | 87 | ||
| 88 | #ifdef CONFIG_PROFILING | 88 | #ifdef CONFIG_PROFILING |
| 89 | 89 | ||
| 90 | static DECLARE_RWSEM(profile_rwsem); | 90 | static BLOCKING_NOTIFIER_HEAD(task_exit_notifier); |
| 91 | static DEFINE_RWLOCK(handoff_lock); | 91 | static ATOMIC_NOTIFIER_HEAD(task_free_notifier); |
| 92 | static struct notifier_block * task_exit_notifier; | 92 | static BLOCKING_NOTIFIER_HEAD(munmap_notifier); |
| 93 | static struct notifier_block * task_free_notifier; | ||
| 94 | static struct notifier_block * munmap_notifier; | ||
| 95 | 93 | ||
| 96 | void profile_task_exit(struct task_struct * task) | 94 | void profile_task_exit(struct task_struct * task) |
| 97 | { | 95 | { |
| 98 | down_read(&profile_rwsem); | 96 | blocking_notifier_call_chain(&task_exit_notifier, 0, task); |
| 99 | notifier_call_chain(&task_exit_notifier, 0, task); | ||
| 100 | up_read(&profile_rwsem); | ||
| 101 | } | 97 | } |
| 102 | 98 | ||
| 103 | int profile_handoff_task(struct task_struct * task) | 99 | int profile_handoff_task(struct task_struct * task) |
| 104 | { | 100 | { |
| 105 | int ret; | 101 | int ret; |
| 106 | read_lock(&handoff_lock); | 102 | ret = atomic_notifier_call_chain(&task_free_notifier, 0, task); |
| 107 | ret = notifier_call_chain(&task_free_notifier, 0, task); | ||
| 108 | read_unlock(&handoff_lock); | ||
| 109 | return (ret == NOTIFY_OK) ? 1 : 0; | 103 | return (ret == NOTIFY_OK) ? 1 : 0; |
| 110 | } | 104 | } |
| 111 | 105 | ||
| 112 | void profile_munmap(unsigned long addr) | 106 | void profile_munmap(unsigned long addr) |
| 113 | { | 107 | { |
| 114 | down_read(&profile_rwsem); | 108 | blocking_notifier_call_chain(&munmap_notifier, 0, (void *)addr); |
| 115 | notifier_call_chain(&munmap_notifier, 0, (void *)addr); | ||
| 116 | up_read(&profile_rwsem); | ||
| 117 | } | 109 | } |
| 118 | 110 | ||
| 119 | int task_handoff_register(struct notifier_block * n) | 111 | int task_handoff_register(struct notifier_block * n) |
| 120 | { | 112 | { |
| 121 | int err = -EINVAL; | 113 | return atomic_notifier_chain_register(&task_free_notifier, n); |
| 122 | |||
| 123 | write_lock(&handoff_lock); | ||
| 124 | err = notifier_chain_register(&task_free_notifier, n); | ||
| 125 | write_unlock(&handoff_lock); | ||
| 126 | return err; | ||
| 127 | } | 114 | } |
| 128 | 115 | ||
| 129 | int task_handoff_unregister(struct notifier_block * n) | 116 | int task_handoff_unregister(struct notifier_block * n) |
| 130 | { | 117 | { |
| 131 | int err = -EINVAL; | 118 | return atomic_notifier_chain_unregister(&task_free_notifier, n); |
| 132 | |||
| 133 | write_lock(&handoff_lock); | ||
| 134 | err = notifier_chain_unregister(&task_free_notifier, n); | ||
| 135 | write_unlock(&handoff_lock); | ||
| 136 | return err; | ||
| 137 | } | 119 | } |
| 138 | 120 | ||
| 139 | int profile_event_register(enum profile_type type, struct notifier_block * n) | 121 | int profile_event_register(enum profile_type type, struct notifier_block * n) |
| 140 | { | 122 | { |
| 141 | int err = -EINVAL; | 123 | int err = -EINVAL; |
| 142 | 124 | ||
| 143 | down_write(&profile_rwsem); | ||
| 144 | |||
| 145 | switch (type) { | 125 | switch (type) { |
| 146 | case PROFILE_TASK_EXIT: | 126 | case PROFILE_TASK_EXIT: |
| 147 | err = notifier_chain_register(&task_exit_notifier, n); | 127 | err = blocking_notifier_chain_register( |
| 128 | &task_exit_notifier, n); | ||
| 148 | break; | 129 | break; |
| 149 | case PROFILE_MUNMAP: | 130 | case PROFILE_MUNMAP: |
| 150 | err = notifier_chain_register(&munmap_notifier, n); | 131 | err = blocking_notifier_chain_register( |
| 132 | &munmap_notifier, n); | ||
| 151 | break; | 133 | break; |
| 152 | } | 134 | } |
| 153 | 135 | ||
| 154 | up_write(&profile_rwsem); | ||
| 155 | |||
| 156 | return err; | 136 | return err; |
| 157 | } | 137 | } |
| 158 | 138 | ||
| @@ -161,18 +141,17 @@ int profile_event_unregister(enum profile_type type, struct notifier_block * n) | |||
| 161 | { | 141 | { |
| 162 | int err = -EINVAL; | 142 | int err = -EINVAL; |
| 163 | 143 | ||
| 164 | down_write(&profile_rwsem); | ||
| 165 | |||
| 166 | switch (type) { | 144 | switch (type) { |
| 167 | case PROFILE_TASK_EXIT: | 145 | case PROFILE_TASK_EXIT: |
| 168 | err = notifier_chain_unregister(&task_exit_notifier, n); | 146 | err = blocking_notifier_chain_unregister( |
| 147 | &task_exit_notifier, n); | ||
| 169 | break; | 148 | break; |
| 170 | case PROFILE_MUNMAP: | 149 | case PROFILE_MUNMAP: |
| 171 | err = notifier_chain_unregister(&munmap_notifier, n); | 150 | err = blocking_notifier_chain_unregister( |
| 151 | &munmap_notifier, n); | ||
| 172 | break; | 152 | break; |
| 173 | } | 153 | } |
| 174 | 154 | ||
| 175 | up_write(&profile_rwsem); | ||
| 176 | return err; | 155 | return err; |
| 177 | } | 156 | } |
| 178 | 157 | ||
diff --git a/kernel/sched.c b/kernel/sched.c index 78acdefeccca..7854ee516b92 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
| @@ -145,7 +145,8 @@ | |||
| 145 | (v1) * (v2_max) / (v1_max) | 145 | (v1) * (v2_max) / (v1_max) |
| 146 | 146 | ||
| 147 | #define DELTA(p) \ | 147 | #define DELTA(p) \ |
| 148 | (SCALE(TASK_NICE(p), 40, MAX_BONUS) + INTERACTIVE_DELTA) | 148 | (SCALE(TASK_NICE(p) + 20, 40, MAX_BONUS) - 20 * MAX_BONUS / 40 + \ |
| 149 | INTERACTIVE_DELTA) | ||
| 149 | 150 | ||
| 150 | #define TASK_INTERACTIVE(p) \ | 151 | #define TASK_INTERACTIVE(p) \ |
| 151 | ((p)->prio <= (p)->static_prio - DELTA(p)) | 152 | ((p)->prio <= (p)->static_prio - DELTA(p)) |
| @@ -2878,13 +2879,11 @@ asmlinkage void __sched schedule(void) | |||
| 2878 | * schedule() atomically, we ignore that path for now. | 2879 | * schedule() atomically, we ignore that path for now. |
| 2879 | * Otherwise, whine if we are scheduling when we should not be. | 2880 | * Otherwise, whine if we are scheduling when we should not be. |
| 2880 | */ | 2881 | */ |
| 2881 | if (likely(!current->exit_state)) { | 2882 | if (unlikely(in_atomic() && !current->exit_state)) { |
| 2882 | if (unlikely(in_atomic())) { | 2883 | printk(KERN_ERR "BUG: scheduling while atomic: " |
| 2883 | printk(KERN_ERR "BUG: scheduling while atomic: " | 2884 | "%s/0x%08x/%d\n", |
| 2884 | "%s/0x%08x/%d\n", | 2885 | current->comm, preempt_count(), current->pid); |
| 2885 | current->comm, preempt_count(), current->pid); | 2886 | dump_stack(); |
| 2886 | dump_stack(); | ||
| 2887 | } | ||
| 2888 | } | 2887 | } |
| 2889 | profile_hit(SCHED_PROFILING, __builtin_return_address(0)); | 2888 | profile_hit(SCHED_PROFILING, __builtin_return_address(0)); |
| 2890 | 2889 | ||
| @@ -5575,11 +5574,31 @@ static int cpu_to_cpu_group(int cpu) | |||
| 5575 | } | 5574 | } |
| 5576 | #endif | 5575 | #endif |
| 5577 | 5576 | ||
| 5577 | #ifdef CONFIG_SCHED_MC | ||
| 5578 | static DEFINE_PER_CPU(struct sched_domain, core_domains); | ||
| 5579 | static struct sched_group sched_group_core[NR_CPUS]; | ||
| 5580 | #endif | ||
| 5581 | |||
| 5582 | #if defined(CONFIG_SCHED_MC) && defined(CONFIG_SCHED_SMT) | ||
| 5583 | static int cpu_to_core_group(int cpu) | ||
| 5584 | { | ||
| 5585 | return first_cpu(cpu_sibling_map[cpu]); | ||
| 5586 | } | ||
| 5587 | #elif defined(CONFIG_SCHED_MC) | ||
| 5588 | static int cpu_to_core_group(int cpu) | ||
| 5589 | { | ||
| 5590 | return cpu; | ||
| 5591 | } | ||
| 5592 | #endif | ||
| 5593 | |||
| 5578 | static DEFINE_PER_CPU(struct sched_domain, phys_domains); | 5594 | static DEFINE_PER_CPU(struct sched_domain, phys_domains); |
| 5579 | static struct sched_group sched_group_phys[NR_CPUS]; | 5595 | static struct sched_group sched_group_phys[NR_CPUS]; |
| 5580 | static int cpu_to_phys_group(int cpu) | 5596 | static int cpu_to_phys_group(int cpu) |
| 5581 | { | 5597 | { |
| 5582 | #ifdef CONFIG_SCHED_SMT | 5598 | #if defined(CONFIG_SCHED_MC) |
| 5599 | cpumask_t mask = cpu_coregroup_map(cpu); | ||
| 5600 | return first_cpu(mask); | ||
| 5601 | #elif defined(CONFIG_SCHED_SMT) | ||
| 5583 | return first_cpu(cpu_sibling_map[cpu]); | 5602 | return first_cpu(cpu_sibling_map[cpu]); |
| 5584 | #else | 5603 | #else |
| 5585 | return cpu; | 5604 | return cpu; |
| @@ -5602,6 +5621,32 @@ static int cpu_to_allnodes_group(int cpu) | |||
| 5602 | { | 5621 | { |
| 5603 | return cpu_to_node(cpu); | 5622 | return cpu_to_node(cpu); |
| 5604 | } | 5623 | } |
| 5624 | static void init_numa_sched_groups_power(struct sched_group *group_head) | ||
| 5625 | { | ||
| 5626 | struct sched_group *sg = group_head; | ||
| 5627 | int j; | ||
| 5628 | |||
| 5629 | if (!sg) | ||
| 5630 | return; | ||
| 5631 | next_sg: | ||
| 5632 | for_each_cpu_mask(j, sg->cpumask) { | ||
| 5633 | struct sched_domain *sd; | ||
| 5634 | |||
| 5635 | sd = &per_cpu(phys_domains, j); | ||
| 5636 | if (j != first_cpu(sd->groups->cpumask)) { | ||
| 5637 | /* | ||
| 5638 | * Only add "power" once for each | ||
| 5639 | * physical package. | ||
| 5640 | */ | ||
| 5641 | continue; | ||
| 5642 | } | ||
| 5643 | |||
| 5644 | sg->cpu_power += sd->groups->cpu_power; | ||
| 5645 | } | ||
| 5646 | sg = sg->next; | ||
| 5647 | if (sg != group_head) | ||
| 5648 | goto next_sg; | ||
| 5649 | } | ||
| 5605 | #endif | 5650 | #endif |
| 5606 | 5651 | ||
| 5607 | /* | 5652 | /* |
| @@ -5677,6 +5722,17 @@ void build_sched_domains(const cpumask_t *cpu_map) | |||
| 5677 | sd->parent = p; | 5722 | sd->parent = p; |
| 5678 | sd->groups = &sched_group_phys[group]; | 5723 | sd->groups = &sched_group_phys[group]; |
| 5679 | 5724 | ||
| 5725 | #ifdef CONFIG_SCHED_MC | ||
| 5726 | p = sd; | ||
| 5727 | sd = &per_cpu(core_domains, i); | ||
| 5728 | group = cpu_to_core_group(i); | ||
| 5729 | *sd = SD_MC_INIT; | ||
| 5730 | sd->span = cpu_coregroup_map(i); | ||
| 5731 | cpus_and(sd->span, sd->span, *cpu_map); | ||
| 5732 | sd->parent = p; | ||
| 5733 | sd->groups = &sched_group_core[group]; | ||
| 5734 | #endif | ||
| 5735 | |||
| 5680 | #ifdef CONFIG_SCHED_SMT | 5736 | #ifdef CONFIG_SCHED_SMT |
| 5681 | p = sd; | 5737 | p = sd; |
| 5682 | sd = &per_cpu(cpu_domains, i); | 5738 | sd = &per_cpu(cpu_domains, i); |
| @@ -5702,6 +5758,19 @@ void build_sched_domains(const cpumask_t *cpu_map) | |||
| 5702 | } | 5758 | } |
| 5703 | #endif | 5759 | #endif |
| 5704 | 5760 | ||
| 5761 | #ifdef CONFIG_SCHED_MC | ||
| 5762 | /* Set up multi-core groups */ | ||
| 5763 | for_each_cpu_mask(i, *cpu_map) { | ||
| 5764 | cpumask_t this_core_map = cpu_coregroup_map(i); | ||
| 5765 | cpus_and(this_core_map, this_core_map, *cpu_map); | ||
| 5766 | if (i != first_cpu(this_core_map)) | ||
| 5767 | continue; | ||
| 5768 | init_sched_build_groups(sched_group_core, this_core_map, | ||
| 5769 | &cpu_to_core_group); | ||
| 5770 | } | ||
| 5771 | #endif | ||
| 5772 | |||
| 5773 | |||
| 5705 | /* Set up physical groups */ | 5774 | /* Set up physical groups */ |
| 5706 | for (i = 0; i < MAX_NUMNODES; i++) { | 5775 | for (i = 0; i < MAX_NUMNODES; i++) { |
| 5707 | cpumask_t nodemask = node_to_cpumask(i); | 5776 | cpumask_t nodemask = node_to_cpumask(i); |
| @@ -5798,51 +5867,38 @@ void build_sched_domains(const cpumask_t *cpu_map) | |||
| 5798 | power = SCHED_LOAD_SCALE; | 5867 | power = SCHED_LOAD_SCALE; |
| 5799 | sd->groups->cpu_power = power; | 5868 | sd->groups->cpu_power = power; |
| 5800 | #endif | 5869 | #endif |
| 5870 | #ifdef CONFIG_SCHED_MC | ||
| 5871 | sd = &per_cpu(core_domains, i); | ||
| 5872 | power = SCHED_LOAD_SCALE + (cpus_weight(sd->groups->cpumask)-1) | ||
| 5873 | * SCHED_LOAD_SCALE / 10; | ||
| 5874 | sd->groups->cpu_power = power; | ||
| 5801 | 5875 | ||
| 5802 | sd = &per_cpu(phys_domains, i); | 5876 | sd = &per_cpu(phys_domains, i); |
| 5877 | |||
| 5878 | /* | ||
| 5879 | * This has to be < 2 * SCHED_LOAD_SCALE | ||
| 5880 | * Lets keep it SCHED_LOAD_SCALE, so that | ||
| 5881 | * while calculating NUMA group's cpu_power | ||
| 5882 | * we can simply do | ||
| 5883 | * numa_group->cpu_power += phys_group->cpu_power; | ||
| 5884 | * | ||
| 5885 | * See "only add power once for each physical pkg" | ||
| 5886 | * comment below | ||
| 5887 | */ | ||
| 5888 | sd->groups->cpu_power = SCHED_LOAD_SCALE; | ||
| 5889 | #else | ||
| 5890 | sd = &per_cpu(phys_domains, i); | ||
| 5803 | power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * | 5891 | power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * |
| 5804 | (cpus_weight(sd->groups->cpumask)-1) / 10; | 5892 | (cpus_weight(sd->groups->cpumask)-1) / 10; |
| 5805 | sd->groups->cpu_power = power; | 5893 | sd->groups->cpu_power = power; |
| 5806 | |||
| 5807 | #ifdef CONFIG_NUMA | ||
| 5808 | sd = &per_cpu(allnodes_domains, i); | ||
| 5809 | if (sd->groups) { | ||
| 5810 | power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * | ||
| 5811 | (cpus_weight(sd->groups->cpumask)-1) / 10; | ||
| 5812 | sd->groups->cpu_power = power; | ||
| 5813 | } | ||
| 5814 | #endif | 5894 | #endif |
| 5815 | } | 5895 | } |
| 5816 | 5896 | ||
| 5817 | #ifdef CONFIG_NUMA | 5897 | #ifdef CONFIG_NUMA |
| 5818 | for (i = 0; i < MAX_NUMNODES; i++) { | 5898 | for (i = 0; i < MAX_NUMNODES; i++) |
| 5819 | struct sched_group *sg = sched_group_nodes[i]; | 5899 | init_numa_sched_groups_power(sched_group_nodes[i]); |
| 5820 | int j; | ||
| 5821 | |||
| 5822 | if (sg == NULL) | ||
| 5823 | continue; | ||
| 5824 | next_sg: | ||
| 5825 | for_each_cpu_mask(j, sg->cpumask) { | ||
| 5826 | struct sched_domain *sd; | ||
| 5827 | int power; | ||
| 5828 | |||
| 5829 | sd = &per_cpu(phys_domains, j); | ||
| 5830 | if (j != first_cpu(sd->groups->cpumask)) { | ||
| 5831 | /* | ||
| 5832 | * Only add "power" once for each | ||
| 5833 | * physical package. | ||
| 5834 | */ | ||
| 5835 | continue; | ||
| 5836 | } | ||
| 5837 | power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * | ||
| 5838 | (cpus_weight(sd->groups->cpumask)-1) / 10; | ||
| 5839 | 5900 | ||
| 5840 | sg->cpu_power += power; | 5901 | init_numa_sched_groups_power(sched_group_allnodes); |
| 5841 | } | ||
| 5842 | sg = sg->next; | ||
| 5843 | if (sg != sched_group_nodes[i]) | ||
| 5844 | goto next_sg; | ||
| 5845 | } | ||
| 5846 | #endif | 5902 | #endif |
| 5847 | 5903 | ||
| 5848 | /* Attach the domains */ | 5904 | /* Attach the domains */ |
| @@ -5850,6 +5906,8 @@ next_sg: | |||
| 5850 | struct sched_domain *sd; | 5906 | struct sched_domain *sd; |
| 5851 | #ifdef CONFIG_SCHED_SMT | 5907 | #ifdef CONFIG_SCHED_SMT |
| 5852 | sd = &per_cpu(cpu_domains, i); | 5908 | sd = &per_cpu(cpu_domains, i); |
| 5909 | #elif defined(CONFIG_SCHED_MC) | ||
| 5910 | sd = &per_cpu(core_domains, i); | ||
| 5853 | #else | 5911 | #else |
| 5854 | sd = &per_cpu(phys_domains, i); | 5912 | sd = &per_cpu(phys_domains, i); |
| 5855 | #endif | 5913 | #endif |
diff --git a/kernel/softlockup.c b/kernel/softlockup.c index d9b3d5847ed8..ced91e1ff564 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c | |||
| @@ -152,5 +152,5 @@ __init void spawn_softlockup_task(void) | |||
| 152 | cpu_callback(&cpu_nfb, CPU_ONLINE, cpu); | 152 | cpu_callback(&cpu_nfb, CPU_ONLINE, cpu); |
| 153 | register_cpu_notifier(&cpu_nfb); | 153 | register_cpu_notifier(&cpu_nfb); |
| 154 | 154 | ||
| 155 | notifier_chain_register(&panic_notifier_list, &panic_block); | 155 | atomic_notifier_chain_register(&panic_notifier_list, &panic_block); |
| 156 | } | 156 | } |
diff --git a/kernel/sys.c b/kernel/sys.c index 38bc73ede2ba..c93d37f71aef 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
| @@ -95,99 +95,304 @@ int cad_pid = 1; | |||
| 95 | * and the like. | 95 | * and the like. |
| 96 | */ | 96 | */ |
| 97 | 97 | ||
| 98 | static struct notifier_block *reboot_notifier_list; | 98 | static BLOCKING_NOTIFIER_HEAD(reboot_notifier_list); |
| 99 | static DEFINE_RWLOCK(notifier_lock); | 99 | |
| 100 | /* | ||
| 101 | * Notifier chain core routines. The exported routines below | ||
| 102 | * are layered on top of these, with appropriate locking added. | ||
| 103 | */ | ||
| 104 | |||
| 105 | static int notifier_chain_register(struct notifier_block **nl, | ||
| 106 | struct notifier_block *n) | ||
| 107 | { | ||
| 108 | while ((*nl) != NULL) { | ||
| 109 | if (n->priority > (*nl)->priority) | ||
| 110 | break; | ||
| 111 | nl = &((*nl)->next); | ||
| 112 | } | ||
| 113 | n->next = *nl; | ||
| 114 | rcu_assign_pointer(*nl, n); | ||
| 115 | return 0; | ||
| 116 | } | ||
| 117 | |||
| 118 | static int notifier_chain_unregister(struct notifier_block **nl, | ||
| 119 | struct notifier_block *n) | ||
| 120 | { | ||
| 121 | while ((*nl) != NULL) { | ||
| 122 | if ((*nl) == n) { | ||
| 123 | rcu_assign_pointer(*nl, n->next); | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | nl = &((*nl)->next); | ||
| 127 | } | ||
| 128 | return -ENOENT; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int __kprobes notifier_call_chain(struct notifier_block **nl, | ||
| 132 | unsigned long val, void *v) | ||
| 133 | { | ||
| 134 | int ret = NOTIFY_DONE; | ||
| 135 | struct notifier_block *nb; | ||
| 136 | |||
| 137 | nb = rcu_dereference(*nl); | ||
| 138 | while (nb) { | ||
| 139 | ret = nb->notifier_call(nb, val, v); | ||
| 140 | if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) | ||
| 141 | break; | ||
| 142 | nb = rcu_dereference(nb->next); | ||
| 143 | } | ||
| 144 | return ret; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* | ||
| 148 | * Atomic notifier chain routines. Registration and unregistration | ||
| 149 | * use a mutex, and call_chain is synchronized by RCU (no locks). | ||
| 150 | */ | ||
| 100 | 151 | ||
| 101 | /** | 152 | /** |
| 102 | * notifier_chain_register - Add notifier to a notifier chain | 153 | * atomic_notifier_chain_register - Add notifier to an atomic notifier chain |
| 103 | * @list: Pointer to root list pointer | 154 | * @nh: Pointer to head of the atomic notifier chain |
| 104 | * @n: New entry in notifier chain | 155 | * @n: New entry in notifier chain |
| 105 | * | 156 | * |
| 106 | * Adds a notifier to a notifier chain. | 157 | * Adds a notifier to an atomic notifier chain. |
| 107 | * | 158 | * |
| 108 | * Currently always returns zero. | 159 | * Currently always returns zero. |
| 109 | */ | 160 | */ |
| 161 | |||
| 162 | int atomic_notifier_chain_register(struct atomic_notifier_head *nh, | ||
| 163 | struct notifier_block *n) | ||
| 164 | { | ||
| 165 | unsigned long flags; | ||
| 166 | int ret; | ||
| 167 | |||
| 168 | spin_lock_irqsave(&nh->lock, flags); | ||
| 169 | ret = notifier_chain_register(&nh->head, n); | ||
| 170 | spin_unlock_irqrestore(&nh->lock, flags); | ||
| 171 | return ret; | ||
| 172 | } | ||
| 173 | |||
| 174 | EXPORT_SYMBOL_GPL(atomic_notifier_chain_register); | ||
| 175 | |||
| 176 | /** | ||
| 177 | * atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain | ||
| 178 | * @nh: Pointer to head of the atomic notifier chain | ||
| 179 | * @n: Entry to remove from notifier chain | ||
| 180 | * | ||
| 181 | * Removes a notifier from an atomic notifier chain. | ||
| 182 | * | ||
| 183 | * Returns zero on success or %-ENOENT on failure. | ||
| 184 | */ | ||
| 185 | int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, | ||
| 186 | struct notifier_block *n) | ||
| 187 | { | ||
| 188 | unsigned long flags; | ||
| 189 | int ret; | ||
| 190 | |||
| 191 | spin_lock_irqsave(&nh->lock, flags); | ||
| 192 | ret = notifier_chain_unregister(&nh->head, n); | ||
| 193 | spin_unlock_irqrestore(&nh->lock, flags); | ||
| 194 | synchronize_rcu(); | ||
| 195 | return ret; | ||
| 196 | } | ||
| 197 | |||
| 198 | EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister); | ||
| 199 | |||
| 200 | /** | ||
| 201 | * atomic_notifier_call_chain - Call functions in an atomic notifier chain | ||
| 202 | * @nh: Pointer to head of the atomic notifier chain | ||
| 203 | * @val: Value passed unmodified to notifier function | ||
| 204 | * @v: Pointer passed unmodified to notifier function | ||
| 205 | * | ||
| 206 | * Calls each function in a notifier chain in turn. The functions | ||
| 207 | * run in an atomic context, so they must not block. | ||
| 208 | * This routine uses RCU to synchronize with changes to the chain. | ||
| 209 | * | ||
| 210 | * If the return value of the notifier can be and'ed | ||
| 211 | * with %NOTIFY_STOP_MASK then atomic_notifier_call_chain | ||
| 212 | * will return immediately, with the return value of | ||
| 213 | * the notifier function which halted execution. | ||
| 214 | * Otherwise the return value is the return value | ||
| 215 | * of the last notifier function called. | ||
| 216 | */ | ||
| 110 | 217 | ||
| 111 | int notifier_chain_register(struct notifier_block **list, struct notifier_block *n) | 218 | int atomic_notifier_call_chain(struct atomic_notifier_head *nh, |
| 219 | unsigned long val, void *v) | ||
| 112 | { | 220 | { |
| 113 | write_lock(¬ifier_lock); | 221 | int ret; |
| 114 | while(*list) | 222 | |
| 115 | { | 223 | rcu_read_lock(); |
| 116 | if(n->priority > (*list)->priority) | 224 | ret = notifier_call_chain(&nh->head, val, v); |
| 117 | break; | 225 | rcu_read_unlock(); |
| 118 | list= &((*list)->next); | 226 | return ret; |
| 119 | } | ||
| 120 | n->next = *list; | ||
| 121 | *list=n; | ||
| 122 | write_unlock(¬ifier_lock); | ||
| 123 | return 0; | ||
| 124 | } | 227 | } |
| 125 | 228 | ||
| 126 | EXPORT_SYMBOL(notifier_chain_register); | 229 | EXPORT_SYMBOL_GPL(atomic_notifier_call_chain); |
| 230 | |||
| 231 | /* | ||
| 232 | * Blocking notifier chain routines. All access to the chain is | ||
| 233 | * synchronized by an rwsem. | ||
| 234 | */ | ||
| 127 | 235 | ||
| 128 | /** | 236 | /** |
| 129 | * notifier_chain_unregister - Remove notifier from a notifier chain | 237 | * blocking_notifier_chain_register - Add notifier to a blocking notifier chain |
| 130 | * @nl: Pointer to root list pointer | 238 | * @nh: Pointer to head of the blocking notifier chain |
| 131 | * @n: New entry in notifier chain | 239 | * @n: New entry in notifier chain |
| 132 | * | 240 | * |
| 133 | * Removes a notifier from a notifier chain. | 241 | * Adds a notifier to a blocking notifier chain. |
| 242 | * Must be called in process context. | ||
| 134 | * | 243 | * |
| 135 | * Returns zero on success, or %-ENOENT on failure. | 244 | * Currently always returns zero. |
| 136 | */ | 245 | */ |
| 137 | 246 | ||
| 138 | int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n) | 247 | int blocking_notifier_chain_register(struct blocking_notifier_head *nh, |
| 248 | struct notifier_block *n) | ||
| 139 | { | 249 | { |
| 140 | write_lock(¬ifier_lock); | 250 | int ret; |
| 141 | while((*nl)!=NULL) | 251 | |
| 142 | { | 252 | /* |
| 143 | if((*nl)==n) | 253 | * This code gets used during boot-up, when task switching is |
| 144 | { | 254 | * not yet working and interrupts must remain disabled. At |
| 145 | *nl=n->next; | 255 | * such times we must not call down_write(). |
| 146 | write_unlock(¬ifier_lock); | 256 | */ |
| 147 | return 0; | 257 | if (unlikely(system_state == SYSTEM_BOOTING)) |
| 148 | } | 258 | return notifier_chain_register(&nh->head, n); |
| 149 | nl=&((*nl)->next); | 259 | |
| 150 | } | 260 | down_write(&nh->rwsem); |
| 151 | write_unlock(¬ifier_lock); | 261 | ret = notifier_chain_register(&nh->head, n); |
| 152 | return -ENOENT; | 262 | up_write(&nh->rwsem); |
| 263 | return ret; | ||
| 153 | } | 264 | } |
| 154 | 265 | ||
| 155 | EXPORT_SYMBOL(notifier_chain_unregister); | 266 | EXPORT_SYMBOL_GPL(blocking_notifier_chain_register); |
| 156 | 267 | ||
| 157 | /** | 268 | /** |
| 158 | * notifier_call_chain - Call functions in a notifier chain | 269 | * blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain |
| 159 | * @n: Pointer to root pointer of notifier chain | 270 | * @nh: Pointer to head of the blocking notifier chain |
| 271 | * @n: Entry to remove from notifier chain | ||
| 272 | * | ||
| 273 | * Removes a notifier from a blocking notifier chain. | ||
| 274 | * Must be called from process context. | ||
| 275 | * | ||
| 276 | * Returns zero on success or %-ENOENT on failure. | ||
| 277 | */ | ||
| 278 | int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, | ||
| 279 | struct notifier_block *n) | ||
| 280 | { | ||
| 281 | int ret; | ||
| 282 | |||
| 283 | /* | ||
| 284 | * This code gets used during boot-up, when task switching is | ||
| 285 | * not yet working and interrupts must remain disabled. At | ||
| 286 | * such times we must not call down_write(). | ||
| 287 | */ | ||
| 288 | if (unlikely(system_state == SYSTEM_BOOTING)) | ||
| 289 | return notifier_chain_unregister(&nh->head, n); | ||
| 290 | |||
| 291 | down_write(&nh->rwsem); | ||
| 292 | ret = notifier_chain_unregister(&nh->head, n); | ||
| 293 | up_write(&nh->rwsem); | ||
| 294 | return ret; | ||
| 295 | } | ||
| 296 | |||
| 297 | EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister); | ||
| 298 | |||
| 299 | /** | ||
| 300 | * blocking_notifier_call_chain - Call functions in a blocking notifier chain | ||
| 301 | * @nh: Pointer to head of the blocking notifier chain | ||
| 160 | * @val: Value passed unmodified to notifier function | 302 | * @val: Value passed unmodified to notifier function |
| 161 | * @v: Pointer passed unmodified to notifier function | 303 | * @v: Pointer passed unmodified to notifier function |
| 162 | * | 304 | * |
| 163 | * Calls each function in a notifier chain in turn. | 305 | * Calls each function in a notifier chain in turn. The functions |
| 306 | * run in a process context, so they are allowed to block. | ||
| 164 | * | 307 | * |
| 165 | * If the return value of the notifier can be and'd | 308 | * If the return value of the notifier can be and'ed |
| 166 | * with %NOTIFY_STOP_MASK, then notifier_call_chain | 309 | * with %NOTIFY_STOP_MASK then blocking_notifier_call_chain |
| 167 | * will return immediately, with the return value of | 310 | * will return immediately, with the return value of |
| 168 | * the notifier function which halted execution. | 311 | * the notifier function which halted execution. |
| 169 | * Otherwise, the return value is the return value | 312 | * Otherwise the return value is the return value |
| 170 | * of the last notifier function called. | 313 | * of the last notifier function called. |
| 171 | */ | 314 | */ |
| 172 | 315 | ||
| 173 | int __kprobes notifier_call_chain(struct notifier_block **n, unsigned long val, void *v) | 316 | int blocking_notifier_call_chain(struct blocking_notifier_head *nh, |
| 317 | unsigned long val, void *v) | ||
| 174 | { | 318 | { |
| 175 | int ret=NOTIFY_DONE; | 319 | int ret; |
| 176 | struct notifier_block *nb = *n; | ||
| 177 | 320 | ||
| 178 | while(nb) | 321 | down_read(&nh->rwsem); |
| 179 | { | 322 | ret = notifier_call_chain(&nh->head, val, v); |
| 180 | ret=nb->notifier_call(nb,val,v); | 323 | up_read(&nh->rwsem); |
| 181 | if(ret&NOTIFY_STOP_MASK) | ||
| 182 | { | ||
| 183 | return ret; | ||
| 184 | } | ||
| 185 | nb=nb->next; | ||
| 186 | } | ||
| 187 | return ret; | 324 | return ret; |
| 188 | } | 325 | } |
| 189 | 326 | ||
| 190 | EXPORT_SYMBOL(notifier_call_chain); | 327 | EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); |
| 328 | |||
| 329 | /* | ||
| 330 | * Raw notifier chain routines. There is no protection; | ||
| 331 | * the caller must provide it. Use at your own risk! | ||
| 332 | */ | ||
| 333 | |||
| 334 | /** | ||
| 335 | * raw_notifier_chain_register - Add notifier to a raw notifier chain | ||
| 336 | * @nh: Pointer to head of the raw notifier chain | ||
| 337 | * @n: New entry in notifier chain | ||
| 338 | * | ||
| 339 | * Adds a notifier to a raw notifier chain. | ||
| 340 | * All locking must be provided by the caller. | ||
| 341 | * | ||
| 342 | * Currently always returns zero. | ||
| 343 | */ | ||
| 344 | |||
| 345 | int raw_notifier_chain_register(struct raw_notifier_head *nh, | ||
| 346 | struct notifier_block *n) | ||
| 347 | { | ||
| 348 | return notifier_chain_register(&nh->head, n); | ||
| 349 | } | ||
| 350 | |||
| 351 | EXPORT_SYMBOL_GPL(raw_notifier_chain_register); | ||
| 352 | |||
| 353 | /** | ||
| 354 | * raw_notifier_chain_unregister - Remove notifier from a raw notifier chain | ||
| 355 | * @nh: Pointer to head of the raw notifier chain | ||
| 356 | * @n: Entry to remove from notifier chain | ||
| 357 | * | ||
| 358 | * Removes a notifier from a raw notifier chain. | ||
| 359 | * All locking must be provided by the caller. | ||
| 360 | * | ||
| 361 | * Returns zero on success or %-ENOENT on failure. | ||
| 362 | */ | ||
| 363 | int raw_notifier_chain_unregister(struct raw_notifier_head *nh, | ||
| 364 | struct notifier_block *n) | ||
| 365 | { | ||
| 366 | return notifier_chain_unregister(&nh->head, n); | ||
| 367 | } | ||
| 368 | |||
| 369 | EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); | ||
| 370 | |||
| 371 | /** | ||
| 372 | * raw_notifier_call_chain - Call functions in a raw notifier chain | ||
| 373 | * @nh: Pointer to head of the raw notifier chain | ||
| 374 | * @val: Value passed unmodified to notifier function | ||
| 375 | * @v: Pointer passed unmodified to notifier function | ||
| 376 | * | ||
| 377 | * Calls each function in a notifier chain in turn. The functions | ||
| 378 | * run in an undefined context. | ||
| 379 | * All locking must be provided by the caller. | ||
| 380 | * | ||
| 381 | * If the return value of the notifier can be and'ed | ||
| 382 | * with %NOTIFY_STOP_MASK then raw_notifier_call_chain | ||
| 383 | * will return immediately, with the return value of | ||
| 384 | * the notifier function which halted execution. | ||
| 385 | * Otherwise the return value is the return value | ||
| 386 | * of the last notifier function called. | ||
| 387 | */ | ||
| 388 | |||
| 389 | int raw_notifier_call_chain(struct raw_notifier_head *nh, | ||
| 390 | unsigned long val, void *v) | ||
| 391 | { | ||
| 392 | return notifier_call_chain(&nh->head, val, v); | ||
| 393 | } | ||
| 394 | |||
| 395 | EXPORT_SYMBOL_GPL(raw_notifier_call_chain); | ||
| 191 | 396 | ||
| 192 | /** | 397 | /** |
| 193 | * register_reboot_notifier - Register function to be called at reboot time | 398 | * register_reboot_notifier - Register function to be called at reboot time |
| @@ -196,13 +401,13 @@ EXPORT_SYMBOL(notifier_call_chain); | |||
| 196 | * Registers a function with the list of functions | 401 | * Registers a function with the list of functions |
| 197 | * to be called at reboot time. | 402 | * to be called at reboot time. |
| 198 | * | 403 | * |
| 199 | * Currently always returns zero, as notifier_chain_register | 404 | * Currently always returns zero, as blocking_notifier_chain_register |
| 200 | * always returns zero. | 405 | * always returns zero. |
| 201 | */ | 406 | */ |
| 202 | 407 | ||
| 203 | int register_reboot_notifier(struct notifier_block * nb) | 408 | int register_reboot_notifier(struct notifier_block * nb) |
| 204 | { | 409 | { |
| 205 | return notifier_chain_register(&reboot_notifier_list, nb); | 410 | return blocking_notifier_chain_register(&reboot_notifier_list, nb); |
| 206 | } | 411 | } |
| 207 | 412 | ||
| 208 | EXPORT_SYMBOL(register_reboot_notifier); | 413 | EXPORT_SYMBOL(register_reboot_notifier); |
| @@ -219,7 +424,7 @@ EXPORT_SYMBOL(register_reboot_notifier); | |||
| 219 | 424 | ||
| 220 | int unregister_reboot_notifier(struct notifier_block * nb) | 425 | int unregister_reboot_notifier(struct notifier_block * nb) |
| 221 | { | 426 | { |
| 222 | return notifier_chain_unregister(&reboot_notifier_list, nb); | 427 | return blocking_notifier_chain_unregister(&reboot_notifier_list, nb); |
| 223 | } | 428 | } |
| 224 | 429 | ||
| 225 | EXPORT_SYMBOL(unregister_reboot_notifier); | 430 | EXPORT_SYMBOL(unregister_reboot_notifier); |
| @@ -380,7 +585,7 @@ EXPORT_SYMBOL_GPL(emergency_restart); | |||
| 380 | 585 | ||
| 381 | void kernel_restart_prepare(char *cmd) | 586 | void kernel_restart_prepare(char *cmd) |
| 382 | { | 587 | { |
| 383 | notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); | 588 | blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); |
| 384 | system_state = SYSTEM_RESTART; | 589 | system_state = SYSTEM_RESTART; |
| 385 | device_shutdown(); | 590 | device_shutdown(); |
| 386 | } | 591 | } |
| @@ -430,7 +635,7 @@ EXPORT_SYMBOL_GPL(kernel_kexec); | |||
| 430 | 635 | ||
| 431 | void kernel_shutdown_prepare(enum system_states state) | 636 | void kernel_shutdown_prepare(enum system_states state) |
| 432 | { | 637 | { |
| 433 | notifier_call_chain(&reboot_notifier_list, | 638 | blocking_notifier_call_chain(&reboot_notifier_list, |
| 434 | (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL); | 639 | (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL); |
| 435 | system_state = state; | 640 | system_state = state; |
| 436 | device_shutdown(); | 641 | device_shutdown(); |
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 1067090db6b1..d82864c4a617 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c | |||
| @@ -42,6 +42,10 @@ cond_syscall(sys_recvmsg); | |||
| 42 | cond_syscall(sys_socketcall); | 42 | cond_syscall(sys_socketcall); |
| 43 | cond_syscall(sys_futex); | 43 | cond_syscall(sys_futex); |
| 44 | cond_syscall(compat_sys_futex); | 44 | cond_syscall(compat_sys_futex); |
| 45 | cond_syscall(sys_set_robust_list); | ||
| 46 | cond_syscall(compat_sys_set_robust_list); | ||
| 47 | cond_syscall(sys_get_robust_list); | ||
| 48 | cond_syscall(compat_sys_get_robust_list); | ||
| 45 | cond_syscall(sys_epoll_create); | 49 | cond_syscall(sys_epoll_create); |
| 46 | cond_syscall(sys_epoll_ctl); | 50 | cond_syscall(sys_epoll_ctl); |
| 47 | cond_syscall(sys_epoll_wait); | 51 | cond_syscall(sys_epoll_wait); |
diff --git a/mm/Makefile b/mm/Makefile index f10c753dce6d..0b8f73f2ed16 100644 --- a/mm/Makefile +++ b/mm/Makefile | |||
| @@ -10,7 +10,7 @@ mmu-$(CONFIG_MMU) := fremap.o highmem.o madvise.o memory.o mincore.o \ | |||
| 10 | obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ | 10 | obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ |
| 11 | page_alloc.o page-writeback.o pdflush.o \ | 11 | page_alloc.o page-writeback.o pdflush.o \ |
| 12 | readahead.o swap.o truncate.o vmscan.o \ | 12 | readahead.o swap.o truncate.o vmscan.o \ |
| 13 | prio_tree.o util.o $(mmu-y) | 13 | prio_tree.o util.o mmzone.o $(mmu-y) |
| 14 | 14 | ||
| 15 | obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o | 15 | obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o |
| 16 | obj-$(CONFIG_HUGETLBFS) += hugetlb.o | 16 | obj-$(CONFIG_HUGETLBFS) += hugetlb.o |
diff --git a/mm/bootmem.c b/mm/bootmem.c index b55bd39fc5dd..d3e3bd2ffcea 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c | |||
| @@ -33,6 +33,7 @@ EXPORT_SYMBOL(max_pfn); /* This is exported so | |||
| 33 | * dma_get_required_mask(), which uses | 33 | * dma_get_required_mask(), which uses |
| 34 | * it, can be an inline function */ | 34 | * it, can be an inline function */ |
| 35 | 35 | ||
| 36 | static LIST_HEAD(bdata_list); | ||
| 36 | #ifdef CONFIG_CRASH_DUMP | 37 | #ifdef CONFIG_CRASH_DUMP |
| 37 | /* | 38 | /* |
| 38 | * If we have booted due to a crash, max_pfn will be a very low value. We need | 39 | * If we have booted due to a crash, max_pfn will be a very low value. We need |
| @@ -52,6 +53,27 @@ unsigned long __init bootmem_bootmap_pages (unsigned long pages) | |||
| 52 | 53 | ||
| 53 | return mapsize; | 54 | return mapsize; |
| 54 | } | 55 | } |
| 56 | /* | ||
| 57 | * link bdata in order | ||
| 58 | */ | ||
| 59 | static void link_bootmem(bootmem_data_t *bdata) | ||
| 60 | { | ||
| 61 | bootmem_data_t *ent; | ||
| 62 | if (list_empty(&bdata_list)) { | ||
| 63 | list_add(&bdata->list, &bdata_list); | ||
| 64 | return; | ||
| 65 | } | ||
| 66 | /* insert in order */ | ||
| 67 | list_for_each_entry(ent, &bdata_list, list) { | ||
| 68 | if (bdata->node_boot_start < ent->node_boot_start) { | ||
| 69 | list_add_tail(&bdata->list, &ent->list); | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | list_add_tail(&bdata->list, &bdata_list); | ||
| 74 | return; | ||
| 75 | } | ||
| 76 | |||
| 55 | 77 | ||
| 56 | /* | 78 | /* |
| 57 | * Called once to set up the allocator itself. | 79 | * Called once to set up the allocator itself. |
| @@ -62,13 +84,11 @@ static unsigned long __init init_bootmem_core (pg_data_t *pgdat, | |||
| 62 | bootmem_data_t *bdata = pgdat->bdata; | 84 | bootmem_data_t *bdata = pgdat->bdata; |
| 63 | unsigned long mapsize = ((end - start)+7)/8; | 85 | unsigned long mapsize = ((end - start)+7)/8; |
| 64 | 86 | ||
| 65 | pgdat->pgdat_next = pgdat_list; | ||
| 66 | pgdat_list = pgdat; | ||
| 67 | |||
| 68 | mapsize = ALIGN(mapsize, sizeof(long)); | 87 | mapsize = ALIGN(mapsize, sizeof(long)); |
| 69 | bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT); | 88 | bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT); |
| 70 | bdata->node_boot_start = (start << PAGE_SHIFT); | 89 | bdata->node_boot_start = (start << PAGE_SHIFT); |
| 71 | bdata->node_low_pfn = end; | 90 | bdata->node_low_pfn = end; |
| 91 | link_bootmem(bdata); | ||
| 72 | 92 | ||
| 73 | /* | 93 | /* |
| 74 | * Initially all pages are reserved - setup_arch() has to | 94 | * Initially all pages are reserved - setup_arch() has to |
| @@ -383,12 +403,11 @@ unsigned long __init free_all_bootmem (void) | |||
| 383 | 403 | ||
| 384 | void * __init __alloc_bootmem(unsigned long size, unsigned long align, unsigned long goal) | 404 | void * __init __alloc_bootmem(unsigned long size, unsigned long align, unsigned long goal) |
| 385 | { | 405 | { |
| 386 | pg_data_t *pgdat = pgdat_list; | 406 | bootmem_data_t *bdata; |
| 387 | void *ptr; | 407 | void *ptr; |
| 388 | 408 | ||
| 389 | for_each_pgdat(pgdat) | 409 | list_for_each_entry(bdata, &bdata_list, list) |
| 390 | if ((ptr = __alloc_bootmem_core(pgdat->bdata, size, | 410 | if ((ptr = __alloc_bootmem_core(bdata, size, align, goal, 0))) |
| 391 | align, goal, 0))) | ||
| 392 | return(ptr); | 411 | return(ptr); |
| 393 | 412 | ||
| 394 | /* | 413 | /* |
| @@ -416,11 +435,11 @@ void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, unsigne | |||
| 416 | 435 | ||
| 417 | void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, unsigned long goal) | 436 | void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, unsigned long goal) |
| 418 | { | 437 | { |
| 419 | pg_data_t *pgdat = pgdat_list; | 438 | bootmem_data_t *bdata; |
| 420 | void *ptr; | 439 | void *ptr; |
| 421 | 440 | ||
| 422 | for_each_pgdat(pgdat) | 441 | list_for_each_entry(bdata, &bdata_list, list) |
| 423 | if ((ptr = __alloc_bootmem_core(pgdat->bdata, size, | 442 | if ((ptr = __alloc_bootmem_core(bdata, size, |
| 424 | align, goal, LOW32LIMIT))) | 443 | align, goal, LOW32LIMIT))) |
| 425 | return(ptr); | 444 | return(ptr); |
| 426 | 445 | ||
diff --git a/mm/mmzone.c b/mm/mmzone.c new file mode 100644 index 000000000000..b022370e612e --- /dev/null +++ b/mm/mmzone.c | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | /* | ||
| 2 | * linux/mm/mmzone.c | ||
| 3 | * | ||
| 4 | * management codes for pgdats and zones. | ||
| 5 | */ | ||
| 6 | |||
| 7 | |||
| 8 | #include <linux/config.h> | ||
| 9 | #include <linux/stddef.h> | ||
| 10 | #include <linux/mmzone.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | |||
| 13 | struct pglist_data *first_online_pgdat(void) | ||
| 14 | { | ||
| 15 | return NODE_DATA(first_online_node); | ||
| 16 | } | ||
| 17 | |||
| 18 | EXPORT_SYMBOL(first_online_pgdat); | ||
| 19 | |||
| 20 | struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) | ||
| 21 | { | ||
| 22 | int nid = next_online_node(pgdat->node_id); | ||
| 23 | |||
| 24 | if (nid == MAX_NUMNODES) | ||
| 25 | return NULL; | ||
| 26 | return NODE_DATA(nid); | ||
| 27 | } | ||
| 28 | EXPORT_SYMBOL(next_online_pgdat); | ||
| 29 | |||
| 30 | |||
| 31 | /* | ||
| 32 | * next_zone - helper magic for for_each_zone() | ||
| 33 | */ | ||
| 34 | struct zone *next_zone(struct zone *zone) | ||
| 35 | { | ||
| 36 | pg_data_t *pgdat = zone->zone_pgdat; | ||
| 37 | |||
| 38 | if (zone < pgdat->node_zones + MAX_NR_ZONES - 1) | ||
| 39 | zone++; | ||
| 40 | else { | ||
| 41 | pgdat = next_online_pgdat(pgdat); | ||
| 42 | if (pgdat) | ||
| 43 | zone = pgdat->node_zones; | ||
| 44 | else | ||
| 45 | zone = NULL; | ||
| 46 | } | ||
| 47 | return zone; | ||
| 48 | } | ||
| 49 | EXPORT_SYMBOL(next_zone); | ||
| 50 | |||
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 338a02bb004d..dc523a1f270d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
| @@ -49,7 +49,6 @@ nodemask_t node_online_map __read_mostly = { { [0] = 1UL } }; | |||
| 49 | EXPORT_SYMBOL(node_online_map); | 49 | EXPORT_SYMBOL(node_online_map); |
| 50 | nodemask_t node_possible_map __read_mostly = NODE_MASK_ALL; | 50 | nodemask_t node_possible_map __read_mostly = NODE_MASK_ALL; |
| 51 | EXPORT_SYMBOL(node_possible_map); | 51 | EXPORT_SYMBOL(node_possible_map); |
| 52 | struct pglist_data *pgdat_list __read_mostly; | ||
| 53 | unsigned long totalram_pages __read_mostly; | 52 | unsigned long totalram_pages __read_mostly; |
| 54 | unsigned long totalhigh_pages __read_mostly; | 53 | unsigned long totalhigh_pages __read_mostly; |
| 55 | long nr_swap_pages; | 54 | long nr_swap_pages; |
| @@ -1201,7 +1200,7 @@ unsigned int nr_free_highpages (void) | |||
| 1201 | pg_data_t *pgdat; | 1200 | pg_data_t *pgdat; |
| 1202 | unsigned int pages = 0; | 1201 | unsigned int pages = 0; |
| 1203 | 1202 | ||
| 1204 | for_each_pgdat(pgdat) | 1203 | for_each_online_pgdat(pgdat) |
| 1205 | pages += pgdat->node_zones[ZONE_HIGHMEM].free_pages; | 1204 | pages += pgdat->node_zones[ZONE_HIGHMEM].free_pages; |
| 1206 | 1205 | ||
| 1207 | return pages; | 1206 | return pages; |
| @@ -1343,7 +1342,7 @@ void get_zone_counts(unsigned long *active, | |||
| 1343 | *active = 0; | 1342 | *active = 0; |
| 1344 | *inactive = 0; | 1343 | *inactive = 0; |
| 1345 | *free = 0; | 1344 | *free = 0; |
| 1346 | for_each_pgdat(pgdat) { | 1345 | for_each_online_pgdat(pgdat) { |
| 1347 | unsigned long l, m, n; | 1346 | unsigned long l, m, n; |
| 1348 | __get_zone_counts(&l, &m, &n, pgdat); | 1347 | __get_zone_counts(&l, &m, &n, pgdat); |
| 1349 | *active += l; | 1348 | *active += l; |
| @@ -2042,7 +2041,6 @@ static __meminit void init_currently_empty_zone(struct zone *zone, | |||
| 2042 | zone_wait_table_init(zone, size); | 2041 | zone_wait_table_init(zone, size); |
| 2043 | pgdat->nr_zones = zone_idx(zone) + 1; | 2042 | pgdat->nr_zones = zone_idx(zone) + 1; |
| 2044 | 2043 | ||
| 2045 | zone->zone_mem_map = pfn_to_page(zone_start_pfn); | ||
| 2046 | zone->zone_start_pfn = zone_start_pfn; | 2044 | zone->zone_start_pfn = zone_start_pfn; |
| 2047 | 2045 | ||
| 2048 | memmap_init(size, pgdat->node_id, zone_idx(zone), zone_start_pfn); | 2046 | memmap_init(size, pgdat->node_id, zone_idx(zone), zone_start_pfn); |
| @@ -2170,8 +2168,9 @@ static void *frag_start(struct seq_file *m, loff_t *pos) | |||
| 2170 | { | 2168 | { |
| 2171 | pg_data_t *pgdat; | 2169 | pg_data_t *pgdat; |
| 2172 | loff_t node = *pos; | 2170 | loff_t node = *pos; |
| 2173 | 2171 | for (pgdat = first_online_pgdat(); | |
| 2174 | for (pgdat = pgdat_list; pgdat && node; pgdat = pgdat->pgdat_next) | 2172 | pgdat && node; |
| 2173 | pgdat = next_online_pgdat(pgdat)) | ||
| 2175 | --node; | 2174 | --node; |
| 2176 | 2175 | ||
| 2177 | return pgdat; | 2176 | return pgdat; |
| @@ -2182,7 +2181,7 @@ static void *frag_next(struct seq_file *m, void *arg, loff_t *pos) | |||
| 2182 | pg_data_t *pgdat = (pg_data_t *)arg; | 2181 | pg_data_t *pgdat = (pg_data_t *)arg; |
| 2183 | 2182 | ||
| 2184 | (*pos)++; | 2183 | (*pos)++; |
| 2185 | return pgdat->pgdat_next; | 2184 | return next_online_pgdat(pgdat); |
| 2186 | } | 2185 | } |
| 2187 | 2186 | ||
| 2188 | static void frag_stop(struct seq_file *m, void *arg) | 2187 | static void frag_stop(struct seq_file *m, void *arg) |
| @@ -2483,7 +2482,7 @@ static void setup_per_zone_lowmem_reserve(void) | |||
| 2483 | struct pglist_data *pgdat; | 2482 | struct pglist_data *pgdat; |
| 2484 | int j, idx; | 2483 | int j, idx; |
| 2485 | 2484 | ||
| 2486 | for_each_pgdat(pgdat) { | 2485 | for_each_online_pgdat(pgdat) { |
| 2487 | for (j = 0; j < MAX_NR_ZONES; j++) { | 2486 | for (j = 0; j < MAX_NR_ZONES; j++) { |
| 2488 | struct zone *zone = pgdat->node_zones + j; | 2487 | struct zone *zone = pgdat->node_zones + j; |
| 2489 | unsigned long present_pages = zone->present_pages; | 2488 | unsigned long present_pages = zone->present_pages; |
| @@ -2745,3 +2744,44 @@ void *__init alloc_large_system_hash(const char *tablename, | |||
| 2745 | 2744 | ||
| 2746 | return table; | 2745 | return table; |
| 2747 | } | 2746 | } |
| 2747 | |||
| 2748 | #ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE | ||
| 2749 | /* | ||
| 2750 | * pfn <-> page translation. out-of-line version. | ||
| 2751 | * (see asm-generic/memory_model.h) | ||
| 2752 | */ | ||
| 2753 | #if defined(CONFIG_FLATMEM) | ||
| 2754 | struct page *pfn_to_page(unsigned long pfn) | ||
| 2755 | { | ||
| 2756 | return mem_map + (pfn - ARCH_PFN_OFFSET); | ||
| 2757 | } | ||
| 2758 | unsigned long page_to_pfn(struct page *page) | ||
| 2759 | { | ||
| 2760 | return (page - mem_map) + ARCH_PFN_OFFSET; | ||
| 2761 | } | ||
| 2762 | #elif defined(CONFIG_DISCONTIGMEM) | ||
| 2763 | struct page *pfn_to_page(unsigned long pfn) | ||
| 2764 | { | ||
| 2765 | int nid = arch_pfn_to_nid(pfn); | ||
| 2766 | return NODE_DATA(nid)->node_mem_map + arch_local_page_offset(pfn,nid); | ||
| 2767 | } | ||
| 2768 | unsigned long page_to_pfn(struct page *page) | ||
| 2769 | { | ||
| 2770 | struct pglist_data *pgdat = NODE_DATA(page_to_nid(page)); | ||
| 2771 | return (page - pgdat->node_mem_map) + pgdat->node_start_pfn; | ||
| 2772 | } | ||
| 2773 | #elif defined(CONFIG_SPARSEMEM) | ||
| 2774 | struct page *pfn_to_page(unsigned long pfn) | ||
| 2775 | { | ||
| 2776 | return __section_mem_map_addr(__pfn_to_section(pfn)) + pfn; | ||
| 2777 | } | ||
| 2778 | |||
| 2779 | unsigned long page_to_pfn(struct page *page) | ||
| 2780 | { | ||
| 2781 | long section_id = page_to_section(page); | ||
| 2782 | return page - __section_mem_map_addr(__nr_to_section(section_id)); | ||
| 2783 | } | ||
| 2784 | #endif /* CONFIG_FLATMEM/DISCONTIGMME/SPARSEMEM */ | ||
| 2785 | EXPORT_SYMBOL(pfn_to_page); | ||
| 2786 | EXPORT_SYMBOL(page_to_pfn); | ||
| 2787 | #endif /* CONFIG_OUT_OF_LINE_PFN_TO_PAGE */ | ||
diff --git a/mm/vmscan.c b/mm/vmscan.c index 78865c849f8f..acdf001d6941 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
| @@ -1305,7 +1305,7 @@ unsigned long shrink_all_memory(unsigned long nr_pages) | |||
| 1305 | 1305 | ||
| 1306 | current->reclaim_state = &reclaim_state; | 1306 | current->reclaim_state = &reclaim_state; |
| 1307 | repeat: | 1307 | repeat: |
| 1308 | for_each_pgdat(pgdat) { | 1308 | for_each_online_pgdat(pgdat) { |
| 1309 | unsigned long freed; | 1309 | unsigned long freed; |
| 1310 | 1310 | ||
| 1311 | freed = balance_pgdat(pgdat, nr_to_free, 0); | 1311 | freed = balance_pgdat(pgdat, nr_to_free, 0); |
| @@ -1335,7 +1335,7 @@ static int __devinit cpu_callback(struct notifier_block *nfb, | |||
| 1335 | cpumask_t mask; | 1335 | cpumask_t mask; |
| 1336 | 1336 | ||
| 1337 | if (action == CPU_ONLINE) { | 1337 | if (action == CPU_ONLINE) { |
| 1338 | for_each_pgdat(pgdat) { | 1338 | for_each_online_pgdat(pgdat) { |
| 1339 | mask = node_to_cpumask(pgdat->node_id); | 1339 | mask = node_to_cpumask(pgdat->node_id); |
| 1340 | if (any_online_cpu(mask) != NR_CPUS) | 1340 | if (any_online_cpu(mask) != NR_CPUS) |
| 1341 | /* One of our CPUs online: restore mask */ | 1341 | /* One of our CPUs online: restore mask */ |
| @@ -1351,7 +1351,7 @@ static int __init kswapd_init(void) | |||
| 1351 | pg_data_t *pgdat; | 1351 | pg_data_t *pgdat; |
| 1352 | 1352 | ||
| 1353 | swap_setup(); | 1353 | swap_setup(); |
| 1354 | for_each_pgdat(pgdat) { | 1354 | for_each_online_pgdat(pgdat) { |
| 1355 | pid_t pid; | 1355 | pid_t pid; |
| 1356 | 1356 | ||
| 1357 | pid = kernel_thread(kswapd, pgdat, CLONE_KERNEL); | 1357 | pid = kernel_thread(kswapd, pgdat, CLONE_KERNEL); |
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9106354c781e..a49a6975092d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c | |||
| @@ -73,23 +73,23 @@ DEFINE_RWLOCK(hci_cb_list_lock); | |||
| 73 | struct hci_proto *hci_proto[HCI_MAX_PROTO]; | 73 | struct hci_proto *hci_proto[HCI_MAX_PROTO]; |
| 74 | 74 | ||
| 75 | /* HCI notifiers list */ | 75 | /* HCI notifiers list */ |
| 76 | static struct notifier_block *hci_notifier; | 76 | static ATOMIC_NOTIFIER_HEAD(hci_notifier); |
| 77 | 77 | ||
| 78 | /* ---- HCI notifications ---- */ | 78 | /* ---- HCI notifications ---- */ |
| 79 | 79 | ||
| 80 | int hci_register_notifier(struct notifier_block *nb) | 80 | int hci_register_notifier(struct notifier_block *nb) |
| 81 | { | 81 | { |
| 82 | return notifier_chain_register(&hci_notifier, nb); | 82 | return atomic_notifier_chain_register(&hci_notifier, nb); |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | int hci_unregister_notifier(struct notifier_block *nb) | 85 | int hci_unregister_notifier(struct notifier_block *nb) |
| 86 | { | 86 | { |
| 87 | return notifier_chain_unregister(&hci_notifier, nb); | 87 | return atomic_notifier_chain_unregister(&hci_notifier, nb); |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | static void hci_notify(struct hci_dev *hdev, int event) | 90 | static void hci_notify(struct hci_dev *hdev, int event) |
| 91 | { | 91 | { |
| 92 | notifier_call_chain(&hci_notifier, event, hdev); | 92 | atomic_notifier_call_chain(&hci_notifier, event, hdev); |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | /* ---- HCI requests ---- */ | 95 | /* ---- HCI requests ---- */ |
diff --git a/net/core/dev.c b/net/core/dev.c index 8e1dc3051222..a3ab11f34153 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
| @@ -193,7 +193,7 @@ static inline struct hlist_head *dev_index_hash(int ifindex) | |||
| 193 | * Our notifier list | 193 | * Our notifier list |
| 194 | */ | 194 | */ |
| 195 | 195 | ||
| 196 | static struct notifier_block *netdev_chain; | 196 | static BLOCKING_NOTIFIER_HEAD(netdev_chain); |
| 197 | 197 | ||
| 198 | /* | 198 | /* |
| 199 | * Device drivers call our routines to queue packets here. We empty the | 199 | * Device drivers call our routines to queue packets here. We empty the |
| @@ -736,7 +736,8 @@ int dev_change_name(struct net_device *dev, char *newname) | |||
| 736 | if (!err) { | 736 | if (!err) { |
| 737 | hlist_del(&dev->name_hlist); | 737 | hlist_del(&dev->name_hlist); |
| 738 | hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); | 738 | hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); |
| 739 | notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); | 739 | blocking_notifier_call_chain(&netdev_chain, |
| 740 | NETDEV_CHANGENAME, dev); | ||
| 740 | } | 741 | } |
| 741 | 742 | ||
| 742 | return err; | 743 | return err; |
| @@ -750,7 +751,7 @@ int dev_change_name(struct net_device *dev, char *newname) | |||
| 750 | */ | 751 | */ |
| 751 | void netdev_features_change(struct net_device *dev) | 752 | void netdev_features_change(struct net_device *dev) |
| 752 | { | 753 | { |
| 753 | notifier_call_chain(&netdev_chain, NETDEV_FEAT_CHANGE, dev); | 754 | blocking_notifier_call_chain(&netdev_chain, NETDEV_FEAT_CHANGE, dev); |
| 754 | } | 755 | } |
| 755 | EXPORT_SYMBOL(netdev_features_change); | 756 | EXPORT_SYMBOL(netdev_features_change); |
| 756 | 757 | ||
| @@ -765,7 +766,8 @@ EXPORT_SYMBOL(netdev_features_change); | |||
| 765 | void netdev_state_change(struct net_device *dev) | 766 | void netdev_state_change(struct net_device *dev) |
| 766 | { | 767 | { |
| 767 | if (dev->flags & IFF_UP) { | 768 | if (dev->flags & IFF_UP) { |
| 768 | notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev); | 769 | blocking_notifier_call_chain(&netdev_chain, |
| 770 | NETDEV_CHANGE, dev); | ||
| 769 | rtmsg_ifinfo(RTM_NEWLINK, dev, 0); | 771 | rtmsg_ifinfo(RTM_NEWLINK, dev, 0); |
| 770 | } | 772 | } |
| 771 | } | 773 | } |
| @@ -862,7 +864,7 @@ int dev_open(struct net_device *dev) | |||
| 862 | /* | 864 | /* |
| 863 | * ... and announce new interface. | 865 | * ... and announce new interface. |
| 864 | */ | 866 | */ |
| 865 | notifier_call_chain(&netdev_chain, NETDEV_UP, dev); | 867 | blocking_notifier_call_chain(&netdev_chain, NETDEV_UP, dev); |
| 866 | } | 868 | } |
| 867 | return ret; | 869 | return ret; |
| 868 | } | 870 | } |
| @@ -885,7 +887,7 @@ int dev_close(struct net_device *dev) | |||
| 885 | * Tell people we are going down, so that they can | 887 | * Tell people we are going down, so that they can |
| 886 | * prepare to death, when device is still operating. | 888 | * prepare to death, when device is still operating. |
| 887 | */ | 889 | */ |
| 888 | notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev); | 890 | blocking_notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev); |
| 889 | 891 | ||
| 890 | dev_deactivate(dev); | 892 | dev_deactivate(dev); |
| 891 | 893 | ||
| @@ -922,7 +924,7 @@ int dev_close(struct net_device *dev) | |||
| 922 | /* | 924 | /* |
| 923 | * Tell people we are down | 925 | * Tell people we are down |
| 924 | */ | 926 | */ |
| 925 | notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); | 927 | blocking_notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); |
| 926 | 928 | ||
| 927 | return 0; | 929 | return 0; |
| 928 | } | 930 | } |
| @@ -953,7 +955,7 @@ int register_netdevice_notifier(struct notifier_block *nb) | |||
| 953 | int err; | 955 | int err; |
| 954 | 956 | ||
| 955 | rtnl_lock(); | 957 | rtnl_lock(); |
| 956 | err = notifier_chain_register(&netdev_chain, nb); | 958 | err = blocking_notifier_chain_register(&netdev_chain, nb); |
| 957 | if (!err) { | 959 | if (!err) { |
| 958 | for (dev = dev_base; dev; dev = dev->next) { | 960 | for (dev = dev_base; dev; dev = dev->next) { |
| 959 | nb->notifier_call(nb, NETDEV_REGISTER, dev); | 961 | nb->notifier_call(nb, NETDEV_REGISTER, dev); |
| @@ -981,7 +983,7 @@ int unregister_netdevice_notifier(struct notifier_block *nb) | |||
| 981 | int err; | 983 | int err; |
| 982 | 984 | ||
| 983 | rtnl_lock(); | 985 | rtnl_lock(); |
| 984 | err = notifier_chain_unregister(&netdev_chain, nb); | 986 | err = blocking_notifier_chain_unregister(&netdev_chain, nb); |
| 985 | rtnl_unlock(); | 987 | rtnl_unlock(); |
| 986 | return err; | 988 | return err; |
| 987 | } | 989 | } |
| @@ -992,12 +994,12 @@ int unregister_netdevice_notifier(struct notifier_block *nb) | |||
| 992 | * @v: pointer passed unmodified to notifier function | 994 | * @v: pointer passed unmodified to notifier function |
| 993 | * | 995 | * |
| 994 | * Call all network notifier blocks. Parameters and return value | 996 | * Call all network notifier blocks. Parameters and return value |
| 995 | * are as for notifier_call_chain(). | 997 | * are as for blocking_notifier_call_chain(). |
| 996 | */ | 998 | */ |
| 997 | 999 | ||
| 998 | int call_netdevice_notifiers(unsigned long val, void *v) | 1000 | int call_netdevice_notifiers(unsigned long val, void *v) |
| 999 | { | 1001 | { |
| 1000 | return notifier_call_chain(&netdev_chain, val, v); | 1002 | return blocking_notifier_call_chain(&netdev_chain, val, v); |
| 1001 | } | 1003 | } |
| 1002 | 1004 | ||
| 1003 | /* When > 0 there are consumers of rx skb time stamps */ | 1005 | /* When > 0 there are consumers of rx skb time stamps */ |
| @@ -2242,7 +2244,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags) | |||
| 2242 | if (dev->flags & IFF_UP && | 2244 | if (dev->flags & IFF_UP && |
| 2243 | ((old_flags ^ dev->flags) &~ (IFF_UP | IFF_PROMISC | IFF_ALLMULTI | | 2245 | ((old_flags ^ dev->flags) &~ (IFF_UP | IFF_PROMISC | IFF_ALLMULTI | |
| 2244 | IFF_VOLATILE))) | 2246 | IFF_VOLATILE))) |
| 2245 | notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev); | 2247 | blocking_notifier_call_chain(&netdev_chain, |
| 2248 | NETDEV_CHANGE, dev); | ||
| 2246 | 2249 | ||
| 2247 | if ((flags ^ dev->gflags) & IFF_PROMISC) { | 2250 | if ((flags ^ dev->gflags) & IFF_PROMISC) { |
| 2248 | int inc = (flags & IFF_PROMISC) ? +1 : -1; | 2251 | int inc = (flags & IFF_PROMISC) ? +1 : -1; |
| @@ -2286,8 +2289,8 @@ int dev_set_mtu(struct net_device *dev, int new_mtu) | |||
| 2286 | else | 2289 | else |
| 2287 | dev->mtu = new_mtu; | 2290 | dev->mtu = new_mtu; |
| 2288 | if (!err && dev->flags & IFF_UP) | 2291 | if (!err && dev->flags & IFF_UP) |
| 2289 | notifier_call_chain(&netdev_chain, | 2292 | blocking_notifier_call_chain(&netdev_chain, |
| 2290 | NETDEV_CHANGEMTU, dev); | 2293 | NETDEV_CHANGEMTU, dev); |
| 2291 | return err; | 2294 | return err; |
| 2292 | } | 2295 | } |
| 2293 | 2296 | ||
| @@ -2303,7 +2306,8 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa) | |||
| 2303 | return -ENODEV; | 2306 | return -ENODEV; |
| 2304 | err = dev->set_mac_address(dev, sa); | 2307 | err = dev->set_mac_address(dev, sa); |
| 2305 | if (!err) | 2308 | if (!err) |
| 2306 | notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); | 2309 | blocking_notifier_call_chain(&netdev_chain, |
| 2310 | NETDEV_CHANGEADDR, dev); | ||
| 2307 | return err; | 2311 | return err; |
| 2308 | } | 2312 | } |
| 2309 | 2313 | ||
| @@ -2359,7 +2363,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) | |||
| 2359 | return -EINVAL; | 2363 | return -EINVAL; |
| 2360 | memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, | 2364 | memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, |
| 2361 | min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); | 2365 | min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); |
| 2362 | notifier_call_chain(&netdev_chain, | 2366 | blocking_notifier_call_chain(&netdev_chain, |
| 2363 | NETDEV_CHANGEADDR, dev); | 2367 | NETDEV_CHANGEADDR, dev); |
| 2364 | return 0; | 2368 | return 0; |
| 2365 | 2369 | ||
| @@ -2813,7 +2817,7 @@ int register_netdevice(struct net_device *dev) | |||
| 2813 | write_unlock_bh(&dev_base_lock); | 2817 | write_unlock_bh(&dev_base_lock); |
| 2814 | 2818 | ||
| 2815 | /* Notify protocols, that a new device appeared. */ | 2819 | /* Notify protocols, that a new device appeared. */ |
| 2816 | notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); | 2820 | blocking_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); |
| 2817 | 2821 | ||
| 2818 | /* Finish registration after unlock */ | 2822 | /* Finish registration after unlock */ |
| 2819 | net_set_todo(dev); | 2823 | net_set_todo(dev); |
| @@ -2892,7 +2896,7 @@ static void netdev_wait_allrefs(struct net_device *dev) | |||
| 2892 | rtnl_lock(); | 2896 | rtnl_lock(); |
| 2893 | 2897 | ||
| 2894 | /* Rebroadcast unregister notification */ | 2898 | /* Rebroadcast unregister notification */ |
| 2895 | notifier_call_chain(&netdev_chain, | 2899 | blocking_notifier_call_chain(&netdev_chain, |
| 2896 | NETDEV_UNREGISTER, dev); | 2900 | NETDEV_UNREGISTER, dev); |
| 2897 | 2901 | ||
| 2898 | if (test_bit(__LINK_STATE_LINKWATCH_PENDING, | 2902 | if (test_bit(__LINK_STATE_LINKWATCH_PENDING, |
| @@ -3148,7 +3152,7 @@ int unregister_netdevice(struct net_device *dev) | |||
| 3148 | /* Notify protocols, that we are about to destroy | 3152 | /* Notify protocols, that we are about to destroy |
| 3149 | this device. They should clean all the things. | 3153 | this device. They should clean all the things. |
| 3150 | */ | 3154 | */ |
| 3151 | notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev); | 3155 | blocking_notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev); |
| 3152 | 3156 | ||
| 3153 | /* | 3157 | /* |
| 3154 | * Flush the multicast chain | 3158 | * Flush the multicast chain |
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index cc7b9d9255ef..d2ae9893ca17 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c | |||
| @@ -68,7 +68,7 @@ __le16 decnet_address = 0; | |||
| 68 | 68 | ||
| 69 | static DEFINE_RWLOCK(dndev_lock); | 69 | static DEFINE_RWLOCK(dndev_lock); |
| 70 | static struct net_device *decnet_default_device; | 70 | static struct net_device *decnet_default_device; |
| 71 | static struct notifier_block *dnaddr_chain; | 71 | static BLOCKING_NOTIFIER_HEAD(dnaddr_chain); |
| 72 | 72 | ||
| 73 | static struct dn_dev *dn_dev_create(struct net_device *dev, int *err); | 73 | static struct dn_dev *dn_dev_create(struct net_device *dev, int *err); |
| 74 | static void dn_dev_delete(struct net_device *dev); | 74 | static void dn_dev_delete(struct net_device *dev); |
| @@ -446,7 +446,7 @@ static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int de | |||
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | rtmsg_ifa(RTM_DELADDR, ifa1); | 448 | rtmsg_ifa(RTM_DELADDR, ifa1); |
| 449 | notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1); | 449 | blocking_notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1); |
| 450 | if (destroy) { | 450 | if (destroy) { |
| 451 | dn_dev_free_ifa(ifa1); | 451 | dn_dev_free_ifa(ifa1); |
| 452 | 452 | ||
| @@ -481,7 +481,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) | |||
| 481 | dn_db->ifa_list = ifa; | 481 | dn_db->ifa_list = ifa; |
| 482 | 482 | ||
| 483 | rtmsg_ifa(RTM_NEWADDR, ifa); | 483 | rtmsg_ifa(RTM_NEWADDR, ifa); |
| 484 | notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa); | 484 | blocking_notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa); |
| 485 | 485 | ||
| 486 | return 0; | 486 | return 0; |
| 487 | } | 487 | } |
| @@ -1285,12 +1285,12 @@ void dn_dev_devices_on(void) | |||
| 1285 | 1285 | ||
| 1286 | int register_dnaddr_notifier(struct notifier_block *nb) | 1286 | int register_dnaddr_notifier(struct notifier_block *nb) |
| 1287 | { | 1287 | { |
| 1288 | return notifier_chain_register(&dnaddr_chain, nb); | 1288 | return blocking_notifier_chain_register(&dnaddr_chain, nb); |
| 1289 | } | 1289 | } |
| 1290 | 1290 | ||
| 1291 | int unregister_dnaddr_notifier(struct notifier_block *nb) | 1291 | int unregister_dnaddr_notifier(struct notifier_block *nb) |
| 1292 | { | 1292 | { |
| 1293 | return notifier_chain_unregister(&dnaddr_chain, nb); | 1293 | return blocking_notifier_chain_unregister(&dnaddr_chain, nb); |
| 1294 | } | 1294 | } |
| 1295 | 1295 | ||
| 1296 | #ifdef CONFIG_PROC_FS | 1296 | #ifdef CONFIG_PROC_FS |
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 44fdf1413e2c..81c2f7885292 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
| @@ -81,7 +81,7 @@ static struct ipv4_devconf ipv4_devconf_dflt = { | |||
| 81 | 81 | ||
| 82 | static void rtmsg_ifa(int event, struct in_ifaddr *); | 82 | static void rtmsg_ifa(int event, struct in_ifaddr *); |
| 83 | 83 | ||
| 84 | static struct notifier_block *inetaddr_chain; | 84 | static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); |
| 85 | static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | 85 | static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, |
| 86 | int destroy); | 86 | int destroy); |
| 87 | #ifdef CONFIG_SYSCTL | 87 | #ifdef CONFIG_SYSCTL |
| @@ -267,7 +267,8 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
| 267 | *ifap1 = ifa->ifa_next; | 267 | *ifap1 = ifa->ifa_next; |
| 268 | 268 | ||
| 269 | rtmsg_ifa(RTM_DELADDR, ifa); | 269 | rtmsg_ifa(RTM_DELADDR, ifa); |
| 270 | notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); | 270 | blocking_notifier_call_chain(&inetaddr_chain, |
| 271 | NETDEV_DOWN, ifa); | ||
| 271 | inet_free_ifa(ifa); | 272 | inet_free_ifa(ifa); |
| 272 | } else { | 273 | } else { |
| 273 | promote = ifa; | 274 | promote = ifa; |
| @@ -291,7 +292,7 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
| 291 | So that, this order is correct. | 292 | So that, this order is correct. |
| 292 | */ | 293 | */ |
| 293 | rtmsg_ifa(RTM_DELADDR, ifa1); | 294 | rtmsg_ifa(RTM_DELADDR, ifa1); |
| 294 | notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); | 295 | blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); |
| 295 | 296 | ||
| 296 | if (promote) { | 297 | if (promote) { |
| 297 | 298 | ||
| @@ -303,7 +304,8 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
| 303 | 304 | ||
| 304 | promote->ifa_flags &= ~IFA_F_SECONDARY; | 305 | promote->ifa_flags &= ~IFA_F_SECONDARY; |
| 305 | rtmsg_ifa(RTM_NEWADDR, promote); | 306 | rtmsg_ifa(RTM_NEWADDR, promote); |
| 306 | notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); | 307 | blocking_notifier_call_chain(&inetaddr_chain, |
| 308 | NETDEV_UP, promote); | ||
| 307 | for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { | 309 | for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { |
| 308 | if (ifa1->ifa_mask != ifa->ifa_mask || | 310 | if (ifa1->ifa_mask != ifa->ifa_mask || |
| 309 | !inet_ifa_match(ifa1->ifa_address, ifa)) | 311 | !inet_ifa_match(ifa1->ifa_address, ifa)) |
| @@ -366,7 +368,7 @@ static int inet_insert_ifa(struct in_ifaddr *ifa) | |||
| 366 | Notifier will trigger FIB update, so that | 368 | Notifier will trigger FIB update, so that |
| 367 | listeners of netlink will know about new ifaddr */ | 369 | listeners of netlink will know about new ifaddr */ |
| 368 | rtmsg_ifa(RTM_NEWADDR, ifa); | 370 | rtmsg_ifa(RTM_NEWADDR, ifa); |
| 369 | notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); | 371 | blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); |
| 370 | 372 | ||
| 371 | return 0; | 373 | return 0; |
| 372 | } | 374 | } |
| @@ -938,12 +940,12 @@ u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scop | |||
| 938 | 940 | ||
| 939 | int register_inetaddr_notifier(struct notifier_block *nb) | 941 | int register_inetaddr_notifier(struct notifier_block *nb) |
| 940 | { | 942 | { |
| 941 | return notifier_chain_register(&inetaddr_chain, nb); | 943 | return blocking_notifier_chain_register(&inetaddr_chain, nb); |
| 942 | } | 944 | } |
| 943 | 945 | ||
| 944 | int unregister_inetaddr_notifier(struct notifier_block *nb) | 946 | int unregister_inetaddr_notifier(struct notifier_block *nb) |
| 945 | { | 947 | { |
| 946 | return notifier_chain_unregister(&inetaddr_chain, nb); | 948 | return blocking_notifier_chain_unregister(&inetaddr_chain, nb); |
| 947 | } | 949 | } |
| 948 | 950 | ||
| 949 | /* Rename ifa_labels for a device name change. Make some effort to preserve existing | 951 | /* Rename ifa_labels for a device name change. Make some effort to preserve existing |
diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c index 9e34034729a6..ceaabc18202b 100644 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ b/net/ipv4/netfilter/ip_conntrack_core.c | |||
| @@ -80,8 +80,8 @@ static int ip_conntrack_vmalloc; | |||
| 80 | static unsigned int ip_conntrack_next_id; | 80 | static unsigned int ip_conntrack_next_id; |
| 81 | static unsigned int ip_conntrack_expect_next_id; | 81 | static unsigned int ip_conntrack_expect_next_id; |
| 82 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | 82 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS |
| 83 | struct notifier_block *ip_conntrack_chain; | 83 | ATOMIC_NOTIFIER_HEAD(ip_conntrack_chain); |
| 84 | struct notifier_block *ip_conntrack_expect_chain; | 84 | ATOMIC_NOTIFIER_HEAD(ip_conntrack_expect_chain); |
| 85 | 85 | ||
| 86 | DEFINE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache); | 86 | DEFINE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache); |
| 87 | 87 | ||
| @@ -92,7 +92,7 @@ __ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ecache) | |||
| 92 | { | 92 | { |
| 93 | DEBUGP("ecache: delivering events for %p\n", ecache->ct); | 93 | DEBUGP("ecache: delivering events for %p\n", ecache->ct); |
| 94 | if (is_confirmed(ecache->ct) && !is_dying(ecache->ct) && ecache->events) | 94 | if (is_confirmed(ecache->ct) && !is_dying(ecache->ct) && ecache->events) |
| 95 | notifier_call_chain(&ip_conntrack_chain, ecache->events, | 95 | atomic_notifier_call_chain(&ip_conntrack_chain, ecache->events, |
| 96 | ecache->ct); | 96 | ecache->ct); |
| 97 | ecache->events = 0; | 97 | ecache->events = 0; |
| 98 | ip_conntrack_put(ecache->ct); | 98 | ip_conntrack_put(ecache->ct); |
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 01c62a0d3742..445006ee4522 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
| @@ -143,7 +143,7 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev, | |||
| 143 | struct prefix_info *pinfo); | 143 | struct prefix_info *pinfo); |
| 144 | static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev); | 144 | static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev); |
| 145 | 145 | ||
| 146 | static struct notifier_block *inet6addr_chain; | 146 | static ATOMIC_NOTIFIER_HEAD(inet6addr_chain); |
| 147 | 147 | ||
| 148 | struct ipv6_devconf ipv6_devconf = { | 148 | struct ipv6_devconf ipv6_devconf = { |
| 149 | .forwarding = 0, | 149 | .forwarding = 0, |
| @@ -593,7 +593,7 @@ out2: | |||
| 593 | read_unlock_bh(&addrconf_lock); | 593 | read_unlock_bh(&addrconf_lock); |
| 594 | 594 | ||
| 595 | if (likely(err == 0)) | 595 | if (likely(err == 0)) |
| 596 | notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa); | 596 | atomic_notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa); |
| 597 | else { | 597 | else { |
| 598 | kfree(ifa); | 598 | kfree(ifa); |
| 599 | ifa = ERR_PTR(err); | 599 | ifa = ERR_PTR(err); |
| @@ -688,7 +688,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) | |||
| 688 | 688 | ||
| 689 | ipv6_ifa_notify(RTM_DELADDR, ifp); | 689 | ipv6_ifa_notify(RTM_DELADDR, ifp); |
| 690 | 690 | ||
| 691 | notifier_call_chain(&inet6addr_chain,NETDEV_DOWN,ifp); | 691 | atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifp); |
| 692 | 692 | ||
| 693 | addrconf_del_timer(ifp); | 693 | addrconf_del_timer(ifp); |
| 694 | 694 | ||
| @@ -3767,12 +3767,12 @@ static void addrconf_sysctl_unregister(struct ipv6_devconf *p) | |||
| 3767 | 3767 | ||
| 3768 | int register_inet6addr_notifier(struct notifier_block *nb) | 3768 | int register_inet6addr_notifier(struct notifier_block *nb) |
| 3769 | { | 3769 | { |
| 3770 | return notifier_chain_register(&inet6addr_chain, nb); | 3770 | return atomic_notifier_chain_register(&inet6addr_chain, nb); |
| 3771 | } | 3771 | } |
| 3772 | 3772 | ||
| 3773 | int unregister_inet6addr_notifier(struct notifier_block *nb) | 3773 | int unregister_inet6addr_notifier(struct notifier_block *nb) |
| 3774 | { | 3774 | { |
| 3775 | return notifier_chain_unregister(&inet6addr_chain,nb); | 3775 | return atomic_notifier_chain_unregister(&inet6addr_chain,nb); |
| 3776 | } | 3776 | } |
| 3777 | 3777 | ||
| 3778 | /* | 3778 | /* |
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0ae281d9bfc3..56389c83557c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
| @@ -90,8 +90,8 @@ static int nf_conntrack_vmalloc; | |||
| 90 | static unsigned int nf_conntrack_next_id; | 90 | static unsigned int nf_conntrack_next_id; |
| 91 | static unsigned int nf_conntrack_expect_next_id; | 91 | static unsigned int nf_conntrack_expect_next_id; |
| 92 | #ifdef CONFIG_NF_CONNTRACK_EVENTS | 92 | #ifdef CONFIG_NF_CONNTRACK_EVENTS |
| 93 | struct notifier_block *nf_conntrack_chain; | 93 | ATOMIC_NOTIFIER_HEAD(nf_conntrack_chain); |
| 94 | struct notifier_block *nf_conntrack_expect_chain; | 94 | ATOMIC_NOTIFIER_HEAD(nf_conntrack_expect_chain); |
| 95 | 95 | ||
| 96 | DEFINE_PER_CPU(struct nf_conntrack_ecache, nf_conntrack_ecache); | 96 | DEFINE_PER_CPU(struct nf_conntrack_ecache, nf_conntrack_ecache); |
| 97 | 97 | ||
| @@ -103,7 +103,7 @@ __nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache) | |||
| 103 | DEBUGP("ecache: delivering events for %p\n", ecache->ct); | 103 | DEBUGP("ecache: delivering events for %p\n", ecache->ct); |
| 104 | if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct) | 104 | if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct) |
| 105 | && ecache->events) | 105 | && ecache->events) |
| 106 | notifier_call_chain(&nf_conntrack_chain, ecache->events, | 106 | atomic_notifier_call_chain(&nf_conntrack_chain, ecache->events, |
| 107 | ecache->ct); | 107 | ecache->ct); |
| 108 | 108 | ||
| 109 | ecache->events = 0; | 109 | ecache->events = 0; |
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d00a9034cb5f..2a233ffcf618 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
| @@ -123,7 +123,7 @@ static void netlink_destroy_callback(struct netlink_callback *cb); | |||
| 123 | static DEFINE_RWLOCK(nl_table_lock); | 123 | static DEFINE_RWLOCK(nl_table_lock); |
| 124 | static atomic_t nl_table_users = ATOMIC_INIT(0); | 124 | static atomic_t nl_table_users = ATOMIC_INIT(0); |
| 125 | 125 | ||
| 126 | static struct notifier_block *netlink_chain; | 126 | static ATOMIC_NOTIFIER_HEAD(netlink_chain); |
| 127 | 127 | ||
| 128 | static u32 netlink_group_mask(u32 group) | 128 | static u32 netlink_group_mask(u32 group) |
| 129 | { | 129 | { |
| @@ -469,7 +469,8 @@ static int netlink_release(struct socket *sock) | |||
| 469 | .protocol = sk->sk_protocol, | 469 | .protocol = sk->sk_protocol, |
| 470 | .pid = nlk->pid, | 470 | .pid = nlk->pid, |
| 471 | }; | 471 | }; |
| 472 | notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); | 472 | atomic_notifier_call_chain(&netlink_chain, |
| 473 | NETLINK_URELEASE, &n); | ||
| 473 | } | 474 | } |
| 474 | 475 | ||
| 475 | if (nlk->module) | 476 | if (nlk->module) |
| @@ -1695,12 +1696,12 @@ static struct file_operations netlink_seq_fops = { | |||
| 1695 | 1696 | ||
| 1696 | int netlink_register_notifier(struct notifier_block *nb) | 1697 | int netlink_register_notifier(struct notifier_block *nb) |
| 1697 | { | 1698 | { |
| 1698 | return notifier_chain_register(&netlink_chain, nb); | 1699 | return atomic_notifier_chain_register(&netlink_chain, nb); |
| 1699 | } | 1700 | } |
| 1700 | 1701 | ||
| 1701 | int netlink_unregister_notifier(struct notifier_block *nb) | 1702 | int netlink_unregister_notifier(struct notifier_block *nb) |
| 1702 | { | 1703 | { |
| 1703 | return notifier_chain_unregister(&netlink_chain, nb); | 1704 | return atomic_notifier_chain_unregister(&netlink_chain, nb); |
| 1704 | } | 1705 | } |
| 1705 | 1706 | ||
| 1706 | static const struct proto_ops netlink_ops = { | 1707 | static const struct proto_ops netlink_ops = { |
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 23632d84d8d7..4d7eb9e704da 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c | |||
| @@ -78,7 +78,8 @@ struct rsi { | |||
| 78 | 78 | ||
| 79 | static struct cache_head *rsi_table[RSI_HASHMAX]; | 79 | static struct cache_head *rsi_table[RSI_HASHMAX]; |
| 80 | static struct cache_detail rsi_cache; | 80 | static struct cache_detail rsi_cache; |
| 81 | static struct rsi *rsi_lookup(struct rsi *item, int set); | 81 | static struct rsi *rsi_update(struct rsi *new, struct rsi *old); |
| 82 | static struct rsi *rsi_lookup(struct rsi *item); | ||
| 82 | 83 | ||
| 83 | static void rsi_free(struct rsi *rsii) | 84 | static void rsi_free(struct rsi *rsii) |
| 84 | { | 85 | { |
| @@ -88,13 +89,11 @@ static void rsi_free(struct rsi *rsii) | |||
| 88 | kfree(rsii->out_token.data); | 89 | kfree(rsii->out_token.data); |
| 89 | } | 90 | } |
| 90 | 91 | ||
| 91 | static void rsi_put(struct cache_head *item, struct cache_detail *cd) | 92 | static void rsi_put(struct kref *ref) |
| 92 | { | 93 | { |
| 93 | struct rsi *rsii = container_of(item, struct rsi, h); | 94 | struct rsi *rsii = container_of(ref, struct rsi, h.ref); |
| 94 | if (cache_put(item, cd)) { | 95 | rsi_free(rsii); |
| 95 | rsi_free(rsii); | 96 | kfree(rsii); |
| 96 | kfree(rsii); | ||
| 97 | } | ||
| 98 | } | 97 | } |
| 99 | 98 | ||
| 100 | static inline int rsi_hash(struct rsi *item) | 99 | static inline int rsi_hash(struct rsi *item) |
| @@ -103,8 +102,10 @@ static inline int rsi_hash(struct rsi *item) | |||
| 103 | ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); | 102 | ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); |
| 104 | } | 103 | } |
| 105 | 104 | ||
| 106 | static inline int rsi_match(struct rsi *item, struct rsi *tmp) | 105 | static int rsi_match(struct cache_head *a, struct cache_head *b) |
| 107 | { | 106 | { |
| 107 | struct rsi *item = container_of(a, struct rsi, h); | ||
| 108 | struct rsi *tmp = container_of(b, struct rsi, h); | ||
| 108 | return netobj_equal(&item->in_handle, &tmp->in_handle) | 109 | return netobj_equal(&item->in_handle, &tmp->in_handle) |
| 109 | && netobj_equal(&item->in_token, &tmp->in_token); | 110 | && netobj_equal(&item->in_token, &tmp->in_token); |
| 110 | } | 111 | } |
| @@ -125,8 +126,11 @@ static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) | |||
| 125 | return dup_to_netobj(dst, src->data, src->len); | 126 | return dup_to_netobj(dst, src->data, src->len); |
| 126 | } | 127 | } |
| 127 | 128 | ||
| 128 | static inline void rsi_init(struct rsi *new, struct rsi *item) | 129 | static void rsi_init(struct cache_head *cnew, struct cache_head *citem) |
| 129 | { | 130 | { |
| 131 | struct rsi *new = container_of(cnew, struct rsi, h); | ||
| 132 | struct rsi *item = container_of(citem, struct rsi, h); | ||
| 133 | |||
| 130 | new->out_handle.data = NULL; | 134 | new->out_handle.data = NULL; |
| 131 | new->out_handle.len = 0; | 135 | new->out_handle.len = 0; |
| 132 | new->out_token.data = NULL; | 136 | new->out_token.data = NULL; |
| @@ -141,8 +145,11 @@ static inline void rsi_init(struct rsi *new, struct rsi *item) | |||
| 141 | item->in_token.data = NULL; | 145 | item->in_token.data = NULL; |
| 142 | } | 146 | } |
| 143 | 147 | ||
| 144 | static inline void rsi_update(struct rsi *new, struct rsi *item) | 148 | static void update_rsi(struct cache_head *cnew, struct cache_head *citem) |
| 145 | { | 149 | { |
| 150 | struct rsi *new = container_of(cnew, struct rsi, h); | ||
| 151 | struct rsi *item = container_of(citem, struct rsi, h); | ||
| 152 | |||
| 146 | BUG_ON(new->out_handle.data || new->out_token.data); | 153 | BUG_ON(new->out_handle.data || new->out_token.data); |
| 147 | new->out_handle.len = item->out_handle.len; | 154 | new->out_handle.len = item->out_handle.len; |
| 148 | item->out_handle.len = 0; | 155 | item->out_handle.len = 0; |
| @@ -157,6 +164,15 @@ static inline void rsi_update(struct rsi *new, struct rsi *item) | |||
| 157 | new->minor_status = item->minor_status; | 164 | new->minor_status = item->minor_status; |
| 158 | } | 165 | } |
| 159 | 166 | ||
| 167 | static struct cache_head *rsi_alloc(void) | ||
| 168 | { | ||
| 169 | struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL); | ||
| 170 | if (rsii) | ||
| 171 | return &rsii->h; | ||
| 172 | else | ||
| 173 | return NULL; | ||
| 174 | } | ||
| 175 | |||
| 160 | static void rsi_request(struct cache_detail *cd, | 176 | static void rsi_request(struct cache_detail *cd, |
| 161 | struct cache_head *h, | 177 | struct cache_head *h, |
| 162 | char **bpp, int *blen) | 178 | char **bpp, int *blen) |
| @@ -198,6 +214,10 @@ static int rsi_parse(struct cache_detail *cd, | |||
| 198 | if (dup_to_netobj(&rsii.in_token, buf, len)) | 214 | if (dup_to_netobj(&rsii.in_token, buf, len)) |
| 199 | goto out; | 215 | goto out; |
| 200 | 216 | ||
| 217 | rsip = rsi_lookup(&rsii); | ||
| 218 | if (!rsip) | ||
| 219 | goto out; | ||
| 220 | |||
| 201 | rsii.h.flags = 0; | 221 | rsii.h.flags = 0; |
| 202 | /* expiry */ | 222 | /* expiry */ |
| 203 | expiry = get_expiry(&mesg); | 223 | expiry = get_expiry(&mesg); |
| @@ -240,12 +260,14 @@ static int rsi_parse(struct cache_detail *cd, | |||
| 240 | goto out; | 260 | goto out; |
| 241 | } | 261 | } |
| 242 | rsii.h.expiry_time = expiry; | 262 | rsii.h.expiry_time = expiry; |
| 243 | rsip = rsi_lookup(&rsii, 1); | 263 | rsip = rsi_update(&rsii, rsip); |
| 244 | status = 0; | 264 | status = 0; |
| 245 | out: | 265 | out: |
| 246 | rsi_free(&rsii); | 266 | rsi_free(&rsii); |
| 247 | if (rsip) | 267 | if (rsip) |
| 248 | rsi_put(&rsip->h, &rsi_cache); | 268 | cache_put(&rsip->h, &rsi_cache); |
| 269 | else | ||
| 270 | status = -ENOMEM; | ||
| 249 | return status; | 271 | return status; |
| 250 | } | 272 | } |
| 251 | 273 | ||
| @@ -257,9 +279,37 @@ static struct cache_detail rsi_cache = { | |||
| 257 | .cache_put = rsi_put, | 279 | .cache_put = rsi_put, |
| 258 | .cache_request = rsi_request, | 280 | .cache_request = rsi_request, |
| 259 | .cache_parse = rsi_parse, | 281 | .cache_parse = rsi_parse, |
| 282 | .match = rsi_match, | ||
| 283 | .init = rsi_init, | ||
| 284 | .update = update_rsi, | ||
| 285 | .alloc = rsi_alloc, | ||
| 260 | }; | 286 | }; |
| 261 | 287 | ||
| 262 | static DefineSimpleCacheLookup(rsi, 0) | 288 | static struct rsi *rsi_lookup(struct rsi *item) |
| 289 | { | ||
| 290 | struct cache_head *ch; | ||
| 291 | int hash = rsi_hash(item); | ||
| 292 | |||
| 293 | ch = sunrpc_cache_lookup(&rsi_cache, &item->h, hash); | ||
| 294 | if (ch) | ||
| 295 | return container_of(ch, struct rsi, h); | ||
| 296 | else | ||
| 297 | return NULL; | ||
| 298 | } | ||
| 299 | |||
| 300 | static struct rsi *rsi_update(struct rsi *new, struct rsi *old) | ||
| 301 | { | ||
| 302 | struct cache_head *ch; | ||
| 303 | int hash = rsi_hash(new); | ||
| 304 | |||
| 305 | ch = sunrpc_cache_update(&rsi_cache, &new->h, | ||
| 306 | &old->h, hash); | ||
| 307 | if (ch) | ||
| 308 | return container_of(ch, struct rsi, h); | ||
| 309 | else | ||
| 310 | return NULL; | ||
| 311 | } | ||
| 312 | |||
| 263 | 313 | ||
| 264 | /* | 314 | /* |
| 265 | * The rpcsec_context cache is used to store a context that is | 315 | * The rpcsec_context cache is used to store a context that is |
| @@ -293,7 +343,8 @@ struct rsc { | |||
| 293 | 343 | ||
| 294 | static struct cache_head *rsc_table[RSC_HASHMAX]; | 344 | static struct cache_head *rsc_table[RSC_HASHMAX]; |
| 295 | static struct cache_detail rsc_cache; | 345 | static struct cache_detail rsc_cache; |
| 296 | static struct rsc *rsc_lookup(struct rsc *item, int set); | 346 | static struct rsc *rsc_update(struct rsc *new, struct rsc *old); |
| 347 | static struct rsc *rsc_lookup(struct rsc *item); | ||
| 297 | 348 | ||
| 298 | static void rsc_free(struct rsc *rsci) | 349 | static void rsc_free(struct rsc *rsci) |
| 299 | { | 350 | { |
| @@ -304,14 +355,12 @@ static void rsc_free(struct rsc *rsci) | |||
| 304 | put_group_info(rsci->cred.cr_group_info); | 355 | put_group_info(rsci->cred.cr_group_info); |
| 305 | } | 356 | } |
| 306 | 357 | ||
| 307 | static void rsc_put(struct cache_head *item, struct cache_detail *cd) | 358 | static void rsc_put(struct kref *ref) |
| 308 | { | 359 | { |
| 309 | struct rsc *rsci = container_of(item, struct rsc, h); | 360 | struct rsc *rsci = container_of(ref, struct rsc, h.ref); |
| 310 | 361 | ||
| 311 | if (cache_put(item, cd)) { | 362 | rsc_free(rsci); |
| 312 | rsc_free(rsci); | 363 | kfree(rsci); |
| 313 | kfree(rsci); | ||
| 314 | } | ||
| 315 | } | 364 | } |
| 316 | 365 | ||
| 317 | static inline int | 366 | static inline int |
| @@ -320,15 +369,21 @@ rsc_hash(struct rsc *rsci) | |||
| 320 | return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); | 369 | return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); |
| 321 | } | 370 | } |
| 322 | 371 | ||
| 323 | static inline int | 372 | static int |
| 324 | rsc_match(struct rsc *new, struct rsc *tmp) | 373 | rsc_match(struct cache_head *a, struct cache_head *b) |
| 325 | { | 374 | { |
| 375 | struct rsc *new = container_of(a, struct rsc, h); | ||
| 376 | struct rsc *tmp = container_of(b, struct rsc, h); | ||
| 377 | |||
| 326 | return netobj_equal(&new->handle, &tmp->handle); | 378 | return netobj_equal(&new->handle, &tmp->handle); |
| 327 | } | 379 | } |
| 328 | 380 | ||
| 329 | static inline void | 381 | static void |
| 330 | rsc_init(struct rsc *new, struct rsc *tmp) | 382 | rsc_init(struct cache_head *cnew, struct cache_head *ctmp) |
| 331 | { | 383 | { |
| 384 | struct rsc *new = container_of(cnew, struct rsc, h); | ||
| 385 | struct rsc *tmp = container_of(ctmp, struct rsc, h); | ||
| 386 | |||
| 332 | new->handle.len = tmp->handle.len; | 387 | new->handle.len = tmp->handle.len; |
| 333 | tmp->handle.len = 0; | 388 | tmp->handle.len = 0; |
| 334 | new->handle.data = tmp->handle.data; | 389 | new->handle.data = tmp->handle.data; |
| @@ -337,9 +392,12 @@ rsc_init(struct rsc *new, struct rsc *tmp) | |||
| 337 | new->cred.cr_group_info = NULL; | 392 | new->cred.cr_group_info = NULL; |
| 338 | } | 393 | } |
| 339 | 394 | ||
| 340 | static inline void | 395 | static void |
| 341 | rsc_update(struct rsc *new, struct rsc *tmp) | 396 | update_rsc(struct cache_head *cnew, struct cache_head *ctmp) |
| 342 | { | 397 | { |
| 398 | struct rsc *new = container_of(cnew, struct rsc, h); | ||
| 399 | struct rsc *tmp = container_of(ctmp, struct rsc, h); | ||
| 400 | |||
| 343 | new->mechctx = tmp->mechctx; | 401 | new->mechctx = tmp->mechctx; |
| 344 | tmp->mechctx = NULL; | 402 | tmp->mechctx = NULL; |
| 345 | memset(&new->seqdata, 0, sizeof(new->seqdata)); | 403 | memset(&new->seqdata, 0, sizeof(new->seqdata)); |
| @@ -348,6 +406,16 @@ rsc_update(struct rsc *new, struct rsc *tmp) | |||
| 348 | tmp->cred.cr_group_info = NULL; | 406 | tmp->cred.cr_group_info = NULL; |
| 349 | } | 407 | } |
| 350 | 408 | ||
| 409 | static struct cache_head * | ||
| 410 | rsc_alloc(void) | ||
| 411 | { | ||
| 412 | struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL); | ||
| 413 | if (rsci) | ||
| 414 | return &rsci->h; | ||
| 415 | else | ||
| 416 | return NULL; | ||
| 417 | } | ||
| 418 | |||
| 351 | static int rsc_parse(struct cache_detail *cd, | 419 | static int rsc_parse(struct cache_detail *cd, |
| 352 | char *mesg, int mlen) | 420 | char *mesg, int mlen) |
| 353 | { | 421 | { |
| @@ -373,6 +441,10 @@ static int rsc_parse(struct cache_detail *cd, | |||
| 373 | if (expiry == 0) | 441 | if (expiry == 0) |
| 374 | goto out; | 442 | goto out; |
| 375 | 443 | ||
| 444 | rscp = rsc_lookup(&rsci); | ||
| 445 | if (!rscp) | ||
| 446 | goto out; | ||
| 447 | |||
| 376 | /* uid, or NEGATIVE */ | 448 | /* uid, or NEGATIVE */ |
| 377 | rv = get_int(&mesg, &rsci.cred.cr_uid); | 449 | rv = get_int(&mesg, &rsci.cred.cr_uid); |
| 378 | if (rv == -EINVAL) | 450 | if (rv == -EINVAL) |
| @@ -428,12 +500,14 @@ static int rsc_parse(struct cache_detail *cd, | |||
| 428 | gss_mech_put(gm); | 500 | gss_mech_put(gm); |
| 429 | } | 501 | } |
| 430 | rsci.h.expiry_time = expiry; | 502 | rsci.h.expiry_time = expiry; |
| 431 | rscp = rsc_lookup(&rsci, 1); | 503 | rscp = rsc_update(&rsci, rscp); |
| 432 | status = 0; | 504 | status = 0; |
| 433 | out: | 505 | out: |
| 434 | rsc_free(&rsci); | 506 | rsc_free(&rsci); |
| 435 | if (rscp) | 507 | if (rscp) |
| 436 | rsc_put(&rscp->h, &rsc_cache); | 508 | cache_put(&rscp->h, &rsc_cache); |
| 509 | else | ||
| 510 | status = -ENOMEM; | ||
| 437 | return status; | 511 | return status; |
| 438 | } | 512 | } |
| 439 | 513 | ||
| @@ -444,9 +518,37 @@ static struct cache_detail rsc_cache = { | |||
| 444 | .name = "auth.rpcsec.context", | 518 | .name = "auth.rpcsec.context", |
| 445 | .cache_put = rsc_put, | 519 | .cache_put = rsc_put, |
| 446 | .cache_parse = rsc_parse, | 520 | .cache_parse = rsc_parse, |
| 521 | .match = rsc_match, | ||
| 522 | .init = rsc_init, | ||
| 523 | .update = update_rsc, | ||
| 524 | .alloc = rsc_alloc, | ||
| 447 | }; | 525 | }; |
| 448 | 526 | ||
| 449 | static DefineSimpleCacheLookup(rsc, 0); | 527 | static struct rsc *rsc_lookup(struct rsc *item) |
| 528 | { | ||
| 529 | struct cache_head *ch; | ||
| 530 | int hash = rsc_hash(item); | ||
| 531 | |||
| 532 | ch = sunrpc_cache_lookup(&rsc_cache, &item->h, hash); | ||
| 533 | if (ch) | ||
| 534 | return container_of(ch, struct rsc, h); | ||
| 535 | else | ||
| 536 | return NULL; | ||
| 537 | } | ||
| 538 | |||
| 539 | static struct rsc *rsc_update(struct rsc *new, struct rsc *old) | ||
| 540 | { | ||
| 541 | struct cache_head *ch; | ||
| 542 | int hash = rsc_hash(new); | ||
| 543 | |||
| 544 | ch = sunrpc_cache_update(&rsc_cache, &new->h, | ||
| 545 | &old->h, hash); | ||
| 546 | if (ch) | ||
| 547 | return container_of(ch, struct rsc, h); | ||
| 548 | else | ||
| 549 | return NULL; | ||
| 550 | } | ||
| 551 | |||
| 450 | 552 | ||
| 451 | static struct rsc * | 553 | static struct rsc * |
| 452 | gss_svc_searchbyctx(struct xdr_netobj *handle) | 554 | gss_svc_searchbyctx(struct xdr_netobj *handle) |
| @@ -457,7 +559,7 @@ gss_svc_searchbyctx(struct xdr_netobj *handle) | |||
| 457 | memset(&rsci, 0, sizeof(rsci)); | 559 | memset(&rsci, 0, sizeof(rsci)); |
| 458 | if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) | 560 | if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) |
| 459 | return NULL; | 561 | return NULL; |
| 460 | found = rsc_lookup(&rsci, 0); | 562 | found = rsc_lookup(&rsci); |
| 461 | rsc_free(&rsci); | 563 | rsc_free(&rsci); |
| 462 | if (!found) | 564 | if (!found) |
| 463 | return NULL; | 565 | return NULL; |
| @@ -645,6 +747,8 @@ find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) | |||
| 645 | return auth_domain_find(name); | 747 | return auth_domain_find(name); |
| 646 | } | 748 | } |
| 647 | 749 | ||
| 750 | static struct auth_ops svcauthops_gss; | ||
| 751 | |||
| 648 | int | 752 | int |
| 649 | svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) | 753 | svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) |
| 650 | { | 754 | { |
| @@ -655,20 +759,18 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) | |||
| 655 | new = kmalloc(sizeof(*new), GFP_KERNEL); | 759 | new = kmalloc(sizeof(*new), GFP_KERNEL); |
| 656 | if (!new) | 760 | if (!new) |
| 657 | goto out; | 761 | goto out; |
| 658 | cache_init(&new->h.h); | 762 | kref_init(&new->h.ref); |
| 659 | new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL); | 763 | new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL); |
| 660 | if (!new->h.name) | 764 | if (!new->h.name) |
| 661 | goto out_free_dom; | 765 | goto out_free_dom; |
| 662 | strcpy(new->h.name, name); | 766 | strcpy(new->h.name, name); |
| 663 | new->h.flavour = RPC_AUTH_GSS; | 767 | new->h.flavour = &svcauthops_gss; |
| 664 | new->pseudoflavor = pseudoflavor; | 768 | new->pseudoflavor = pseudoflavor; |
| 665 | new->h.h.expiry_time = NEVER; | ||
| 666 | 769 | ||
| 667 | test = auth_domain_lookup(&new->h, 1); | 770 | test = auth_domain_lookup(name, &new->h); |
| 668 | if (test == &new->h) { | 771 | if (test != &new->h) { /* XXX Duplicate registration? */ |
| 669 | BUG_ON(atomic_dec_and_test(&new->h.h.refcnt)); | ||
| 670 | } else { /* XXX Duplicate registration? */ | ||
| 671 | auth_domain_put(&new->h); | 772 | auth_domain_put(&new->h); |
| 773 | /* dangling ref-count... */ | ||
| 672 | goto out; | 774 | goto out; |
| 673 | } | 775 | } |
| 674 | return 0; | 776 | return 0; |
| @@ -895,7 +997,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp) | |||
| 895 | goto drop; | 997 | goto drop; |
| 896 | } | 998 | } |
| 897 | 999 | ||
| 898 | rsip = rsi_lookup(&rsikey, 0); | 1000 | rsip = rsi_lookup(&rsikey); |
| 899 | rsi_free(&rsikey); | 1001 | rsi_free(&rsikey); |
| 900 | if (!rsip) { | 1002 | if (!rsip) { |
| 901 | goto drop; | 1003 | goto drop; |
| @@ -970,7 +1072,7 @@ drop: | |||
| 970 | ret = SVC_DROP; | 1072 | ret = SVC_DROP; |
| 971 | out: | 1073 | out: |
| 972 | if (rsci) | 1074 | if (rsci) |
| 973 | rsc_put(&rsci->h, &rsc_cache); | 1075 | cache_put(&rsci->h, &rsc_cache); |
| 974 | return ret; | 1076 | return ret; |
| 975 | } | 1077 | } |
| 976 | 1078 | ||
| @@ -1062,7 +1164,7 @@ out_err: | |||
| 1062 | put_group_info(rqstp->rq_cred.cr_group_info); | 1164 | put_group_info(rqstp->rq_cred.cr_group_info); |
| 1063 | rqstp->rq_cred.cr_group_info = NULL; | 1165 | rqstp->rq_cred.cr_group_info = NULL; |
| 1064 | if (gsd->rsci) | 1166 | if (gsd->rsci) |
| 1065 | rsc_put(&gsd->rsci->h, &rsc_cache); | 1167 | cache_put(&gsd->rsci->h, &rsc_cache); |
| 1066 | gsd->rsci = NULL; | 1168 | gsd->rsci = NULL; |
| 1067 | 1169 | ||
| 1068 | return stat; | 1170 | return stat; |
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 0acccfeeb284..3ac4193a78ed 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c | |||
| @@ -37,16 +37,138 @@ | |||
| 37 | static void cache_defer_req(struct cache_req *req, struct cache_head *item); | 37 | static void cache_defer_req(struct cache_req *req, struct cache_head *item); |
| 38 | static void cache_revisit_request(struct cache_head *item); | 38 | static void cache_revisit_request(struct cache_head *item); |
| 39 | 39 | ||
| 40 | void cache_init(struct cache_head *h) | 40 | static void cache_init(struct cache_head *h) |
| 41 | { | 41 | { |
| 42 | time_t now = get_seconds(); | 42 | time_t now = get_seconds(); |
| 43 | h->next = NULL; | 43 | h->next = NULL; |
| 44 | h->flags = 0; | 44 | h->flags = 0; |
| 45 | atomic_set(&h->refcnt, 1); | 45 | kref_init(&h->ref); |
| 46 | h->expiry_time = now + CACHE_NEW_EXPIRY; | 46 | h->expiry_time = now + CACHE_NEW_EXPIRY; |
| 47 | h->last_refresh = now; | 47 | h->last_refresh = now; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, | ||
| 51 | struct cache_head *key, int hash) | ||
| 52 | { | ||
| 53 | struct cache_head **head, **hp; | ||
| 54 | struct cache_head *new = NULL; | ||
| 55 | |||
| 56 | head = &detail->hash_table[hash]; | ||
| 57 | |||
| 58 | read_lock(&detail->hash_lock); | ||
| 59 | |||
| 60 | for (hp=head; *hp != NULL ; hp = &(*hp)->next) { | ||
| 61 | struct cache_head *tmp = *hp; | ||
| 62 | if (detail->match(tmp, key)) { | ||
| 63 | cache_get(tmp); | ||
| 64 | read_unlock(&detail->hash_lock); | ||
| 65 | return tmp; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | read_unlock(&detail->hash_lock); | ||
| 69 | /* Didn't find anything, insert an empty entry */ | ||
| 70 | |||
| 71 | new = detail->alloc(); | ||
| 72 | if (!new) | ||
| 73 | return NULL; | ||
| 74 | cache_init(new); | ||
| 75 | |||
| 76 | write_lock(&detail->hash_lock); | ||
| 77 | |||
| 78 | /* check if entry appeared while we slept */ | ||
| 79 | for (hp=head; *hp != NULL ; hp = &(*hp)->next) { | ||
| 80 | struct cache_head *tmp = *hp; | ||
| 81 | if (detail->match(tmp, key)) { | ||
| 82 | cache_get(tmp); | ||
| 83 | write_unlock(&detail->hash_lock); | ||
| 84 | cache_put(new, detail); | ||
| 85 | return tmp; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | detail->init(new, key); | ||
| 89 | new->next = *head; | ||
| 90 | *head = new; | ||
| 91 | detail->entries++; | ||
| 92 | cache_get(new); | ||
| 93 | write_unlock(&detail->hash_lock); | ||
| 94 | |||
| 95 | return new; | ||
| 96 | } | ||
| 97 | EXPORT_SYMBOL(sunrpc_cache_lookup); | ||
| 98 | |||
| 99 | |||
| 100 | static void queue_loose(struct cache_detail *detail, struct cache_head *ch); | ||
| 101 | |||
| 102 | static int cache_fresh_locked(struct cache_head *head, time_t expiry) | ||
| 103 | { | ||
| 104 | head->expiry_time = expiry; | ||
| 105 | head->last_refresh = get_seconds(); | ||
| 106 | return !test_and_set_bit(CACHE_VALID, &head->flags); | ||
| 107 | } | ||
| 108 | |||
| 109 | static void cache_fresh_unlocked(struct cache_head *head, | ||
| 110 | struct cache_detail *detail, int new) | ||
| 111 | { | ||
| 112 | if (new) | ||
| 113 | cache_revisit_request(head); | ||
| 114 | if (test_and_clear_bit(CACHE_PENDING, &head->flags)) { | ||
| 115 | cache_revisit_request(head); | ||
| 116 | queue_loose(detail, head); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | struct cache_head *sunrpc_cache_update(struct cache_detail *detail, | ||
| 121 | struct cache_head *new, struct cache_head *old, int hash) | ||
| 122 | { | ||
| 123 | /* The 'old' entry is to be replaced by 'new'. | ||
| 124 | * If 'old' is not VALID, we update it directly, | ||
| 125 | * otherwise we need to replace it | ||
| 126 | */ | ||
| 127 | struct cache_head **head; | ||
| 128 | struct cache_head *tmp; | ||
| 129 | int is_new; | ||
| 130 | |||
| 131 | if (!test_bit(CACHE_VALID, &old->flags)) { | ||
| 132 | write_lock(&detail->hash_lock); | ||
| 133 | if (!test_bit(CACHE_VALID, &old->flags)) { | ||
| 134 | if (test_bit(CACHE_NEGATIVE, &new->flags)) | ||
| 135 | set_bit(CACHE_NEGATIVE, &old->flags); | ||
| 136 | else | ||
| 137 | detail->update(old, new); | ||
| 138 | is_new = cache_fresh_locked(old, new->expiry_time); | ||
| 139 | write_unlock(&detail->hash_lock); | ||
| 140 | cache_fresh_unlocked(old, detail, is_new); | ||
| 141 | return old; | ||
| 142 | } | ||
| 143 | write_unlock(&detail->hash_lock); | ||
| 144 | } | ||
| 145 | /* We need to insert a new entry */ | ||
| 146 | tmp = detail->alloc(); | ||
| 147 | if (!tmp) { | ||
| 148 | cache_put(old, detail); | ||
| 149 | return NULL; | ||
| 150 | } | ||
| 151 | cache_init(tmp); | ||
| 152 | detail->init(tmp, old); | ||
| 153 | head = &detail->hash_table[hash]; | ||
| 154 | |||
| 155 | write_lock(&detail->hash_lock); | ||
| 156 | if (test_bit(CACHE_NEGATIVE, &new->flags)) | ||
| 157 | set_bit(CACHE_NEGATIVE, &tmp->flags); | ||
| 158 | else | ||
| 159 | detail->update(tmp, new); | ||
| 160 | tmp->next = *head; | ||
| 161 | *head = tmp; | ||
| 162 | cache_get(tmp); | ||
| 163 | is_new = cache_fresh_locked(tmp, new->expiry_time); | ||
| 164 | cache_fresh_locked(old, 0); | ||
| 165 | write_unlock(&detail->hash_lock); | ||
| 166 | cache_fresh_unlocked(tmp, detail, is_new); | ||
| 167 | cache_fresh_unlocked(old, detail, 0); | ||
| 168 | cache_put(old, detail); | ||
| 169 | return tmp; | ||
| 170 | } | ||
| 171 | EXPORT_SYMBOL(sunrpc_cache_update); | ||
| 50 | 172 | ||
| 51 | static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h); | 173 | static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h); |
| 52 | /* | 174 | /* |
| @@ -94,7 +216,8 @@ int cache_check(struct cache_detail *detail, | |||
| 94 | clear_bit(CACHE_PENDING, &h->flags); | 216 | clear_bit(CACHE_PENDING, &h->flags); |
| 95 | if (rv == -EAGAIN) { | 217 | if (rv == -EAGAIN) { |
| 96 | set_bit(CACHE_NEGATIVE, &h->flags); | 218 | set_bit(CACHE_NEGATIVE, &h->flags); |
| 97 | cache_fresh(detail, h, get_seconds()+CACHE_NEW_EXPIRY); | 219 | cache_fresh_unlocked(h, detail, |
| 220 | cache_fresh_locked(h, get_seconds()+CACHE_NEW_EXPIRY)); | ||
| 98 | rv = -ENOENT; | 221 | rv = -ENOENT; |
| 99 | } | 222 | } |
| 100 | break; | 223 | break; |
| @@ -110,25 +233,11 @@ int cache_check(struct cache_detail *detail, | |||
| 110 | if (rv == -EAGAIN) | 233 | if (rv == -EAGAIN) |
| 111 | cache_defer_req(rqstp, h); | 234 | cache_defer_req(rqstp, h); |
| 112 | 235 | ||
| 113 | if (rv && h) | 236 | if (rv) |
| 114 | detail->cache_put(h, detail); | 237 | cache_put(h, detail); |
| 115 | return rv; | 238 | return rv; |
| 116 | } | 239 | } |
| 117 | 240 | ||
| 118 | static void queue_loose(struct cache_detail *detail, struct cache_head *ch); | ||
| 119 | |||
| 120 | void cache_fresh(struct cache_detail *detail, | ||
| 121 | struct cache_head *head, time_t expiry) | ||
| 122 | { | ||
| 123 | |||
| 124 | head->expiry_time = expiry; | ||
| 125 | head->last_refresh = get_seconds(); | ||
| 126 | if (!test_and_set_bit(CACHE_VALID, &head->flags)) | ||
| 127 | cache_revisit_request(head); | ||
| 128 | if (test_and_clear_bit(CACHE_PENDING, &head->flags)) | ||
| 129 | queue_loose(detail, head); | ||
| 130 | } | ||
| 131 | |||
| 132 | /* | 241 | /* |
| 133 | * caches need to be periodically cleaned. | 242 | * caches need to be periodically cleaned. |
| 134 | * For this we maintain a list of cache_detail and | 243 | * For this we maintain a list of cache_detail and |
| @@ -322,7 +431,7 @@ static int cache_clean(void) | |||
| 322 | if (test_and_clear_bit(CACHE_PENDING, &ch->flags)) | 431 | if (test_and_clear_bit(CACHE_PENDING, &ch->flags)) |
| 323 | queue_loose(current_detail, ch); | 432 | queue_loose(current_detail, ch); |
| 324 | 433 | ||
| 325 | if (atomic_read(&ch->refcnt) == 1) | 434 | if (atomic_read(&ch->ref.refcount) == 1) |
| 326 | break; | 435 | break; |
| 327 | } | 436 | } |
| 328 | if (ch) { | 437 | if (ch) { |
| @@ -337,7 +446,7 @@ static int cache_clean(void) | |||
| 337 | current_index ++; | 446 | current_index ++; |
| 338 | spin_unlock(&cache_list_lock); | 447 | spin_unlock(&cache_list_lock); |
| 339 | if (ch) | 448 | if (ch) |
| 340 | d->cache_put(ch, d); | 449 | cache_put(ch, d); |
| 341 | } else | 450 | } else |
| 342 | spin_unlock(&cache_list_lock); | 451 | spin_unlock(&cache_list_lock); |
| 343 | 452 | ||
| @@ -453,7 +562,7 @@ static void cache_defer_req(struct cache_req *req, struct cache_head *item) | |||
| 453 | /* there was one too many */ | 562 | /* there was one too many */ |
| 454 | dreq->revisit(dreq, 1); | 563 | dreq->revisit(dreq, 1); |
| 455 | } | 564 | } |
| 456 | if (test_bit(CACHE_VALID, &item->flags)) { | 565 | if (!test_bit(CACHE_PENDING, &item->flags)) { |
| 457 | /* must have just been validated... */ | 566 | /* must have just been validated... */ |
| 458 | cache_revisit_request(item); | 567 | cache_revisit_request(item); |
| 459 | } | 568 | } |
| @@ -614,7 +723,7 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) | |||
| 614 | !test_bit(CACHE_PENDING, &rq->item->flags)) { | 723 | !test_bit(CACHE_PENDING, &rq->item->flags)) { |
| 615 | list_del(&rq->q.list); | 724 | list_del(&rq->q.list); |
| 616 | spin_unlock(&queue_lock); | 725 | spin_unlock(&queue_lock); |
| 617 | cd->cache_put(rq->item, cd); | 726 | cache_put(rq->item, cd); |
| 618 | kfree(rq->buf); | 727 | kfree(rq->buf); |
| 619 | kfree(rq); | 728 | kfree(rq); |
| 620 | } else | 729 | } else |
| @@ -794,10 +903,10 @@ static void queue_loose(struct cache_detail *detail, struct cache_head *ch) | |||
| 794 | if (cr->item != ch) | 903 | if (cr->item != ch) |
| 795 | continue; | 904 | continue; |
| 796 | if (cr->readers != 0) | 905 | if (cr->readers != 0) |
| 797 | break; | 906 | continue; |
| 798 | list_del(&cr->q.list); | 907 | list_del(&cr->q.list); |
| 799 | spin_unlock(&queue_lock); | 908 | spin_unlock(&queue_lock); |
| 800 | detail->cache_put(cr->item, detail); | 909 | cache_put(cr->item, detail); |
| 801 | kfree(cr->buf); | 910 | kfree(cr->buf); |
| 802 | kfree(cr); | 911 | kfree(cr); |
| 803 | return; | 912 | return; |
| @@ -1082,8 +1191,8 @@ static int c_show(struct seq_file *m, void *p) | |||
| 1082 | return cd->cache_show(m, cd, NULL); | 1191 | return cd->cache_show(m, cd, NULL); |
| 1083 | 1192 | ||
| 1084 | ifdebug(CACHE) | 1193 | ifdebug(CACHE) |
| 1085 | seq_printf(m, "# expiry=%ld refcnt=%d\n", | 1194 | seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n", |
| 1086 | cp->expiry_time, atomic_read(&cp->refcnt)); | 1195 | cp->expiry_time, atomic_read(&cp->ref.refcount), cp->flags); |
| 1087 | cache_get(cp); | 1196 | cache_get(cp); |
| 1088 | if (cache_check(cd, cp, NULL)) | 1197 | if (cache_check(cd, cp, NULL)) |
| 1089 | /* cache_check does a cache_put on failure */ | 1198 | /* cache_check does a cache_put on failure */ |
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 9f7373203592..769114f0f886 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c | |||
| @@ -105,8 +105,6 @@ EXPORT_SYMBOL(auth_unix_lookup); | |||
| 105 | EXPORT_SYMBOL(cache_check); | 105 | EXPORT_SYMBOL(cache_check); |
| 106 | EXPORT_SYMBOL(cache_flush); | 106 | EXPORT_SYMBOL(cache_flush); |
| 107 | EXPORT_SYMBOL(cache_purge); | 107 | EXPORT_SYMBOL(cache_purge); |
| 108 | EXPORT_SYMBOL(cache_fresh); | ||
| 109 | EXPORT_SYMBOL(cache_init); | ||
| 110 | EXPORT_SYMBOL(cache_register); | 108 | EXPORT_SYMBOL(cache_register); |
| 111 | EXPORT_SYMBOL(cache_unregister); | 109 | EXPORT_SYMBOL(cache_unregister); |
| 112 | EXPORT_SYMBOL(qword_add); | 110 | EXPORT_SYMBOL(qword_add); |
| @@ -142,6 +140,7 @@ EXPORT_SYMBOL(nlm_debug); | |||
| 142 | 140 | ||
| 143 | extern int register_rpc_pipefs(void); | 141 | extern int register_rpc_pipefs(void); |
| 144 | extern void unregister_rpc_pipefs(void); | 142 | extern void unregister_rpc_pipefs(void); |
| 143 | extern struct cache_detail ip_map_cache; | ||
| 145 | 144 | ||
| 146 | static int __init | 145 | static int __init |
| 147 | init_sunrpc(void) | 146 | init_sunrpc(void) |
| @@ -158,7 +157,6 @@ init_sunrpc(void) | |||
| 158 | #ifdef CONFIG_PROC_FS | 157 | #ifdef CONFIG_PROC_FS |
| 159 | rpc_proc_init(); | 158 | rpc_proc_init(); |
| 160 | #endif | 159 | #endif |
| 161 | cache_register(&auth_domain_cache); | ||
| 162 | cache_register(&ip_map_cache); | 160 | cache_register(&ip_map_cache); |
| 163 | out: | 161 | out: |
| 164 | return err; | 162 | return err; |
| @@ -169,8 +167,6 @@ cleanup_sunrpc(void) | |||
| 169 | { | 167 | { |
| 170 | unregister_rpc_pipefs(); | 168 | unregister_rpc_pipefs(); |
| 171 | rpc_destroy_mempool(); | 169 | rpc_destroy_mempool(); |
| 172 | if (cache_unregister(&auth_domain_cache)) | ||
| 173 | printk(KERN_ERR "sunrpc: failed to unregister auth_domain cache\n"); | ||
| 174 | if (cache_unregister(&ip_map_cache)) | 170 | if (cache_unregister(&ip_map_cache)) |
| 175 | printk(KERN_ERR "sunrpc: failed to unregister ip_map cache\n"); | 171 | printk(KERN_ERR "sunrpc: failed to unregister ip_map cache\n"); |
| 176 | #ifdef RPC_DEBUG | 172 | #ifdef RPC_DEBUG |
diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c index dda4f0c63511..5b28c6176806 100644 --- a/net/sunrpc/svcauth.c +++ b/net/sunrpc/svcauth.c | |||
| @@ -106,112 +106,56 @@ svc_auth_unregister(rpc_authflavor_t flavor) | |||
| 106 | EXPORT_SYMBOL(svc_auth_unregister); | 106 | EXPORT_SYMBOL(svc_auth_unregister); |
| 107 | 107 | ||
| 108 | /************************************************** | 108 | /************************************************** |
| 109 | * cache for domain name to auth_domain | 109 | * 'auth_domains' are stored in a hash table indexed by name. |
| 110 | * Entries are only added by flavours which will normally | 110 | * When the last reference to an 'auth_domain' is dropped, |
| 111 | * have a structure that 'inherits' from auth_domain. | 111 | * the object is unhashed and freed. |
| 112 | * e.g. when an IP -> domainname is given to auth_unix, | 112 | * If auth_domain_lookup fails to find an entry, it will return |
| 113 | * and the domain name doesn't exist, it will create a | 113 | * it's second argument 'new'. If this is non-null, it will |
| 114 | * auth_unix_domain and add it to this hash table. | 114 | * have been atomically linked into the table. |
| 115 | * If it finds the name does exist, but isn't AUTH_UNIX, | ||
| 116 | * it will complain. | ||
| 117 | */ | 115 | */ |
| 118 | 116 | ||
| 119 | /* | ||
| 120 | * Auth auth_domain cache is somewhat different to other caches, | ||
| 121 | * largely because the entries are possibly of different types: | ||
| 122 | * each auth flavour has it's own type. | ||
| 123 | * One consequence of this that DefineCacheLookup cannot | ||
| 124 | * allocate a new structure as it cannot know the size. | ||
| 125 | * Notice that the "INIT" code fragment is quite different | ||
| 126 | * from other caches. When auth_domain_lookup might be | ||
| 127 | * creating a new domain, the new domain is passed in | ||
| 128 | * complete and it is used as-is rather than being copied into | ||
| 129 | * another structure. | ||
| 130 | */ | ||
| 131 | #define DN_HASHBITS 6 | 117 | #define DN_HASHBITS 6 |
| 132 | #define DN_HASHMAX (1<<DN_HASHBITS) | 118 | #define DN_HASHMAX (1<<DN_HASHBITS) |
| 133 | #define DN_HASHMASK (DN_HASHMAX-1) | 119 | #define DN_HASHMASK (DN_HASHMAX-1) |
| 134 | 120 | ||
| 135 | static struct cache_head *auth_domain_table[DN_HASHMAX]; | 121 | static struct hlist_head auth_domain_table[DN_HASHMAX]; |
| 136 | 122 | static spinlock_t auth_domain_lock = SPIN_LOCK_UNLOCKED; | |
| 137 | static void auth_domain_drop(struct cache_head *item, struct cache_detail *cd) | ||
| 138 | { | ||
| 139 | struct auth_domain *dom = container_of(item, struct auth_domain, h); | ||
| 140 | if (cache_put(item,cd)) | ||
| 141 | authtab[dom->flavour]->domain_release(dom); | ||
| 142 | } | ||
| 143 | |||
| 144 | |||
| 145 | struct cache_detail auth_domain_cache = { | ||
| 146 | .owner = THIS_MODULE, | ||
| 147 | .hash_size = DN_HASHMAX, | ||
| 148 | .hash_table = auth_domain_table, | ||
| 149 | .name = "auth.domain", | ||
| 150 | .cache_put = auth_domain_drop, | ||
| 151 | }; | ||
| 152 | 123 | ||
| 153 | void auth_domain_put(struct auth_domain *dom) | 124 | void auth_domain_put(struct auth_domain *dom) |
| 154 | { | 125 | { |
| 155 | auth_domain_drop(&dom->h, &auth_domain_cache); | 126 | if (atomic_dec_and_lock(&dom->ref.refcount, &auth_domain_lock)) { |
| 156 | } | 127 | hlist_del(&dom->hash); |
| 157 | 128 | dom->flavour->domain_release(dom); | |
| 158 | static inline int auth_domain_hash(struct auth_domain *item) | 129 | } |
| 159 | { | ||
| 160 | return hash_str(item->name, DN_HASHBITS); | ||
| 161 | } | ||
| 162 | static inline int auth_domain_match(struct auth_domain *tmp, struct auth_domain *item) | ||
| 163 | { | ||
| 164 | return strcmp(tmp->name, item->name) == 0; | ||
| 165 | } | 130 | } |
| 166 | 131 | ||
| 167 | struct auth_domain * | 132 | struct auth_domain * |
| 168 | auth_domain_lookup(struct auth_domain *item, int set) | 133 | auth_domain_lookup(char *name, struct auth_domain *new) |
| 169 | { | 134 | { |
| 170 | struct auth_domain *tmp = NULL; | 135 | struct auth_domain *hp; |
| 171 | struct cache_head **hp, **head; | 136 | struct hlist_head *head; |
| 172 | head = &auth_domain_cache.hash_table[auth_domain_hash(item)]; | 137 | struct hlist_node *np; |
| 173 | 138 | ||
| 174 | if (set) | 139 | head = &auth_domain_table[hash_str(name, DN_HASHBITS)]; |
| 175 | write_lock(&auth_domain_cache.hash_lock); | 140 | |
| 176 | else | 141 | spin_lock(&auth_domain_lock); |
| 177 | read_lock(&auth_domain_cache.hash_lock); | 142 | |
| 178 | for (hp=head; *hp != NULL; hp = &tmp->h.next) { | 143 | hlist_for_each_entry(hp, np, head, hash) { |
| 179 | tmp = container_of(*hp, struct auth_domain, h); | 144 | if (strcmp(hp->name, name)==0) { |
| 180 | if (!auth_domain_match(tmp, item)) | 145 | kref_get(&hp->ref); |
| 181 | continue; | 146 | spin_unlock(&auth_domain_lock); |
| 182 | if (!set) { | 147 | return hp; |
| 183 | cache_get(&tmp->h); | ||
| 184 | goto out_noset; | ||
| 185 | } | 148 | } |
| 186 | *hp = tmp->h.next; | ||
| 187 | tmp->h.next = NULL; | ||
| 188 | auth_domain_drop(&tmp->h, &auth_domain_cache); | ||
| 189 | goto out_set; | ||
| 190 | } | 149 | } |
| 191 | /* Didn't find anything */ | 150 | if (new) { |
| 192 | if (!set) | 151 | hlist_add_head(&new->hash, head); |
| 193 | goto out_nada; | 152 | kref_get(&new->ref); |
| 194 | auth_domain_cache.entries++; | 153 | } |
| 195 | out_set: | 154 | spin_unlock(&auth_domain_lock); |
| 196 | item->h.next = *head; | 155 | return new; |
| 197 | *head = &item->h; | ||
| 198 | cache_get(&item->h); | ||
| 199 | write_unlock(&auth_domain_cache.hash_lock); | ||
| 200 | cache_fresh(&auth_domain_cache, &item->h, item->h.expiry_time); | ||
| 201 | cache_get(&item->h); | ||
| 202 | return item; | ||
| 203 | out_nada: | ||
| 204 | tmp = NULL; | ||
| 205 | out_noset: | ||
| 206 | read_unlock(&auth_domain_cache.hash_lock); | ||
| 207 | return tmp; | ||
| 208 | } | 156 | } |
| 209 | 157 | ||
| 210 | struct auth_domain *auth_domain_find(char *name) | 158 | struct auth_domain *auth_domain_find(char *name) |
| 211 | { | 159 | { |
| 212 | struct auth_domain *rv, ad; | 160 | return auth_domain_lookup(name, NULL); |
| 213 | |||
| 214 | ad.name = name; | ||
| 215 | rv = auth_domain_lookup(&ad, 0); | ||
| 216 | return rv; | ||
| 217 | } | 161 | } |
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 3e6c694bbad1..7e5707e2d6b6 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c | |||
| @@ -27,41 +27,35 @@ struct unix_domain { | |||
| 27 | /* other stuff later */ | 27 | /* other stuff later */ |
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | extern struct auth_ops svcauth_unix; | ||
| 31 | |||
| 30 | struct auth_domain *unix_domain_find(char *name) | 32 | struct auth_domain *unix_domain_find(char *name) |
| 31 | { | 33 | { |
| 32 | struct auth_domain *rv, ud; | 34 | struct auth_domain *rv; |
| 33 | struct unix_domain *new; | 35 | struct unix_domain *new = NULL; |
| 34 | 36 | ||
| 35 | ud.name = name; | 37 | rv = auth_domain_lookup(name, NULL); |
| 36 | 38 | while(1) { | |
| 37 | rv = auth_domain_lookup(&ud, 0); | 39 | if (rv) { |
| 38 | 40 | if (new && rv != &new->h) | |
| 39 | foundit: | 41 | auth_domain_put(&new->h); |
| 40 | if (rv && rv->flavour != RPC_AUTH_UNIX) { | 42 | |
| 41 | auth_domain_put(rv); | 43 | if (rv->flavour != &svcauth_unix) { |
| 42 | return NULL; | 44 | auth_domain_put(rv); |
| 43 | } | 45 | return NULL; |
| 44 | if (rv) | 46 | } |
| 45 | return rv; | 47 | return rv; |
| 46 | 48 | } | |
| 47 | new = kmalloc(sizeof(*new), GFP_KERNEL); | 49 | |
| 48 | if (new == NULL) | 50 | new = kmalloc(sizeof(*new), GFP_KERNEL); |
| 49 | return NULL; | 51 | if (new == NULL) |
| 50 | cache_init(&new->h.h); | 52 | return NULL; |
| 51 | new->h.name = kstrdup(name, GFP_KERNEL); | 53 | kref_init(&new->h.ref); |
| 52 | new->h.flavour = RPC_AUTH_UNIX; | 54 | new->h.name = kstrdup(name, GFP_KERNEL); |
| 53 | new->addr_changes = 0; | 55 | new->h.flavour = &svcauth_unix; |
| 54 | new->h.h.expiry_time = NEVER; | 56 | new->addr_changes = 0; |
| 55 | 57 | rv = auth_domain_lookup(name, &new->h); | |
| 56 | rv = auth_domain_lookup(&new->h, 2); | ||
| 57 | if (rv == &new->h) { | ||
| 58 | if (atomic_dec_and_test(&new->h.h.refcnt)) BUG(); | ||
| 59 | } else { | ||
| 60 | auth_domain_put(&new->h); | ||
| 61 | goto foundit; | ||
| 62 | } | 58 | } |
| 63 | |||
| 64 | return rv; | ||
| 65 | } | 59 | } |
| 66 | 60 | ||
| 67 | static void svcauth_unix_domain_release(struct auth_domain *dom) | 61 | static void svcauth_unix_domain_release(struct auth_domain *dom) |
| @@ -90,15 +84,15 @@ struct ip_map { | |||
| 90 | }; | 84 | }; |
| 91 | static struct cache_head *ip_table[IP_HASHMAX]; | 85 | static struct cache_head *ip_table[IP_HASHMAX]; |
| 92 | 86 | ||
| 93 | static void ip_map_put(struct cache_head *item, struct cache_detail *cd) | 87 | static void ip_map_put(struct kref *kref) |
| 94 | { | 88 | { |
| 89 | struct cache_head *item = container_of(kref, struct cache_head, ref); | ||
| 95 | struct ip_map *im = container_of(item, struct ip_map,h); | 90 | struct ip_map *im = container_of(item, struct ip_map,h); |
| 96 | if (cache_put(item, cd)) { | 91 | |
| 97 | if (test_bit(CACHE_VALID, &item->flags) && | 92 | if (test_bit(CACHE_VALID, &item->flags) && |
| 98 | !test_bit(CACHE_NEGATIVE, &item->flags)) | 93 | !test_bit(CACHE_NEGATIVE, &item->flags)) |
| 99 | auth_domain_put(&im->m_client->h); | 94 | auth_domain_put(&im->m_client->h); |
| 100 | kfree(im); | 95 | kfree(im); |
| 101 | } | ||
| 102 | } | 96 | } |
| 103 | 97 | ||
| 104 | #if IP_HASHBITS == 8 | 98 | #if IP_HASHBITS == 8 |
| @@ -112,28 +106,38 @@ static inline int hash_ip(unsigned long ip) | |||
| 112 | return (hash ^ (hash>>8)) & 0xff; | 106 | return (hash ^ (hash>>8)) & 0xff; |
| 113 | } | 107 | } |
| 114 | #endif | 108 | #endif |
| 115 | 109 | static int ip_map_match(struct cache_head *corig, struct cache_head *cnew) | |
| 116 | static inline int ip_map_hash(struct ip_map *item) | ||
| 117 | { | ||
| 118 | return hash_str(item->m_class, IP_HASHBITS) ^ | ||
| 119 | hash_ip((unsigned long)item->m_addr.s_addr); | ||
| 120 | } | ||
| 121 | static inline int ip_map_match(struct ip_map *item, struct ip_map *tmp) | ||
| 122 | { | 110 | { |
| 123 | return strcmp(tmp->m_class, item->m_class) == 0 | 111 | struct ip_map *orig = container_of(corig, struct ip_map, h); |
| 124 | && tmp->m_addr.s_addr == item->m_addr.s_addr; | 112 | struct ip_map *new = container_of(cnew, struct ip_map, h); |
| 113 | return strcmp(orig->m_class, new->m_class) == 0 | ||
| 114 | && orig->m_addr.s_addr == new->m_addr.s_addr; | ||
| 125 | } | 115 | } |
| 126 | static inline void ip_map_init(struct ip_map *new, struct ip_map *item) | 116 | static void ip_map_init(struct cache_head *cnew, struct cache_head *citem) |
| 127 | { | 117 | { |
| 118 | struct ip_map *new = container_of(cnew, struct ip_map, h); | ||
| 119 | struct ip_map *item = container_of(citem, struct ip_map, h); | ||
| 120 | |||
| 128 | strcpy(new->m_class, item->m_class); | 121 | strcpy(new->m_class, item->m_class); |
| 129 | new->m_addr.s_addr = item->m_addr.s_addr; | 122 | new->m_addr.s_addr = item->m_addr.s_addr; |
| 130 | } | 123 | } |
| 131 | static inline void ip_map_update(struct ip_map *new, struct ip_map *item) | 124 | static void update(struct cache_head *cnew, struct cache_head *citem) |
| 132 | { | 125 | { |
| 133 | cache_get(&item->m_client->h.h); | 126 | struct ip_map *new = container_of(cnew, struct ip_map, h); |
| 127 | struct ip_map *item = container_of(citem, struct ip_map, h); | ||
| 128 | |||
| 129 | kref_get(&item->m_client->h.ref); | ||
| 134 | new->m_client = item->m_client; | 130 | new->m_client = item->m_client; |
| 135 | new->m_add_change = item->m_add_change; | 131 | new->m_add_change = item->m_add_change; |
| 136 | } | 132 | } |
| 133 | static struct cache_head *ip_map_alloc(void) | ||
| 134 | { | ||
| 135 | struct ip_map *i = kmalloc(sizeof(*i), GFP_KERNEL); | ||
| 136 | if (i) | ||
| 137 | return &i->h; | ||
| 138 | else | ||
| 139 | return NULL; | ||
| 140 | } | ||
| 137 | 141 | ||
| 138 | static void ip_map_request(struct cache_detail *cd, | 142 | static void ip_map_request(struct cache_detail *cd, |
| 139 | struct cache_head *h, | 143 | struct cache_head *h, |
| @@ -154,7 +158,8 @@ static void ip_map_request(struct cache_detail *cd, | |||
| 154 | (*bpp)[-1] = '\n'; | 158 | (*bpp)[-1] = '\n'; |
| 155 | } | 159 | } |
| 156 | 160 | ||
| 157 | static struct ip_map *ip_map_lookup(struct ip_map *, int); | 161 | static struct ip_map *ip_map_lookup(char *class, struct in_addr addr); |
| 162 | static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry); | ||
| 158 | 163 | ||
| 159 | static int ip_map_parse(struct cache_detail *cd, | 164 | static int ip_map_parse(struct cache_detail *cd, |
| 160 | char *mesg, int mlen) | 165 | char *mesg, int mlen) |
| @@ -166,7 +171,11 @@ static int ip_map_parse(struct cache_detail *cd, | |||
| 166 | int len; | 171 | int len; |
| 167 | int b1,b2,b3,b4; | 172 | int b1,b2,b3,b4; |
| 168 | char c; | 173 | char c; |
| 169 | struct ip_map ipm, *ipmp; | 174 | char class[8]; |
| 175 | struct in_addr addr; | ||
| 176 | int err; | ||
| 177 | |||
| 178 | struct ip_map *ipmp; | ||
| 170 | struct auth_domain *dom; | 179 | struct auth_domain *dom; |
| 171 | time_t expiry; | 180 | time_t expiry; |
| 172 | 181 | ||
| @@ -175,7 +184,7 @@ static int ip_map_parse(struct cache_detail *cd, | |||
| 175 | mesg[mlen-1] = 0; | 184 | mesg[mlen-1] = 0; |
| 176 | 185 | ||
| 177 | /* class */ | 186 | /* class */ |
| 178 | len = qword_get(&mesg, ipm.m_class, sizeof(ipm.m_class)); | 187 | len = qword_get(&mesg, class, sizeof(class)); |
| 179 | if (len <= 0) return -EINVAL; | 188 | if (len <= 0) return -EINVAL; |
| 180 | 189 | ||
| 181 | /* ip address */ | 190 | /* ip address */ |
| @@ -200,25 +209,22 @@ static int ip_map_parse(struct cache_detail *cd, | |||
| 200 | } else | 209 | } else |
| 201 | dom = NULL; | 210 | dom = NULL; |
| 202 | 211 | ||
| 203 | ipm.m_addr.s_addr = | 212 | addr.s_addr = |
| 204 | htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); | 213 | htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); |
| 205 | ipm.h.flags = 0; | 214 | |
| 206 | if (dom) { | 215 | ipmp = ip_map_lookup(class,addr); |
| 207 | ipm.m_client = container_of(dom, struct unix_domain, h); | 216 | if (ipmp) { |
| 208 | ipm.m_add_change = ipm.m_client->addr_changes; | 217 | err = ip_map_update(ipmp, |
| 218 | container_of(dom, struct unix_domain, h), | ||
| 219 | expiry); | ||
| 209 | } else | 220 | } else |
| 210 | set_bit(CACHE_NEGATIVE, &ipm.h.flags); | 221 | err = -ENOMEM; |
| 211 | ipm.h.expiry_time = expiry; | ||
| 212 | 222 | ||
| 213 | ipmp = ip_map_lookup(&ipm, 1); | ||
| 214 | if (ipmp) | ||
| 215 | ip_map_put(&ipmp->h, &ip_map_cache); | ||
| 216 | if (dom) | 223 | if (dom) |
| 217 | auth_domain_put(dom); | 224 | auth_domain_put(dom); |
| 218 | if (!ipmp) | 225 | |
| 219 | return -ENOMEM; | ||
| 220 | cache_flush(); | 226 | cache_flush(); |
| 221 | return 0; | 227 | return err; |
| 222 | } | 228 | } |
| 223 | 229 | ||
| 224 | static int ip_map_show(struct seq_file *m, | 230 | static int ip_map_show(struct seq_file *m, |
| @@ -262,32 +268,70 @@ struct cache_detail ip_map_cache = { | |||
| 262 | .cache_request = ip_map_request, | 268 | .cache_request = ip_map_request, |
| 263 | .cache_parse = ip_map_parse, | 269 | .cache_parse = ip_map_parse, |
| 264 | .cache_show = ip_map_show, | 270 | .cache_show = ip_map_show, |
| 271 | .match = ip_map_match, | ||
| 272 | .init = ip_map_init, | ||
| 273 | .update = update, | ||
| 274 | .alloc = ip_map_alloc, | ||
| 265 | }; | 275 | }; |
| 266 | 276 | ||
| 267 | static DefineSimpleCacheLookup(ip_map, 0) | 277 | static struct ip_map *ip_map_lookup(char *class, struct in_addr addr) |
| 278 | { | ||
| 279 | struct ip_map ip; | ||
| 280 | struct cache_head *ch; | ||
| 281 | |||
| 282 | strcpy(ip.m_class, class); | ||
| 283 | ip.m_addr = addr; | ||
| 284 | ch = sunrpc_cache_lookup(&ip_map_cache, &ip.h, | ||
| 285 | hash_str(class, IP_HASHBITS) ^ | ||
| 286 | hash_ip((unsigned long)addr.s_addr)); | ||
| 287 | |||
| 288 | if (ch) | ||
| 289 | return container_of(ch, struct ip_map, h); | ||
| 290 | else | ||
| 291 | return NULL; | ||
| 292 | } | ||
| 268 | 293 | ||
| 294 | static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry) | ||
| 295 | { | ||
| 296 | struct ip_map ip; | ||
| 297 | struct cache_head *ch; | ||
| 298 | |||
| 299 | ip.m_client = udom; | ||
| 300 | ip.h.flags = 0; | ||
| 301 | if (!udom) | ||
| 302 | set_bit(CACHE_NEGATIVE, &ip.h.flags); | ||
| 303 | else { | ||
| 304 | ip.m_add_change = udom->addr_changes; | ||
| 305 | /* if this is from the legacy set_client system call, | ||
| 306 | * we need m_add_change to be one higher | ||
| 307 | */ | ||
| 308 | if (expiry == NEVER) | ||
| 309 | ip.m_add_change++; | ||
| 310 | } | ||
| 311 | ip.h.expiry_time = expiry; | ||
| 312 | ch = sunrpc_cache_update(&ip_map_cache, | ||
| 313 | &ip.h, &ipm->h, | ||
| 314 | hash_str(ipm->m_class, IP_HASHBITS) ^ | ||
| 315 | hash_ip((unsigned long)ipm->m_addr.s_addr)); | ||
| 316 | if (!ch) | ||
| 317 | return -ENOMEM; | ||
| 318 | cache_put(ch, &ip_map_cache); | ||
| 319 | return 0; | ||
| 320 | } | ||
| 269 | 321 | ||
| 270 | int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom) | 322 | int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom) |
| 271 | { | 323 | { |
| 272 | struct unix_domain *udom; | 324 | struct unix_domain *udom; |
| 273 | struct ip_map ip, *ipmp; | 325 | struct ip_map *ipmp; |
| 274 | 326 | ||
| 275 | if (dom->flavour != RPC_AUTH_UNIX) | 327 | if (dom->flavour != &svcauth_unix) |
| 276 | return -EINVAL; | 328 | return -EINVAL; |
| 277 | udom = container_of(dom, struct unix_domain, h); | 329 | udom = container_of(dom, struct unix_domain, h); |
| 278 | strcpy(ip.m_class, "nfsd"); | 330 | ipmp = ip_map_lookup("nfsd", addr); |
| 279 | ip.m_addr = addr; | ||
| 280 | ip.m_client = udom; | ||
| 281 | ip.m_add_change = udom->addr_changes+1; | ||
| 282 | ip.h.flags = 0; | ||
| 283 | ip.h.expiry_time = NEVER; | ||
| 284 | |||
| 285 | ipmp = ip_map_lookup(&ip, 1); | ||
| 286 | 331 | ||
| 287 | if (ipmp) { | 332 | if (ipmp) |
| 288 | ip_map_put(&ipmp->h, &ip_map_cache); | 333 | return ip_map_update(ipmp, udom, NEVER); |
| 289 | return 0; | 334 | else |
| 290 | } else | ||
| 291 | return -ENOMEM; | 335 | return -ENOMEM; |
| 292 | } | 336 | } |
| 293 | 337 | ||
| @@ -295,7 +339,7 @@ int auth_unix_forget_old(struct auth_domain *dom) | |||
| 295 | { | 339 | { |
| 296 | struct unix_domain *udom; | 340 | struct unix_domain *udom; |
| 297 | 341 | ||
| 298 | if (dom->flavour != RPC_AUTH_UNIX) | 342 | if (dom->flavour != &svcauth_unix) |
| 299 | return -EINVAL; | 343 | return -EINVAL; |
| 300 | udom = container_of(dom, struct unix_domain, h); | 344 | udom = container_of(dom, struct unix_domain, h); |
| 301 | udom->addr_changes++; | 345 | udom->addr_changes++; |
| @@ -310,7 +354,7 @@ struct auth_domain *auth_unix_lookup(struct in_addr addr) | |||
| 310 | strcpy(key.m_class, "nfsd"); | 354 | strcpy(key.m_class, "nfsd"); |
| 311 | key.m_addr = addr; | 355 | key.m_addr = addr; |
| 312 | 356 | ||
| 313 | ipm = ip_map_lookup(&key, 0); | 357 | ipm = ip_map_lookup("nfsd", addr); |
| 314 | 358 | ||
| 315 | if (!ipm) | 359 | if (!ipm) |
| 316 | return NULL; | 360 | return NULL; |
| @@ -323,31 +367,28 @@ struct auth_domain *auth_unix_lookup(struct in_addr addr) | |||
| 323 | rv = NULL; | 367 | rv = NULL; |
| 324 | } else { | 368 | } else { |
| 325 | rv = &ipm->m_client->h; | 369 | rv = &ipm->m_client->h; |
| 326 | cache_get(&rv->h); | 370 | kref_get(&rv->ref); |
| 327 | } | 371 | } |
| 328 | ip_map_put(&ipm->h, &ip_map_cache); | 372 | cache_put(&ipm->h, &ip_map_cache); |
| 329 | return rv; | 373 | return rv; |
| 330 | } | 374 | } |
| 331 | 375 | ||
| 332 | void svcauth_unix_purge(void) | 376 | void svcauth_unix_purge(void) |
| 333 | { | 377 | { |
| 334 | cache_purge(&ip_map_cache); | 378 | cache_purge(&ip_map_cache); |
| 335 | cache_purge(&auth_domain_cache); | ||
| 336 | } | 379 | } |
| 337 | 380 | ||
| 338 | static int | 381 | static int |
| 339 | svcauth_unix_set_client(struct svc_rqst *rqstp) | 382 | svcauth_unix_set_client(struct svc_rqst *rqstp) |
| 340 | { | 383 | { |
| 341 | struct ip_map key, *ipm; | 384 | struct ip_map *ipm; |
| 342 | 385 | ||
| 343 | rqstp->rq_client = NULL; | 386 | rqstp->rq_client = NULL; |
| 344 | if (rqstp->rq_proc == 0) | 387 | if (rqstp->rq_proc == 0) |
| 345 | return SVC_OK; | 388 | return SVC_OK; |
| 346 | 389 | ||
| 347 | strcpy(key.m_class, rqstp->rq_server->sv_program->pg_class); | 390 | ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, |
| 348 | key.m_addr = rqstp->rq_addr.sin_addr; | 391 | rqstp->rq_addr.sin_addr); |
| 349 | |||
| 350 | ipm = ip_map_lookup(&key, 0); | ||
| 351 | 392 | ||
| 352 | if (ipm == NULL) | 393 | if (ipm == NULL) |
| 353 | return SVC_DENIED; | 394 | return SVC_DENIED; |
| @@ -361,8 +402,8 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) | |||
| 361 | return SVC_DENIED; | 402 | return SVC_DENIED; |
| 362 | case 0: | 403 | case 0: |
| 363 | rqstp->rq_client = &ipm->m_client->h; | 404 | rqstp->rq_client = &ipm->m_client->h; |
| 364 | cache_get(&rqstp->rq_client->h); | 405 | kref_get(&rqstp->rq_client->ref); |
| 365 | ip_map_put(&ipm->h, &ip_map_cache); | 406 | cache_put(&ipm->h, &ip_map_cache); |
| 366 | break; | 407 | break; |
| 367 | } | 408 | } |
| 368 | return SVC_OK; | 409 | return SVC_OK; |
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index 9d10d79e27af..9ea3059a7064 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c | |||
| @@ -59,7 +59,8 @@ module_param_array(irq, int, NULL, 0444); | |||
| 59 | MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device."); | 59 | MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device."); |
| 60 | 60 | ||
| 61 | static struct platform_device *platform_devices[SNDRV_CARDS]; | 61 | static struct platform_device *platform_devices[SNDRV_CARDS]; |
| 62 | static int pnp_registered = 0; | 62 | static int pnp_registered; |
| 63 | static unsigned int snd_mpu401_devices; | ||
| 63 | 64 | ||
| 64 | static int snd_mpu401_create(int dev, struct snd_card **rcard) | 65 | static int snd_mpu401_create(int dev, struct snd_card **rcard) |
| 65 | { | 66 | { |
| @@ -197,6 +198,7 @@ static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev, | |||
| 197 | } | 198 | } |
| 198 | snd_card_set_dev(card, &pnp_dev->dev); | 199 | snd_card_set_dev(card, &pnp_dev->dev); |
| 199 | pnp_set_drvdata(pnp_dev, card); | 200 | pnp_set_drvdata(pnp_dev, card); |
| 201 | snd_mpu401_devices++; | ||
| 200 | ++dev; | 202 | ++dev; |
| 201 | return 0; | 203 | return 0; |
| 202 | } | 204 | } |
| @@ -234,12 +236,11 @@ static void __init_or_module snd_mpu401_unregister_all(void) | |||
| 234 | 236 | ||
| 235 | static int __init alsa_card_mpu401_init(void) | 237 | static int __init alsa_card_mpu401_init(void) |
| 236 | { | 238 | { |
| 237 | int i, err, devices; | 239 | int i, err; |
| 238 | 240 | ||
| 239 | if ((err = platform_driver_register(&snd_mpu401_driver)) < 0) | 241 | if ((err = platform_driver_register(&snd_mpu401_driver)) < 0) |
| 240 | return err; | 242 | return err; |
| 241 | 243 | ||
| 242 | devices = 0; | ||
| 243 | for (i = 0; i < SNDRV_CARDS; i++) { | 244 | for (i = 0; i < SNDRV_CARDS; i++) { |
| 244 | struct platform_device *device; | 245 | struct platform_device *device; |
| 245 | if (! enable[i]) | 246 | if (! enable[i]) |
| @@ -255,14 +256,13 @@ static int __init alsa_card_mpu401_init(void) | |||
| 255 | goto errout; | 256 | goto errout; |
| 256 | } | 257 | } |
| 257 | platform_devices[i] = device; | 258 | platform_devices[i] = device; |
| 258 | devices++; | 259 | snd_mpu401_devices++; |
| 259 | } | 260 | } |
| 260 | if ((err = pnp_register_driver(&snd_mpu401_pnp_driver)) >= 0) { | 261 | err = pnp_register_driver(&snd_mpu401_pnp_driver); |
| 262 | if (!err) | ||
| 261 | pnp_registered = 1; | 263 | pnp_registered = 1; |
| 262 | devices += err; | ||
| 263 | } | ||
| 264 | 264 | ||
| 265 | if (!devices) { | 265 | if (!snd_mpu401_devices) { |
| 266 | #ifdef MODULE | 266 | #ifdef MODULE |
| 267 | printk(KERN_ERR "MPU-401 device not found or device busy\n"); | 267 | printk(KERN_ERR "MPU-401 device not found or device busy\n"); |
| 268 | #endif | 268 | #endif |
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index 7051f7798ed7..31f299aed281 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c | |||
| @@ -262,6 +262,8 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard | |||
| 262 | return 0; | 262 | return 0; |
| 263 | } | 263 | } |
| 264 | 264 | ||
| 265 | static unsigned int __devinitdata ad1816a_devices; | ||
| 266 | |||
| 265 | static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card, | 267 | static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card, |
| 266 | const struct pnp_card_device_id *id) | 268 | const struct pnp_card_device_id *id) |
| 267 | { | 269 | { |
| @@ -275,6 +277,7 @@ static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card, | |||
| 275 | if (res < 0) | 277 | if (res < 0) |
| 276 | return res; | 278 | return res; |
| 277 | dev++; | 279 | dev++; |
| 280 | ad1816a_devices++; | ||
| 278 | return 0; | 281 | return 0; |
| 279 | } | 282 | } |
| 280 | return -ENODEV; | 283 | return -ENODEV; |
| @@ -297,10 +300,13 @@ static struct pnp_card_driver ad1816a_pnpc_driver = { | |||
| 297 | 300 | ||
| 298 | static int __init alsa_card_ad1816a_init(void) | 301 | static int __init alsa_card_ad1816a_init(void) |
| 299 | { | 302 | { |
| 300 | int cards; | 303 | int err; |
| 304 | |||
| 305 | err = pnp_register_card_driver(&ad1816a_pnpc_driver); | ||
| 306 | if (err) | ||
| 307 | return err; | ||
| 301 | 308 | ||
| 302 | cards = pnp_register_card_driver(&ad1816a_pnpc_driver); | 309 | if (!ad1816a_devices) { |
| 303 | if (cards <= 0) { | ||
| 304 | pnp_unregister_card_driver(&ad1816a_pnpc_driver); | 310 | pnp_unregister_card_driver(&ad1816a_pnpc_driver); |
| 305 | #ifdef MODULE | 311 | #ifdef MODULE |
| 306 | printk(KERN_ERR "no AD1816A based soundcards found.\n"); | 312 | printk(KERN_ERR "no AD1816A based soundcards found.\n"); |
diff --git a/sound/isa/als100.c b/sound/isa/als100.c index 9b77c17b3f66..a52bd8a14c9b 100644 --- a/sound/isa/als100.c +++ b/sound/isa/als100.c | |||
| @@ -199,7 +199,7 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard, | |||
| 199 | return 0; | 199 | return 0; |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | static int __init snd_card_als100_probe(int dev, | 202 | static int __devinit snd_card_als100_probe(int dev, |
| 203 | struct pnp_card_link *pcard, | 203 | struct pnp_card_link *pcard, |
| 204 | const struct pnp_card_device_id *pid) | 204 | const struct pnp_card_device_id *pid) |
| 205 | { | 205 | { |
| @@ -281,6 +281,8 @@ static int __init snd_card_als100_probe(int dev, | |||
| 281 | return 0; | 281 | return 0; |
| 282 | } | 282 | } |
| 283 | 283 | ||
| 284 | static unsigned int __devinitdata als100_devices; | ||
| 285 | |||
| 284 | static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card, | 286 | static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card, |
| 285 | const struct pnp_card_device_id *id) | 287 | const struct pnp_card_device_id *id) |
| 286 | { | 288 | { |
| @@ -294,6 +296,7 @@ static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card, | |||
| 294 | if (res < 0) | 296 | if (res < 0) |
| 295 | return res; | 297 | return res; |
| 296 | dev++; | 298 | dev++; |
| 299 | als100_devices++; | ||
| 297 | return 0; | 300 | return 0; |
| 298 | } | 301 | } |
| 299 | return -ENODEV; | 302 | return -ENODEV; |
| @@ -345,10 +348,13 @@ static struct pnp_card_driver als100_pnpc_driver = { | |||
| 345 | 348 | ||
| 346 | static int __init alsa_card_als100_init(void) | 349 | static int __init alsa_card_als100_init(void) |
| 347 | { | 350 | { |
| 348 | int cards; | 351 | int err; |
| 352 | |||
| 353 | err = pnp_register_card_driver(&als100_pnpc_driver); | ||
| 354 | if (err) | ||
| 355 | return err; | ||
| 349 | 356 | ||
| 350 | cards = pnp_register_card_driver(&als100_pnpc_driver); | 357 | if (!als100_devices) { |
| 351 | if (cards <= 0) { | ||
| 352 | pnp_unregister_card_driver(&als100_pnpc_driver); | 358 | pnp_unregister_card_driver(&als100_pnpc_driver); |
| 353 | #ifdef MODULE | 359 | #ifdef MODULE |
| 354 | snd_printk(KERN_ERR "no ALS100 based soundcards found\n"); | 360 | snd_printk(KERN_ERR "no ALS100 based soundcards found\n"); |
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c index a530691bf4f7..15e59283aac6 100644 --- a/sound/isa/azt2320.c +++ b/sound/isa/azt2320.c | |||
| @@ -310,6 +310,8 @@ static int __devinit snd_card_azt2320_probe(int dev, | |||
| 310 | return 0; | 310 | return 0; |
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | static unsigned int __devinitdata azt2320_devices; | ||
| 314 | |||
| 313 | static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card, | 315 | static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card, |
| 314 | const struct pnp_card_device_id *id) | 316 | const struct pnp_card_device_id *id) |
| 315 | { | 317 | { |
| @@ -323,6 +325,7 @@ static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card, | |||
| 323 | if (res < 0) | 325 | if (res < 0) |
| 324 | return res; | 326 | return res; |
| 325 | dev++; | 327 | dev++; |
| 328 | azt2320_devices++; | ||
| 326 | return 0; | 329 | return 0; |
| 327 | } | 330 | } |
| 328 | return -ENODEV; | 331 | return -ENODEV; |
| @@ -372,10 +375,13 @@ static struct pnp_card_driver azt2320_pnpc_driver = { | |||
| 372 | 375 | ||
| 373 | static int __init alsa_card_azt2320_init(void) | 376 | static int __init alsa_card_azt2320_init(void) |
| 374 | { | 377 | { |
| 375 | int cards; | 378 | int err; |
| 379 | |||
| 380 | err = pnp_register_card_driver(&azt2320_pnpc_driver); | ||
| 381 | if (err) | ||
| 382 | return err; | ||
| 376 | 383 | ||
| 377 | cards = pnp_register_card_driver(&azt2320_pnpc_driver); | 384 | if (!azt2320_devices) { |
| 378 | if (cards <= 0) { | ||
| 379 | pnp_unregister_card_driver(&azt2320_pnpc_driver); | 385 | pnp_unregister_card_driver(&azt2320_pnpc_driver); |
| 380 | #ifdef MODULE | 386 | #ifdef MODULE |
| 381 | snd_printk(KERN_ERR "no AZT2320 based soundcards found\n"); | 387 | snd_printk(KERN_ERR "no AZT2320 based soundcards found\n"); |
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index fd9bb2575de8..fa63048a8b9d 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c | |||
| @@ -175,7 +175,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_cmi8330_pnpids); | |||
| 175 | #endif | 175 | #endif |
| 176 | 176 | ||
| 177 | 177 | ||
| 178 | static struct ad1848_mix_elem snd_cmi8330_controls[] __initdata = { | 178 | static struct ad1848_mix_elem snd_cmi8330_controls[] __devinitdata = { |
| 179 | AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), | 179 | AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), |
| 180 | AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1), | 180 | AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1), |
| 181 | AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), | 181 | AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), |
| @@ -204,7 +204,7 @@ AD1848_SINGLE(SNDRV_CTL_NAME_IEC958("Input ",PLAYBACK,SWITCH), 0, CMI8330_MUTEMU | |||
| 204 | }; | 204 | }; |
| 205 | 205 | ||
| 206 | #ifdef ENABLE_SB_MIXER | 206 | #ifdef ENABLE_SB_MIXER |
| 207 | static struct sbmix_elem cmi8330_sb_mixers[] __initdata = { | 207 | static struct sbmix_elem cmi8330_sb_mixers[] __devinitdata = { |
| 208 | SB_DOUBLE("SB Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), | 208 | SB_DOUBLE("SB Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), |
| 209 | SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), | 209 | SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), |
| 210 | SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15), | 210 | SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15), |
| @@ -222,7 +222,7 @@ SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6 | |||
| 222 | SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1), | 222 | SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1), |
| 223 | }; | 223 | }; |
| 224 | 224 | ||
| 225 | static unsigned char cmi8330_sb_init_values[][2] __initdata = { | 225 | static unsigned char cmi8330_sb_init_values[][2] __devinitdata = { |
| 226 | { SB_DSP4_MASTER_DEV + 0, 0 }, | 226 | { SB_DSP4_MASTER_DEV + 0, 0 }, |
| 227 | { SB_DSP4_MASTER_DEV + 1, 0 }, | 227 | { SB_DSP4_MASTER_DEV + 1, 0 }, |
| 228 | { SB_DSP4_PCM_DEV + 0, 0 }, | 228 | { SB_DSP4_PCM_DEV + 0, 0 }, |
| @@ -545,7 +545,7 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev) | |||
| 545 | return snd_card_register(card); | 545 | return snd_card_register(card); |
| 546 | } | 546 | } |
| 547 | 547 | ||
| 548 | static int __init snd_cmi8330_nonpnp_probe(struct platform_device *pdev) | 548 | static int __devinit snd_cmi8330_nonpnp_probe(struct platform_device *pdev) |
| 549 | { | 549 | { |
| 550 | struct snd_card *card; | 550 | struct snd_card *card; |
| 551 | int err; | 551 | int err; |
| @@ -607,6 +607,8 @@ static struct platform_driver snd_cmi8330_driver = { | |||
| 607 | 607 | ||
| 608 | 608 | ||
| 609 | #ifdef CONFIG_PNP | 609 | #ifdef CONFIG_PNP |
| 610 | static unsigned int __devinitdata cmi8330_pnp_devices; | ||
| 611 | |||
| 610 | static int __devinit snd_cmi8330_pnp_detect(struct pnp_card_link *pcard, | 612 | static int __devinit snd_cmi8330_pnp_detect(struct pnp_card_link *pcard, |
| 611 | const struct pnp_card_device_id *pid) | 613 | const struct pnp_card_device_id *pid) |
| 612 | { | 614 | { |
| @@ -636,6 +638,7 @@ static int __devinit snd_cmi8330_pnp_detect(struct pnp_card_link *pcard, | |||
| 636 | } | 638 | } |
| 637 | pnp_set_card_drvdata(pcard, card); | 639 | pnp_set_card_drvdata(pcard, card); |
| 638 | dev++; | 640 | dev++; |
| 641 | cmi8330_pnp_devices++; | ||
| 639 | return 0; | 642 | return 0; |
| 640 | } | 643 | } |
| 641 | 644 | ||
| @@ -706,9 +709,9 @@ static int __init alsa_card_cmi8330_init(void) | |||
| 706 | 709 | ||
| 707 | #ifdef CONFIG_PNP | 710 | #ifdef CONFIG_PNP |
| 708 | err = pnp_register_card_driver(&cmi8330_pnpc_driver); | 711 | err = pnp_register_card_driver(&cmi8330_pnpc_driver); |
| 709 | if (err >= 0) { | 712 | if (!err) { |
| 710 | pnp_registered = 1; | 713 | pnp_registered = 1; |
| 711 | cards += err; | 714 | cards += cmi8330_pnp_devices; |
| 712 | } | 715 | } |
| 713 | #endif | 716 | #endif |
| 714 | 717 | ||
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index 4060918e0327..382bb17ef49f 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c | |||
| @@ -133,6 +133,7 @@ static int pnpc_registered; | |||
| 133 | static int pnp_registered; | 133 | static int pnp_registered; |
| 134 | #endif | 134 | #endif |
| 135 | #endif /* CONFIG_PNP */ | 135 | #endif /* CONFIG_PNP */ |
| 136 | static unsigned int snd_cs423x_devices; | ||
| 136 | 137 | ||
| 137 | struct snd_card_cs4236 { | 138 | struct snd_card_cs4236 { |
| 138 | struct snd_cs4231 *chip; | 139 | struct snd_cs4231 *chip; |
| @@ -564,7 +565,7 @@ static int __init snd_cs423x_nonpnp_probe(struct platform_device *pdev) | |||
| 564 | snd_card_free(card); | 565 | snd_card_free(card); |
| 565 | return err; | 566 | return err; |
| 566 | } | 567 | } |
| 567 | 568 | ||
| 568 | platform_set_drvdata(pdev, card); | 569 | platform_set_drvdata(pdev, card); |
| 569 | return 0; | 570 | return 0; |
| 570 | } | 571 | } |
| @@ -650,6 +651,7 @@ static int __devinit snd_cs4232_pnpbios_detect(struct pnp_dev *pdev, | |||
| 650 | } | 651 | } |
| 651 | pnp_set_drvdata(pdev, card); | 652 | pnp_set_drvdata(pdev, card); |
| 652 | dev++; | 653 | dev++; |
| 654 | snd_cs423x_devices++; | ||
| 653 | return 0; | 655 | return 0; |
| 654 | } | 656 | } |
| 655 | 657 | ||
| @@ -713,6 +715,7 @@ static int __devinit snd_cs423x_pnpc_detect(struct pnp_card_link *pcard, | |||
| 713 | } | 715 | } |
| 714 | pnp_set_card_drvdata(pcard, card); | 716 | pnp_set_card_drvdata(pcard, card); |
| 715 | dev++; | 717 | dev++; |
| 718 | snd_cs423x_devices++; | ||
| 716 | return 0; | 719 | return 0; |
| 717 | } | 720 | } |
| 718 | 721 | ||
| @@ -721,7 +724,7 @@ static void __devexit snd_cs423x_pnpc_remove(struct pnp_card_link * pcard) | |||
| 721 | snd_card_free(pnp_get_card_drvdata(pcard)); | 724 | snd_card_free(pnp_get_card_drvdata(pcard)); |
| 722 | pnp_set_card_drvdata(pcard, NULL); | 725 | pnp_set_card_drvdata(pcard, NULL); |
| 723 | } | 726 | } |
| 724 | 727 | ||
| 725 | #ifdef CONFIG_PM | 728 | #ifdef CONFIG_PM |
| 726 | static int snd_cs423x_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state) | 729 | static int snd_cs423x_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state) |
| 727 | { | 730 | { |
| @@ -766,7 +769,7 @@ static void __init_or_module snd_cs423x_unregister_all(void) | |||
| 766 | 769 | ||
| 767 | static int __init alsa_card_cs423x_init(void) | 770 | static int __init alsa_card_cs423x_init(void) |
| 768 | { | 771 | { |
| 769 | int i, err, cards = 0; | 772 | int i, err; |
| 770 | 773 | ||
| 771 | if ((err = platform_driver_register(&cs423x_nonpnp_driver)) < 0) | 774 | if ((err = platform_driver_register(&cs423x_nonpnp_driver)) < 0) |
| 772 | return err; | 775 | return err; |
| @@ -782,24 +785,20 @@ static int __init alsa_card_cs423x_init(void) | |||
| 782 | goto errout; | 785 | goto errout; |
| 783 | } | 786 | } |
| 784 | platform_devices[i] = device; | 787 | platform_devices[i] = device; |
| 785 | cards++; | 788 | snd_cs423x_devices++; |
| 786 | } | 789 | } |
| 787 | #ifdef CONFIG_PNP | 790 | #ifdef CONFIG_PNP |
| 788 | #ifdef CS4232 | 791 | #ifdef CS4232 |
| 789 | i = pnp_register_driver(&cs4232_pnp_driver); | 792 | err = pnp_register_driver(&cs4232_pnp_driver); |
| 790 | if (i >= 0) { | 793 | if (!err) |
| 791 | pnp_registered = 1; | 794 | pnp_registered = 1; |
| 792 | cards += i; | ||
| 793 | } | ||
| 794 | #endif | 795 | #endif |
| 795 | i = pnp_register_card_driver(&cs423x_pnpc_driver); | 796 | err = pnp_register_card_driver(&cs423x_pnpc_driver); |
| 796 | if (i >= 0) { | 797 | if (!err) |
| 797 | pnpc_registered = 1; | 798 | pnpc_registered = 1; |
| 798 | cards += i; | ||
| 799 | } | ||
| 800 | #endif /* CONFIG_PNP */ | 799 | #endif /* CONFIG_PNP */ |
| 801 | 800 | ||
| 802 | if (!cards) { | 801 | if (!snd_cs423x_devices) { |
| 803 | #ifdef MODULE | 802 | #ifdef MODULE |
| 804 | printk(KERN_ERR IDENT " soundcard not found or device busy\n"); | 803 | printk(KERN_ERR IDENT " soundcard not found or device busy\n"); |
| 805 | #endif | 804 | #endif |
diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c index 50e7bc5ef561..0acb4e5da47f 100644 --- a/sound/isa/dt019x.c +++ b/sound/isa/dt019x.c | |||
| @@ -272,6 +272,8 @@ static int __devinit snd_card_dt019x_probe(int dev, struct pnp_card_link *pcard, | |||
| 272 | return 0; | 272 | return 0; |
| 273 | } | 273 | } |
| 274 | 274 | ||
| 275 | static unsigned int __devinitdata dt019x_devices; | ||
| 276 | |||
| 275 | static int __devinit snd_dt019x_pnp_probe(struct pnp_card_link *card, | 277 | static int __devinit snd_dt019x_pnp_probe(struct pnp_card_link *card, |
| 276 | const struct pnp_card_device_id *pid) | 278 | const struct pnp_card_device_id *pid) |
| 277 | { | 279 | { |
| @@ -285,6 +287,7 @@ static int __devinit snd_dt019x_pnp_probe(struct pnp_card_link *card, | |||
| 285 | if (res < 0) | 287 | if (res < 0) |
| 286 | return res; | 288 | return res; |
| 287 | dev++; | 289 | dev++; |
| 290 | dt019x_devices++; | ||
| 288 | return 0; | 291 | return 0; |
| 289 | } | 292 | } |
| 290 | return -ENODEV; | 293 | return -ENODEV; |
| @@ -336,10 +339,13 @@ static struct pnp_card_driver dt019x_pnpc_driver = { | |||
| 336 | 339 | ||
| 337 | static int __init alsa_card_dt019x_init(void) | 340 | static int __init alsa_card_dt019x_init(void) |
| 338 | { | 341 | { |
| 339 | int cards = 0; | 342 | int err; |
| 343 | |||
| 344 | err = pnp_register_card_driver(&dt019x_pnpc_driver); | ||
| 345 | if (err) | ||
| 346 | return err; | ||
| 340 | 347 | ||
| 341 | cards = pnp_register_card_driver(&dt019x_pnpc_driver); | 348 | if (!dt019x_devices) { |
| 342 | if (cards <= 0) { | ||
| 343 | pnp_unregister_card_driver(&dt019x_pnpc_driver); | 349 | pnp_unregister_card_driver(&dt019x_pnpc_driver); |
| 344 | #ifdef MODULE | 350 | #ifdef MODULE |
| 345 | snd_printk(KERN_ERR "no DT-019X / ALS-007 based soundcards found\n"); | 351 | snd_printk(KERN_ERR "no DT-019X / ALS-007 based soundcards found\n"); |
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 721955d26194..9fbc185b4cc2 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c | |||
| @@ -2204,7 +2204,7 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev) | |||
| 2204 | return snd_card_register(card); | 2204 | return snd_card_register(card); |
| 2205 | } | 2205 | } |
| 2206 | 2206 | ||
| 2207 | static int __init snd_es18xx_nonpnp_probe1(int dev, struct platform_device *devptr) | 2207 | static int __devinit snd_es18xx_nonpnp_probe1(int dev, struct platform_device *devptr) |
| 2208 | { | 2208 | { |
| 2209 | struct snd_card *card; | 2209 | struct snd_card *card; |
| 2210 | int err; | 2210 | int err; |
| @@ -2221,7 +2221,7 @@ static int __init snd_es18xx_nonpnp_probe1(int dev, struct platform_device *devp | |||
| 2221 | return 0; | 2221 | return 0; |
| 2222 | } | 2222 | } |
| 2223 | 2223 | ||
| 2224 | static int __init snd_es18xx_nonpnp_probe(struct platform_device *pdev) | 2224 | static int __devinit snd_es18xx_nonpnp_probe(struct platform_device *pdev) |
| 2225 | { | 2225 | { |
| 2226 | int dev = pdev->id; | 2226 | int dev = pdev->id; |
| 2227 | int err; | 2227 | int err; |
| @@ -2297,6 +2297,8 @@ static struct platform_driver snd_es18xx_nonpnp_driver = { | |||
| 2297 | 2297 | ||
| 2298 | 2298 | ||
| 2299 | #ifdef CONFIG_PNP | 2299 | #ifdef CONFIG_PNP |
| 2300 | static unsigned int __devinitdata es18xx_pnp_devices; | ||
| 2301 | |||
| 2300 | static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard, | 2302 | static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard, |
| 2301 | const struct pnp_card_device_id *pid) | 2303 | const struct pnp_card_device_id *pid) |
| 2302 | { | 2304 | { |
| @@ -2327,6 +2329,7 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard, | |||
| 2327 | 2329 | ||
| 2328 | pnp_set_card_drvdata(pcard, card); | 2330 | pnp_set_card_drvdata(pcard, card); |
| 2329 | dev++; | 2331 | dev++; |
| 2332 | es18xx_pnp_devices++; | ||
| 2330 | return 0; | 2333 | return 0; |
| 2331 | } | 2334 | } |
| 2332 | 2335 | ||
| @@ -2397,10 +2400,10 @@ static int __init alsa_card_es18xx_init(void) | |||
| 2397 | } | 2400 | } |
| 2398 | 2401 | ||
| 2399 | #ifdef CONFIG_PNP | 2402 | #ifdef CONFIG_PNP |
| 2400 | i = pnp_register_card_driver(&es18xx_pnpc_driver); | 2403 | err = pnp_register_card_driver(&es18xx_pnpc_driver); |
| 2401 | if (i >= 0) { | 2404 | if (!err) { |
| 2402 | pnp_registered = 1; | 2405 | pnp_registered = 1; |
| 2403 | cards += i; | 2406 | cards += es18xx_pnp_devices; |
| 2404 | } | 2407 | } |
| 2405 | #endif | 2408 | #endif |
| 2406 | 2409 | ||
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 2cacd0fa6871..de71b7a99c83 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c | |||
| @@ -791,7 +791,7 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev) | |||
| 791 | return 0; | 791 | return 0; |
| 792 | } | 792 | } |
| 793 | 793 | ||
| 794 | static int __init snd_interwave_nonpnp_probe1(int dev, struct platform_device *devptr) | 794 | static int __devinit snd_interwave_nonpnp_probe1(int dev, struct platform_device *devptr) |
| 795 | { | 795 | { |
| 796 | struct snd_card *card; | 796 | struct snd_card *card; |
| 797 | int err; | 797 | int err; |
| @@ -809,7 +809,7 @@ static int __init snd_interwave_nonpnp_probe1(int dev, struct platform_device *d | |||
| 809 | return 0; | 809 | return 0; |
| 810 | } | 810 | } |
| 811 | 811 | ||
| 812 | static int __init snd_interwave_nonpnp_probe(struct platform_device *pdev) | 812 | static int __devinit snd_interwave_nonpnp_probe(struct platform_device *pdev) |
| 813 | { | 813 | { |
| 814 | int dev = pdev->id; | 814 | int dev = pdev->id; |
| 815 | int err; | 815 | int err; |
| @@ -867,6 +867,7 @@ static struct platform_driver snd_interwave_driver = { | |||
| 867 | }; | 867 | }; |
| 868 | 868 | ||
| 869 | #ifdef CONFIG_PNP | 869 | #ifdef CONFIG_PNP |
| 870 | static unsigned int __devinitdata interwave_pnp_devices; | ||
| 870 | 871 | ||
| 871 | static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *pcard, | 872 | static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *pcard, |
| 872 | const struct pnp_card_device_id *pid) | 873 | const struct pnp_card_device_id *pid) |
| @@ -897,6 +898,7 @@ static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *pcard, | |||
| 897 | } | 898 | } |
| 898 | pnp_set_card_drvdata(pcard, card); | 899 | pnp_set_card_drvdata(pcard, card); |
| 899 | dev++; | 900 | dev++; |
| 901 | interwave_pnp_devices++; | ||
| 900 | return 0; | 902 | return 0; |
| 901 | } | 903 | } |
| 902 | 904 | ||
| @@ -954,10 +956,10 @@ static int __init alsa_card_interwave_init(void) | |||
| 954 | } | 956 | } |
| 955 | 957 | ||
| 956 | /* ISA PnP cards */ | 958 | /* ISA PnP cards */ |
| 957 | i = pnp_register_card_driver(&interwave_pnpc_driver); | 959 | err = pnp_register_card_driver(&interwave_pnpc_driver); |
| 958 | if (i >= 0) { | 960 | if (!err) { |
| 959 | pnp_registered = 1; | 961 | pnp_registered = 1; |
| 960 | cards += i; | 962 | cards += interwave_pnp_devices;; |
| 961 | } | 963 | } |
| 962 | 964 | ||
| 963 | if (!cards) { | 965 | if (!cards) { |
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 56fcd8a946a4..c906e205d7d5 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c | |||
| @@ -95,6 +95,7 @@ static struct platform_device *platform_devices[SNDRV_CARDS]; | |||
| 95 | static int pnp_registered; | 95 | static int pnp_registered; |
| 96 | static int pnpc_registered; | 96 | static int pnpc_registered; |
| 97 | #endif | 97 | #endif |
| 98 | static unsigned int snd_opl3sa2_devices; | ||
| 98 | 99 | ||
| 99 | /* control ports */ | 100 | /* control ports */ |
| 100 | #define OPL3SA2_PM_CTRL 0x01 | 101 | #define OPL3SA2_PM_CTRL 0x01 |
| @@ -760,6 +761,7 @@ static int __devinit snd_opl3sa2_pnp_detect(struct pnp_dev *pdev, | |||
| 760 | } | 761 | } |
| 761 | pnp_set_drvdata(pdev, card); | 762 | pnp_set_drvdata(pdev, card); |
| 762 | dev++; | 763 | dev++; |
| 764 | snd_opl3sa2_devices++; | ||
| 763 | return 0; | 765 | return 0; |
| 764 | } | 766 | } |
| 765 | 767 | ||
| @@ -826,6 +828,7 @@ static int __devinit snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard, | |||
| 826 | } | 828 | } |
| 827 | pnp_set_card_drvdata(pcard, card); | 829 | pnp_set_card_drvdata(pcard, card); |
| 828 | dev++; | 830 | dev++; |
| 831 | snd_opl3sa2_devices++; | ||
| 829 | return 0; | 832 | return 0; |
| 830 | } | 833 | } |
| 831 | 834 | ||
| @@ -944,7 +947,7 @@ static void __init_or_module snd_opl3sa2_unregister_all(void) | |||
| 944 | 947 | ||
| 945 | static int __init alsa_card_opl3sa2_init(void) | 948 | static int __init alsa_card_opl3sa2_init(void) |
| 946 | { | 949 | { |
| 947 | int i, err, cards = 0; | 950 | int i, err; |
| 948 | 951 | ||
| 949 | if ((err = platform_driver_register(&snd_opl3sa2_nonpnp_driver)) < 0) | 952 | if ((err = platform_driver_register(&snd_opl3sa2_nonpnp_driver)) < 0) |
| 950 | return err; | 953 | return err; |
| @@ -964,23 +967,19 @@ static int __init alsa_card_opl3sa2_init(void) | |||
| 964 | goto errout; | 967 | goto errout; |
| 965 | } | 968 | } |
| 966 | platform_devices[i] = device; | 969 | platform_devices[i] = device; |
| 967 | cards++; | 970 | snd_opl3sa2_devices++; |
| 968 | } | 971 | } |
| 969 | 972 | ||
| 970 | #ifdef CONFIG_PNP | 973 | #ifdef CONFIG_PNP |
| 971 | err = pnp_register_driver(&opl3sa2_pnp_driver); | 974 | err = pnp_register_driver(&opl3sa2_pnp_driver); |
| 972 | if (err >= 0) { | 975 | if (!err) |
| 973 | pnp_registered = 1; | 976 | pnp_registered = 1; |
| 974 | cards += err; | ||
| 975 | } | ||
| 976 | err = pnp_register_card_driver(&opl3sa2_pnpc_driver); | 977 | err = pnp_register_card_driver(&opl3sa2_pnpc_driver); |
| 977 | if (err >= 0) { | 978 | if (!err) |
| 978 | pnpc_registered = 1; | 979 | pnpc_registered = 1; |
| 979 | cards += err; | ||
| 980 | } | ||
| 981 | #endif | 980 | #endif |
| 982 | 981 | ||
| 983 | if (!cards) { | 982 | if (!snd_opl3sa2_devices) { |
| 984 | #ifdef MODULE | 983 | #ifdef MODULE |
| 985 | snd_printk(KERN_ERR "Yamaha OPL3-SA soundcard not found or device busy\n"); | 984 | snd_printk(KERN_ERR "Yamaha OPL3-SA soundcard not found or device busy\n"); |
| 986 | #endif | 985 | #endif |
diff --git a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c index 9da80bfa3027..d4d65b84265a 100644 --- a/sound/isa/sb/es968.c +++ b/sound/isa/sb/es968.c | |||
| @@ -124,7 +124,7 @@ static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard, | |||
| 124 | return 0; | 124 | return 0; |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | static int __init snd_card_es968_probe(int dev, | 127 | static int __devinit snd_card_es968_probe(int dev, |
| 128 | struct pnp_card_link *pcard, | 128 | struct pnp_card_link *pcard, |
| 129 | const struct pnp_card_device_id *pid) | 129 | const struct pnp_card_device_id *pid) |
| 130 | { | 130 | { |
| @@ -182,6 +182,8 @@ static int __init snd_card_es968_probe(int dev, | |||
| 182 | return 0; | 182 | return 0; |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | static unsigned int __devinitdata es968_devices; | ||
| 186 | |||
| 185 | static int __devinit snd_es968_pnp_detect(struct pnp_card_link *card, | 187 | static int __devinit snd_es968_pnp_detect(struct pnp_card_link *card, |
| 186 | const struct pnp_card_device_id *id) | 188 | const struct pnp_card_device_id *id) |
| 187 | { | 189 | { |
| @@ -195,6 +197,7 @@ static int __devinit snd_es968_pnp_detect(struct pnp_card_link *card, | |||
| 195 | if (res < 0) | 197 | if (res < 0) |
| 196 | return res; | 198 | return res; |
| 197 | dev++; | 199 | dev++; |
| 200 | es968_devices++; | ||
| 198 | return 0; | 201 | return 0; |
| 199 | } | 202 | } |
| 200 | return -ENODEV; | 203 | return -ENODEV; |
| @@ -246,8 +249,11 @@ static struct pnp_card_driver es968_pnpc_driver = { | |||
| 246 | 249 | ||
| 247 | static int __init alsa_card_es968_init(void) | 250 | static int __init alsa_card_es968_init(void) |
| 248 | { | 251 | { |
| 249 | int cards = pnp_register_card_driver(&es968_pnpc_driver); | 252 | int err = pnp_register_card_driver(&es968_pnpc_driver); |
| 250 | if (cards <= 0) { | 253 | if (err) |
| 254 | return err; | ||
| 255 | |||
| 256 | if (!es968_devices) { | ||
| 251 | pnp_unregister_card_driver(&es968_pnpc_driver); | 257 | pnp_unregister_card_driver(&es968_pnpc_driver); |
| 252 | #ifdef MODULE | 258 | #ifdef MODULE |
| 253 | snd_printk(KERN_ERR "no ES968 based soundcards found\n"); | 259 | snd_printk(KERN_ERR "no ES968 based soundcards found\n"); |
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c index 5737ab76160c..21ea65925a9e 100644 --- a/sound/isa/sb/sb16.c +++ b/sound/isa/sb/sb16.c | |||
| @@ -369,7 +369,7 @@ static struct snd_card *snd_sb16_card_new(int dev) | |||
| 369 | return card; | 369 | return card; |
| 370 | } | 370 | } |
| 371 | 371 | ||
| 372 | static int __init snd_sb16_probe(struct snd_card *card, int dev) | 372 | static int __devinit snd_sb16_probe(struct snd_card *card, int dev) |
| 373 | { | 373 | { |
| 374 | int xirq, xdma8, xdma16; | 374 | int xirq, xdma8, xdma16; |
| 375 | struct snd_sb *chip; | 375 | struct snd_sb *chip; |
| @@ -518,7 +518,7 @@ static int snd_sb16_resume(struct snd_card *card) | |||
| 518 | } | 518 | } |
| 519 | #endif | 519 | #endif |
| 520 | 520 | ||
| 521 | static int __init snd_sb16_nonpnp_probe1(int dev, struct platform_device *devptr) | 521 | static int __devinit snd_sb16_nonpnp_probe1(int dev, struct platform_device *devptr) |
| 522 | { | 522 | { |
| 523 | struct snd_card_sb16 *acard; | 523 | struct snd_card_sb16 *acard; |
| 524 | struct snd_card *card; | 524 | struct snd_card *card; |
| @@ -548,7 +548,7 @@ static int __init snd_sb16_nonpnp_probe1(int dev, struct platform_device *devptr | |||
| 548 | } | 548 | } |
| 549 | 549 | ||
| 550 | 550 | ||
| 551 | static int __init snd_sb16_nonpnp_probe(struct platform_device *pdev) | 551 | static int __devinit snd_sb16_nonpnp_probe(struct platform_device *pdev) |
| 552 | { | 552 | { |
| 553 | int dev = pdev->id; | 553 | int dev = pdev->id; |
| 554 | int err; | 554 | int err; |
| @@ -629,6 +629,7 @@ static struct platform_driver snd_sb16_nonpnp_driver = { | |||
| 629 | 629 | ||
| 630 | 630 | ||
| 631 | #ifdef CONFIG_PNP | 631 | #ifdef CONFIG_PNP |
| 632 | static unsigned int __devinitdata sb16_pnp_devices; | ||
| 632 | 633 | ||
| 633 | static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard, | 634 | static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard, |
| 634 | const struct pnp_card_device_id *pid) | 635 | const struct pnp_card_device_id *pid) |
| @@ -651,6 +652,7 @@ static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard, | |||
| 651 | } | 652 | } |
| 652 | pnp_set_card_drvdata(pcard, card); | 653 | pnp_set_card_drvdata(pcard, card); |
| 653 | dev++; | 654 | dev++; |
| 655 | sb16_pnp_devices++; | ||
| 654 | return 0; | 656 | return 0; |
| 655 | } | 657 | } |
| 656 | 658 | ||
| @@ -727,10 +729,10 @@ static int __init alsa_card_sb16_init(void) | |||
| 727 | } | 729 | } |
| 728 | #ifdef CONFIG_PNP | 730 | #ifdef CONFIG_PNP |
| 729 | /* PnP cards at last */ | 731 | /* PnP cards at last */ |
| 730 | i = pnp_register_card_driver(&sb16_pnpc_driver); | 732 | err = pnp_register_card_driver(&sb16_pnpc_driver); |
| 731 | if (i >= 0) { | 733 | if (!err) { |
| 732 | pnp_registered = 1; | 734 | pnp_registered = 1; |
| 733 | cards += i; | 735 | cards += sb16_pnp_devices; |
| 734 | } | 736 | } |
| 735 | #endif | 737 | #endif |
| 736 | 738 | ||
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 29bba8cc3ef3..48e5552d3444 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c | |||
| @@ -1255,7 +1255,7 @@ static int __devinit create_sscape(int dev, struct snd_card **rcardp) | |||
| 1255 | } | 1255 | } |
| 1256 | 1256 | ||
| 1257 | 1257 | ||
| 1258 | static int __init snd_sscape_probe(struct platform_device *pdev) | 1258 | static int __devinit snd_sscape_probe(struct platform_device *pdev) |
| 1259 | { | 1259 | { |
| 1260 | int dev = pdev->id; | 1260 | int dev = pdev->id; |
| 1261 | struct snd_card *card; | 1261 | struct snd_card *card; |
| @@ -1469,7 +1469,7 @@ static int __init sscape_init(void) | |||
| 1469 | if (ret < 0) | 1469 | if (ret < 0) |
| 1470 | return ret; | 1470 | return ret; |
| 1471 | #ifdef CONFIG_PNP | 1471 | #ifdef CONFIG_PNP |
| 1472 | if (pnp_register_card_driver(&sscape_pnpc_driver) >= 0) | 1472 | if (pnp_register_card_driver(&sscape_pnpc_driver) == 0) |
| 1473 | pnp_registered = 1; | 1473 | pnp_registered = 1; |
| 1474 | #endif | 1474 | #endif |
| 1475 | return 0; | 1475 | return 0; |
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index c0115bf9065e..2f13cd5d4dcb 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c | |||
| @@ -589,7 +589,7 @@ snd_wavefront_probe (struct snd_card *card, int dev) | |||
| 589 | return snd_card_register(card); | 589 | return snd_card_register(card); |
| 590 | } | 590 | } |
| 591 | 591 | ||
| 592 | static int __init snd_wavefront_nonpnp_probe(struct platform_device *pdev) | 592 | static int __devinit snd_wavefront_nonpnp_probe(struct platform_device *pdev) |
| 593 | { | 593 | { |
| 594 | int dev = pdev->id; | 594 | int dev = pdev->id; |
| 595 | struct snd_card *card; | 595 | struct snd_card *card; |
| @@ -637,6 +637,7 @@ static struct platform_driver snd_wavefront_driver = { | |||
| 637 | 637 | ||
| 638 | 638 | ||
| 639 | #ifdef CONFIG_PNP | 639 | #ifdef CONFIG_PNP |
| 640 | static unsigned int __devinitdata wavefront_pnp_devices; | ||
| 640 | 641 | ||
| 641 | static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard, | 642 | static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard, |
| 642 | const struct pnp_card_device_id *pid) | 643 | const struct pnp_card_device_id *pid) |
| @@ -670,6 +671,7 @@ static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard, | |||
| 670 | 671 | ||
| 671 | pnp_set_card_drvdata(pcard, card); | 672 | pnp_set_card_drvdata(pcard, card); |
| 672 | dev++; | 673 | dev++; |
| 674 | wavefront_pnp_devices++; | ||
| 673 | return 0; | 675 | return 0; |
| 674 | } | 676 | } |
| 675 | 677 | ||
| @@ -729,10 +731,10 @@ static int __init alsa_card_wavefront_init(void) | |||
| 729 | } | 731 | } |
| 730 | 732 | ||
| 731 | #ifdef CONFIG_PNP | 733 | #ifdef CONFIG_PNP |
| 732 | i = pnp_register_card_driver(&wavefront_pnpc_driver); | 734 | err = pnp_register_card_driver(&wavefront_pnpc_driver); |
| 733 | if (i >= 0) { | 735 | if (!err) { |
| 734 | pnp_registered = 1; | 736 | pnp_registered = 1; |
| 735 | cards += i; | 737 | cards += wavefront_pnp_devices; |
| 736 | } | 738 | } |
| 737 | #endif | 739 | #endif |
| 738 | 740 | ||
diff --git a/sound/oss/cs4232.c b/sound/oss/cs4232.c index 7c59e2d4003a..c7f86f09c28d 100644 --- a/sound/oss/cs4232.c +++ b/sound/oss/cs4232.c | |||
| @@ -360,6 +360,8 @@ static int __initdata synthio = -1; | |||
| 360 | static int __initdata synthirq = -1; | 360 | static int __initdata synthirq = -1; |
| 361 | static int __initdata isapnp = 1; | 361 | static int __initdata isapnp = 1; |
| 362 | 362 | ||
| 363 | static unsigned int cs4232_devices; | ||
| 364 | |||
| 363 | MODULE_DESCRIPTION("CS4232 based soundcard driver"); | 365 | MODULE_DESCRIPTION("CS4232 based soundcard driver"); |
| 364 | MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis"); | 366 | MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis"); |
| 365 | MODULE_LICENSE("GPL"); | 367 | MODULE_LICENSE("GPL"); |
| @@ -421,6 +423,7 @@ static int cs4232_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev | |||
| 421 | return -ENODEV; | 423 | return -ENODEV; |
| 422 | } | 424 | } |
| 423 | pnp_set_drvdata(dev,isapnpcfg); | 425 | pnp_set_drvdata(dev,isapnpcfg); |
| 426 | cs4232_devices++; | ||
| 424 | return 0; | 427 | return 0; |
| 425 | } | 428 | } |
| 426 | 429 | ||
| @@ -455,10 +458,11 @@ static int __init init_cs4232(void) | |||
| 455 | #endif | 458 | #endif |
| 456 | cfg.irq = -1; | 459 | cfg.irq = -1; |
| 457 | 460 | ||
| 458 | if (isapnp && | 461 | if (isapnp) { |
| 459 | (pnp_register_driver(&cs4232_driver) > 0) | 462 | pnp_register_driver(&cs4232_driver); |
| 460 | ) | 463 | if (cs4232_devices) |
| 461 | return 0; | 464 | return 0; |
| 465 | } | ||
| 462 | 466 | ||
| 463 | if(io==-1||irq==-1||dma==-1) | 467 | if(io==-1||irq==-1||dma==-1) |
| 464 | { | 468 | { |
| @@ -503,7 +507,8 @@ static int __init setup_cs4232(char *str) | |||
| 503 | int ints[7]; | 507 | int ints[7]; |
| 504 | 508 | ||
| 505 | /* If we have isapnp cards, no need for options */ | 509 | /* If we have isapnp cards, no need for options */ |
| 506 | if (pnp_register_driver(&cs4232_driver) > 0) | 510 | pnp_register_driver(&cs4232_driver); |
| 511 | if (cs4232_devices) | ||
| 507 | return 1; | 512 | return 1; |
| 508 | 513 | ||
| 509 | str = get_options(str, ARRAY_SIZE(ints), ints); | 514 | str = get_options(str, ARRAY_SIZE(ints), ints); |
diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c index 680b82e15298..d38e88abc8fa 100644 --- a/sound/oss/sb_card.c +++ b/sound/oss/sb_card.c | |||
| @@ -52,6 +52,7 @@ static int __initdata sm_games = 0; /* Logitech soundman games? */ | |||
| 52 | static struct sb_card_config *legacy = NULL; | 52 | static struct sb_card_config *legacy = NULL; |
| 53 | 53 | ||
| 54 | #ifdef CONFIG_PNP | 54 | #ifdef CONFIG_PNP |
| 55 | static int pnp_registered; | ||
| 55 | static int __initdata pnp = 1; | 56 | static int __initdata pnp = 1; |
| 56 | /* | 57 | /* |
| 57 | static int __initdata uart401 = 0; | 58 | static int __initdata uart401 = 0; |
| @@ -133,7 +134,7 @@ static void sb_unload(struct sb_card_config *scc) | |||
| 133 | } | 134 | } |
| 134 | 135 | ||
| 135 | /* Register legacy card with OSS subsystem */ | 136 | /* Register legacy card with OSS subsystem */ |
| 136 | static int sb_init_legacy(void) | 137 | static int __init sb_init_legacy(void) |
| 137 | { | 138 | { |
| 138 | struct sb_module_options sbmo = {0}; | 139 | struct sb_module_options sbmo = {0}; |
| 139 | 140 | ||
| @@ -234,6 +235,8 @@ static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc) | |||
| 234 | } | 235 | } |
| 235 | } | 236 | } |
| 236 | 237 | ||
| 238 | static unsigned int sb_pnp_devices; | ||
| 239 | |||
| 237 | /* Probe callback function for the PnP API */ | 240 | /* Probe callback function for the PnP API */ |
| 238 | static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id) | 241 | static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id) |
| 239 | { | 242 | { |
| @@ -264,6 +267,7 @@ static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device | |||
| 264 | scc->conf.dma, scc->conf.dma2); | 267 | scc->conf.dma, scc->conf.dma2); |
| 265 | 268 | ||
| 266 | pnp_set_card_drvdata(card, scc); | 269 | pnp_set_card_drvdata(card, scc); |
| 270 | sb_pnp_devices++; | ||
| 267 | 271 | ||
| 268 | return sb_register_oss(scc, &sbmo); | 272 | return sb_register_oss(scc, &sbmo); |
| 269 | } | 273 | } |
| @@ -289,6 +293,14 @@ static struct pnp_card_driver sb_pnp_driver = { | |||
| 289 | MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table); | 293 | MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table); |
| 290 | #endif /* CONFIG_PNP */ | 294 | #endif /* CONFIG_PNP */ |
| 291 | 295 | ||
| 296 | static void __init_or_module sb_unregister_all(void) | ||
| 297 | { | ||
| 298 | #ifdef CONFIG_PNP | ||
| 299 | if (pnp_registered) | ||
| 300 | pnp_unregister_card_driver(&sb_pnp_driver); | ||
| 301 | #endif | ||
| 302 | } | ||
| 303 | |||
| 292 | static int __init sb_init(void) | 304 | static int __init sb_init(void) |
| 293 | { | 305 | { |
| 294 | int lres = 0; | 306 | int lres = 0; |
| @@ -307,17 +319,18 @@ static int __init sb_init(void) | |||
| 307 | 319 | ||
| 308 | #ifdef CONFIG_PNP | 320 | #ifdef CONFIG_PNP |
| 309 | if(pnp) { | 321 | if(pnp) { |
| 310 | pres = pnp_register_card_driver(&sb_pnp_driver); | 322 | int err = pnp_register_card_driver(&sb_pnp_driver); |
| 323 | if (!err) | ||
| 324 | pnp_registered = 1; | ||
| 325 | pres = sb_pnp_devices; | ||
| 311 | } | 326 | } |
| 312 | #endif | 327 | #endif |
| 313 | printk(KERN_INFO "sb: Init: Done\n"); | 328 | printk(KERN_INFO "sb: Init: Done\n"); |
| 314 | 329 | ||
| 315 | /* If either PnP or Legacy registered a card then return | 330 | /* If either PnP or Legacy registered a card then return |
| 316 | * success */ | 331 | * success */ |
| 317 | if (pres <= 0 && lres <= 0) { | 332 | if (pres == 0 && lres <= 0) { |
| 318 | #ifdef CONFIG_PNP | 333 | sb_unregister_all(); |
| 319 | pnp_unregister_card_driver(&sb_pnp_driver); | ||
| 320 | #endif | ||
| 321 | return -ENODEV; | 334 | return -ENODEV; |
| 322 | } | 335 | } |
| 323 | return 0; | 336 | return 0; |
| @@ -333,9 +346,7 @@ static void __exit sb_exit(void) | |||
| 333 | sb_unload(legacy); | 346 | sb_unload(legacy); |
| 334 | } | 347 | } |
| 335 | 348 | ||
| 336 | #ifdef CONFIG_PNP | 349 | sb_unregister_all(); |
| 337 | pnp_unregister_card_driver(&sb_pnp_driver); | ||
| 338 | #endif | ||
| 339 | 350 | ||
| 340 | if (smw_free) { | 351 | if (smw_free) { |
| 341 | vfree(smw_free); | 352 | vfree(smw_free); |
