diff options
117 files changed, 4600 insertions, 374 deletions
diff --git a/Documentation/DMA-mapping.txt b/Documentation/DMA-mapping.txt index 7c717699032..63392c9132b 100644 --- a/Documentation/DMA-mapping.txt +++ b/Documentation/DMA-mapping.txt @@ -698,12 +698,12 @@ these interfaces. Remember that, as defined, consistent mappings are always going to be SAC addressable. The first thing your driver needs to do is query the PCI platform -layer with your devices DAC addressing capabilities: +layer if it is capable of handling your devices DAC addressing +capabilities: - int pci_dac_set_dma_mask(struct pci_dev *pdev, u64 mask); + int pci_dac_dma_supported(struct pci_dev *hwdev, u64 mask); -This routine behaves identically to pci_set_dma_mask. You may not -use the following interfaces if this routine fails. +You may not use the following interfaces if this routine fails. Next, DMA addresses using this API are kept track of using the dma64_addr_t type. It is guaranteed to be big enough to hold any diff --git a/Documentation/accounting/delay-accounting.txt b/Documentation/accounting/delay-accounting.txt new file mode 100644 index 00000000000..be215e58423 --- /dev/null +++ b/Documentation/accounting/delay-accounting.txt @@ -0,0 +1,110 @@ +Delay accounting +---------------- + +Tasks encounter delays in execution when they wait +for some kernel resource to become available e.g. a +runnable task may wait for a free CPU to run on. + +The per-task delay accounting functionality measures +the delays experienced by a task while + +a) waiting for a CPU (while being runnable) +b) completion of synchronous block I/O initiated by the task +c) swapping in pages + +and makes these statistics available to userspace through +the taskstats interface. + +Such delays provide feedback for setting a task's cpu priority, +io priority and rss limit values appropriately. Long delays for +important tasks could be a trigger for raising its corresponding priority. + +The functionality, through its use of the taskstats interface, also provides +delay statistics aggregated for all tasks (or threads) belonging to a +thread group (corresponding to a traditional Unix process). This is a commonly +needed aggregation that is more efficiently done by the kernel. + +Userspace utilities, particularly resource management applications, can also +aggregate delay statistics into arbitrary groups. To enable this, delay +statistics of a task are available both during its lifetime as well as on its +exit, ensuring continuous and complete monitoring can be done. + + +Interface +--------- + +Delay accounting uses the taskstats interface which is described +in detail in a separate document in this directory. Taskstats returns a +generic data structure to userspace corresponding to per-pid and per-tgid +statistics. The delay accounting functionality populates specific fields of +this structure. See + include/linux/taskstats.h +for a description of the fields pertaining to delay accounting. +It will generally be in the form of counters returning the cumulative +delay seen for cpu, sync block I/O, swapin etc. + +Taking the difference of two successive readings of a given +counter (say cpu_delay_total) for a task will give the delay +experienced by the task waiting for the corresponding resource +in that interval. + +When a task exits, records containing the per-task statistics +are sent to userspace without requiring a command. If it is the last exiting +task of a thread group, the per-tgid statistics are also sent. More details +are given in the taskstats interface description. + +The getdelays.c userspace utility in this directory allows simple commands to +be run and the corresponding delay statistics to be displayed. It also serves +as an example of using the taskstats interface. + +Usage +----- + +Compile the kernel with + CONFIG_TASK_DELAY_ACCT=y + CONFIG_TASKSTATS=y + +Enable the accounting at boot time by adding +the following to the kernel boot options + delayacct + +and after the system has booted up, use a utility +similar to getdelays.c to access the delays +seen by a given task or a task group (tgid). +The utility also allows a given command to be +executed and the corresponding delays to be +seen. + +General format of the getdelays command + +getdelays [-t tgid] [-p pid] [-c cmd...] + + +Get delays, since system boot, for pid 10 +# ./getdelays -p 10 +(output similar to next case) + +Get sum of delays, since system boot, for all pids with tgid 5 +# ./getdelays -t 5 + + +CPU count real total virtual total delay total + 7876 92005750 100000000 24001500 +IO count delay total + 0 0 +MEM count delay total + 0 0 + +Get delays seen in executing a given simple command +# ./getdelays -c ls / + +bin data1 data3 data5 dev home media opt root srv sys usr +boot data2 data4 data6 etc lib mnt proc sbin subdomain tmp var + + +CPU count real total virtual total delay total + 6 4000250 4000000 0 +IO count delay total + 0 0 +MEM count delay total + 0 0 diff --git a/Documentation/accounting/getdelays.c b/Documentation/accounting/getdelays.c new file mode 100644 index 00000000000..795ca3911cc --- /dev/null +++ b/Documentation/accounting/getdelays.c @@ -0,0 +1,396 @@ +/* getdelays.c + * + * Utility to get per-pid and per-tgid delay accounting statistics + * Also illustrates usage of the taskstats interface + * + * Copyright (C) Shailabh Nagar, IBM Corp. 2005 + * Copyright (C) Balbir Singh, IBM Corp. 2006 + * Copyright (c) Jay Lan, SGI. 2006 + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <poll.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <signal.h> + +#include <linux/genetlink.h> +#include <linux/taskstats.h> + +/* + * Generic macros for dealing with netlink sockets. Might be duplicated + * elsewhere. It is recommended that commercial grade applications use + * libnl or libnetlink and use the interfaces provided by the library + */ +#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) +#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) +#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) +#define NLA_PAYLOAD(len) (len - NLA_HDRLEN) + +#define err(code, fmt, arg...) do { printf(fmt, ##arg); exit(code); } while (0) +int done = 0; +int rcvbufsz=0; + + char name[100]; +int dbg=0, print_delays=0; +__u64 stime, utime; +#define PRINTF(fmt, arg...) { \ + if (dbg) { \ + printf(fmt, ##arg); \ + } \ + } + +/* Maximum size of response requested or message sent */ +#define MAX_MSG_SIZE 256 +/* Maximum number of cpus expected to be specified in a cpumask */ +#define MAX_CPUS 32 +/* Maximum length of pathname to log file */ +#define MAX_FILENAME 256 + +struct msgtemplate { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[MAX_MSG_SIZE]; +}; + +char cpumask[100+6*MAX_CPUS]; + +/* + * Create a raw netlink socket and bind + */ +static int create_nl_socket(int protocol) +{ + int fd; + struct sockaddr_nl local; + + fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (fd < 0) + return -1; + + if (rcvbufsz) + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, + &rcvbufsz, sizeof(rcvbufsz)) < 0) { + printf("Unable to set socket rcv buf size to %d\n", + rcvbufsz); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) + goto error; + + return fd; +error: + close(fd); + return -1; +} + + +int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, + __u8 genl_cmd, __u16 nla_type, + void *nla_data, int nla_len) +{ + struct nlattr *na; + struct sockaddr_nl nladdr; + int r, buflen; + char *buf; + + struct msgtemplate msg; + + msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + msg.n.nlmsg_type = nlmsg_type; + msg.n.nlmsg_flags = NLM_F_REQUEST; + msg.n.nlmsg_seq = 0; + msg.n.nlmsg_pid = nlmsg_pid; + msg.g.cmd = genl_cmd; + msg.g.version = 0x1; + na = (struct nlattr *) GENLMSG_DATA(&msg); + na->nla_type = nla_type; + na->nla_len = nla_len + 1 + NLA_HDRLEN; + memcpy(NLA_DATA(na), nla_data, nla_len); + msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); + + buf = (char *) &msg; + buflen = msg.n.nlmsg_len ; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr, + sizeof(nladdr))) < buflen) { + if (r > 0) { + buf += r; + buflen -= r; + } else if (errno != EAGAIN) + return -1; + } + return 0; +} + + +/* + * Probe the controller in genetlink to find the family id + * for the TASKSTATS family + */ +int get_family_id(int sd) +{ + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[256]; + } ans; + + int id, rc; + struct nlattr *na; + int rep_len; + + strcpy(name, TASKSTATS_GENL_NAME); + rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, + CTRL_ATTR_FAMILY_NAME, (void *)name, + strlen(TASKSTATS_GENL_NAME)+1); + + rep_len = recv(sd, &ans, sizeof(ans), 0); + if (ans.n.nlmsg_type == NLMSG_ERROR || + (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) + return 0; + + na = (struct nlattr *) GENLMSG_DATA(&ans); + na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); + if (na->nla_type == CTRL_ATTR_FAMILY_ID) { + id = *(__u16 *) NLA_DATA(na); + } + return id; +} + +void print_delayacct(struct taskstats *t) +{ + printf("\n\nCPU %15s%15s%15s%15s\n" + " %15llu%15llu%15llu%15llu\n" + "IO %15s%15s\n" + " %15llu%15llu\n" + "MEM %15s%15s\n" + " %15llu%15llu\n\n", + "count", "real total", "virtual total", "delay total", + t->cpu_count, t->cpu_run_real_total, t->cpu_run_virtual_total, + t->cpu_delay_total, + "count", "delay total", + t->blkio_count, t->blkio_delay_total, + "count", "delay total", t->swapin_count, t->swapin_delay_total); +} + +int main(int argc, char *argv[]) +{ + int c, rc, rep_len, aggr_len, len2, cmd_type; + __u16 id; + __u32 mypid; + + struct nlattr *na; + int nl_sd = -1; + int len = 0; + pid_t tid = 0; + pid_t rtid = 0; + + int fd = 0; + int count = 0; + int write_file = 0; + int maskset = 0; + char logfile[128]; + int loop = 0; + + struct msgtemplate msg; + + while (1) { + c = getopt(argc, argv, "dw:r:m:t:p:v:l"); + if (c < 0) + break; + + switch (c) { + case 'd': + printf("print delayacct stats ON\n"); + print_delays = 1; + break; + case 'w': + strncpy(logfile, optarg, MAX_FILENAME); + printf("write to file %s\n", logfile); + write_file = 1; + break; + case 'r': + rcvbufsz = atoi(optarg); + printf("receive buf size %d\n", rcvbufsz); + if (rcvbufsz < 0) + err(1, "Invalid rcv buf size\n"); + break; + case 'm': + strncpy(cpumask, optarg, sizeof(cpumask)); + maskset = 1; + printf("cpumask %s maskset %d\n", cpumask, maskset); + break; + case 't': + tid = atoi(optarg); + if (!tid) + err(1, "Invalid tgid\n"); + cmd_type = TASKSTATS_CMD_ATTR_TGID; + print_delays = 1; + break; + case 'p': + tid = atoi(optarg); + if (!tid) + err(1, "Invalid pid\n"); + cmd_type = TASKSTATS_CMD_ATTR_PID; + print_delays = 1; + break; + case 'v': + printf("debug on\n"); + dbg = 1; + break; + case 'l': + printf("listen forever\n"); + loop = 1; + break; + default: + printf("Unknown option %d\n", c); + exit(-1); + } + } + + if (write_file) { + fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) { + perror("Cannot open output file\n"); + exit(1); + } + } + + if ((nl_sd = create_nl_socket(NETLINK_GENERIC)) < 0) + err(1, "error creating Netlink socket\n"); + + + mypid = getpid(); + id = get_family_id(nl_sd); + if (!id) { + printf("Error getting family id, errno %d", errno); + goto err; + } + PRINTF("family id %d\n", id); + + if (maskset) { + rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, + TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, + &cpumask, sizeof(cpumask)); + PRINTF("Sent register cpumask, retval %d\n", rc); + if (rc < 0) { + printf("error sending register cpumask\n"); + goto err; + } + } + + if (tid) { + rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, + cmd_type, &tid, sizeof(__u32)); + PRINTF("Sent pid/tgid, retval %d\n", rc); + if (rc < 0) { + printf("error sending tid/tgid cmd\n"); + goto done; + } + } + + do { + int i; + + rep_len = recv(nl_sd, &msg, sizeof(msg), 0); + PRINTF("received %d bytes\n", rep_len); + + if (rep_len < 0) { + printf("nonfatal reply error: errno %d\n", errno); + continue; + } + if (msg.n.nlmsg_type == NLMSG_ERROR || + !NLMSG_OK((&msg.n), rep_len)) { + printf("fatal reply error, errno %d\n", errno); + goto done; + } + + PRINTF("nlmsghdr size=%d, nlmsg_len=%d, rep_len=%d\n", + sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len); + + + rep_len = GENLMSG_PAYLOAD(&msg.n); + + na = (struct nlattr *) GENLMSG_DATA(&msg); + len = 0; + i = 0; + while (len < rep_len) { + len += NLA_ALIGN(na->nla_len); + switch (na->nla_type) { + case TASKSTATS_TYPE_AGGR_TGID: + /* Fall through */ + case TASKSTATS_TYPE_AGGR_PID: + aggr_len = NLA_PAYLOAD(na->nla_len); + len2 = 0; + /* For nested attributes, na follows */ + na = (struct nlattr *) NLA_DATA(na); + done = 0; + while (len2 < aggr_len) { + switch (na->nla_type) { + case TASKSTATS_TYPE_PID: + rtid = *(int *) NLA_DATA(na); + if (print_delays) + printf("PID\t%d\n", rtid); + break; + case TASKSTATS_TYPE_TGID: + rtid = *(int *) NLA_DATA(na); + if (print_delays) + printf("TGID\t%d\n", rtid); + break; + case TASKSTATS_TYPE_STATS: + count++; + if (print_delays) + print_delayacct((struct taskstats *) NLA_DATA(na)); + if (fd) { + if (write(fd, NLA_DATA(na), na->nla_len) < 0) { + err(1,"write error\n"); + } + } + if (!loop) + goto done; + break; + default: + printf("Unknown nested nla_type %d\n", na->nla_type); + break; + } + len2 += NLA_ALIGN(na->nla_len); + na = (struct nlattr *) ((char *) na + len2); + } + break; + + default: + printf("Unknown nla_type %d\n", na->nla_type); + break; + } + na = (struct nlattr *) (GENLMSG_DATA(&msg) + len); + } + } while (loop); +done: + if (maskset) { + rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, + TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, + &cpumask, sizeof(cpumask)); + printf("Sent deregister mask, retval %d\n", rc); + if (rc < 0) + err(rc, "error sending deregister cpumask\n"); + } +err: + close(nl_sd); + if (fd) + close(fd); + return 0; +} diff --git a/Documentation/accounting/taskstats.txt b/Documentation/accounting/taskstats.txt new file mode 100644 index 00000000000..92ebf29e904 --- /dev/null +++ b/Documentation/accounting/taskstats.txt @@ -0,0 +1,181 @@ +Per-task statistics interface +----------------------------- + + +Taskstats is a netlink-based interface for sending per-task and +per-process statistics from the kernel to userspace. + +Taskstats was designed for the following benefits: + +- efficiently provide statistics during lifetime of a task and on its exit +- unified interface for multiple accounting subsystems +- extensibility for use by future accounting patches + +Terminology +----------- + +"pid", "tid" and "task" are used interchangeably and refer to the standard +Linux task defined by struct task_struct. per-pid stats are the same as +per-task stats. + +"tgid", "process" and "thread group" are used interchangeably and refer to the +tasks that share an mm_struct i.e. the traditional Unix process. Despite the +use of tgid, there is no special treatment for the task that is thread group +leader - a process is deemed alive as long as it has any task belonging to it. + +Usage +----- + +To get statistics during a task's lifetime, userspace opens a unicast netlink +socket (NETLINK_GENERIC family) and sends commands specifying a pid or a tgid. +The response contains statistics for a task (if pid is specified) or the sum of +statistics for all tasks of the process (if tgid is specified). + +To obtain statistics for tasks which are exiting, the userspace listener +sends a register command and specifies a cpumask. Whenever a task exits on +one of the cpus in the cpumask, its per-pid statistics are sent to the +registered listener. Using cpumasks allows the data received by one listener +to be limited and assists in flow control over the netlink interface and is +explained in more detail below. + +If the exiting task is the last thread exiting its thread group, +an additional record containing the per-tgid stats is also sent to userspace. +The latter contains the sum of per-pid stats for all threads in the thread +group, both past and present. + +getdelays.c is a simple utility demonstrating usage of the taskstats interface +for reporting delay accounting statistics. Users can register cpumasks, +send commands and process responses, listen for per-tid/tgid exit data, +write the data received to a file and do basic flow control by increasing +receive buffer sizes. + +Interface +--------- + +The user-kernel interface is encapsulated in include/linux/taskstats.h + +To avoid this documentation becoming obsolete as the interface evolves, only +an outline of the current version is given. taskstats.h always overrides the +description here. + +struct taskstats is the common accounting structure for both per-pid and +per-tgid data. It is versioned and can be extended by each accounting subsystem +that is added to the kernel. The fields and their semantics are defined in the +taskstats.h file. + +The data exchanged between user and kernel space is a netlink message belonging +to the NETLINK_GENERIC family and using the netlink attributes interface. +The messages are in the format + + +----------+- - -+-------------+-------------------+ + | nlmsghdr | Pad | genlmsghdr | taskstats payload | + +----------+- - -+-------------+-------------------+ + + +The taskstats payload is one of the following three kinds: + +1. Commands: Sent from user to kernel. Commands to get data on +a pid/tgid consist of one attribute, of type TASKSTATS_CMD_ATTR_PID/TGID, +containing a u32 pid or tgid in the attribute payload. The pid/tgid denotes +the task/process for which userspace wants statistics. + +Commands to register/deregister interest in exit data from a set of cpus +consist of one attribute, of type +TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK and contain a cpumask in the +attribute payload. The cpumask is specified as an ascii string of +comma-separated cpu ranges e.g. to listen to exit data from cpus 1,2,3,5,7,8 +the cpumask would be "1-3,5,7-8". If userspace forgets to deregister interest +in cpus before closing the listening socket, the kernel cleans up its interest +set over time. However, for the sake of efficiency, an explicit deregistration +is advisable. + +2. Response for a command: sent from the kernel in response to a userspace +command. The payload is a series of three attributes of type: + +a) TASKSTATS_TYPE_AGGR_PID/TGID : attribute containing no payload but indicates +a pid/tgid will be followed by some stats. + +b) TASKSTATS_TYPE_PID/TGID: attribute whose payload is the pid/tgid whose stats +is being returned. + +c) TASKSTATS_TYPE_STATS: attribute with a struct taskstsats as payload. The +same structure is used for both per-pid and per-tgid stats. + +3. New message sent by kernel whenever a task exits. The payload consists of a + series of attributes of the following type: + +a) TASKSTATS_TYPE_AGGR_PID: indicates next two attributes will be pid+stats +b) TASKSTATS_TYPE_PID: contains exiting task's pid +c) TASKSTATS_TYPE_STATS: contains the exiting task's per-pid stats +d) TASKSTATS_TYPE_AGGR_TGID: indicates next two attributes will be tgid+stats +e) TASKSTATS_TYPE_TGID: contains tgid of process to which task belongs +f) TASKSTATS_TYPE_STATS: contains the per-tgid stats for exiting task's process + + +per-tgid stats +-------------- + +Taskstats provides per-process stats, in addition to per-task stats, since +resource management is often done at a process granularity and aggregating task +stats in userspace alone is inefficient and potentially inaccurate (due to lack +of atomicity). + +However, maintaining per-process, in addition to per-task stats, within the +kernel has space and time overheads. To address this, the taskstats code +accumalates each exiting task's statistics into a process-wide data structure. +When the last task of a process exits, the process level data accumalated also +gets sent to userspace (along with the per-task data). + +When a user queries to get per-tgid data, the sum of all other live threads in +the group is added up and added to the accumalated total for previously exited +threads of the same thread group. + +Extending taskstats +------------------- + +There are two ways to extend the taskstats interface to export more +per-task/process stats as patches to collect them get added to the kernel +in future: + +1. Adding more fields to the end of the existing struct taskstats. Backward + compatibility is ensured by the version number within the + structure. Userspace will use only the fields of the struct that correspond + to the version its using. + +2. Defining separate statistic structs and using the netlink attributes + interface to return them. Since userspace processes each netlink attribute + independently, it can always ignore attributes whose type it does not + understand (because it is using an older version of the interface). + + +Choosing between 1. and 2. is a matter of trading off flexibility and +overhead. If only a few fields need to be added, then 1. is the preferable +path since the kernel and userspace don't need to incur the overhead of +processing new netlink attributes. But if the new fields expand the existing +struct too much, requiring disparate userspace accounting utilities to +unnecessarily receive large structures whose fields are of no interest, then +extending the attributes structure would be worthwhile. + +Flow control for taskstats +-------------------------- + +When the rate of task exits becomes large, a listener may not be able to keep +up with the kernel's rate of sending per-tid/tgid exit data leading to data +loss. This possibility gets compounded when the taskstats structure gets +extended and the number of cpus grows large. + +To avoid losing statistics, userspace should do one or more of the following: + +- increase the receive buffer sizes for the netlink sockets opened by +listeners to receive exit data. + +- create more listeners and reduce the number of cpus being listened to by +each listener. In the extreme case, there could be one listener for each cpu. +Users may also consider setting the cpu affinity of the listener to the subset +of cpus to which it listens, especially if they are listening to just one cpu. + +Despite these measures, if the userspace receives ENOBUFS error messages +indicated overflow of receive buffers, it should take measures to handle the +loss of data. + +---- diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 149f62ba14a..e11f7728ec6 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -448,6 +448,8 @@ running once the system is up. Format: <area>[,<node>] See also Documentation/networking/decnet.txt. + delayacct [KNL] Enable per-task delay accounting + dhash_entries= [KNL] Set number of hash buckets for dentry cache. diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index 28d1bc3edb1..46b9b389df3 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -1015,10 +1015,9 @@ CPU from reordering them. There are some more advanced barrier functions: (*) set_mb(var, value) - (*) set_wmb(var, value) - These assign the value to the variable and then insert at least a write - barrier after it, depending on the function. They aren't guaranteed to + This assigns the value to the variable and then inserts at least a write + barrier after it, depending on the function. It isn't guaranteed to insert anything more than a compiler barrier in a UP compilation. diff --git a/Documentation/ramdisk.txt b/Documentation/ramdisk.txt index 7c25584e082..52f75b7d51c 100644 --- a/Documentation/ramdisk.txt +++ b/Documentation/ramdisk.txt @@ -6,7 +6,7 @@ Contents: 1) Overview 2) Kernel Command Line Parameters 3) Using "rdev -r" - 4) An Example of Creating a Compressed RAM Disk + 4) An Example of Creating a Compressed RAM Disk 1) Overview @@ -34,7 +34,7 @@ make it clearer. The original "ramdisk=<ram_size>" has been kept around for compatibility reasons, but it may be removed in the future. The new RAM disk also has the ability to load compressed RAM disk images, -allowing one to squeeze more programs onto an average installation or +allowing one to squeeze more programs onto an average installation or rescue floppy disk. @@ -51,7 +51,7 @@ default is 4096 (4 MB) (8192 (8 MB) on S390). =================== This parameter tells the RAM disk driver how many bytes to use per block. The -default is 512. +default is 1024 (BLOCK_SIZE). 3) Using "rdev -r" @@ -70,7 +70,7 @@ These numbers are no magical secrets, as seen below: ./arch/i386/kernel/setup.c:#define RAMDISK_PROMPT_FLAG 0x8000 ./arch/i386/kernel/setup.c:#define RAMDISK_LOAD_FLAG 0x4000 -Consider a typical two floppy disk setup, where you will have the +Consider a typical two floppy disk setup, where you will have the kernel on disk one, and have already put a RAM disk image onto disk #2. Hence you want to set bits 0 to 13 as 0, meaning that your RAM disk @@ -97,12 +97,12 @@ Since the default start = 0 and the default prompt = 1, you could use: append = "load_ramdisk=1" -4) An Example of Creating a Compressed RAM Disk +4) An Example of Creating a Compressed RAM Disk ---------------------------------------------- To create a RAM disk image, you will need a spare block device to construct it on. This can be the RAM disk device itself, or an -unused disk partition (such as an unmounted swap partition). For this +unused disk partition (such as an unmounted swap partition). For this example, we will use the RAM disk device, "/dev/ram0". Note: This technique should not be done on a machine with less than 8 MB diff --git a/MAINTAINERS b/MAINTAINERS index a3462c3414c..e99028ca2f7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -771,6 +771,7 @@ M: aliakc@web.de P: Jamie Lenehan M: lenehan@twibble.org W: http://twibble.org/dist/dc395x/ +L: dc395x@twibble.org L: http://lists.twibble.org/mailman/listinfo/dc395x/ S: Maintained @@ -1501,6 +1502,7 @@ P: Yi Zhu M: yi.zhu@intel.com P: James Ketrenos M: jketreno@linux.intel.com +L: ipw2100-devel@lists.sourceforge.net L: http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel W: http://ipw2100.sourceforge.net S: Supported @@ -1510,6 +1512,7 @@ P: Yi Zhu M: yi.zhu@intel.com P: James Ketrenos M: jketreno@linux.intel.com +L: ipw2100-devel@lists.sourceforge.net L: http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel W: http://ipw2200.sourceforge.net S: Supported @@ -2226,6 +2229,7 @@ S: Maintained PCMCIA SUBSYSTEM P: Linux PCMCIA Team +L: linux-pcmcia@lists.infradead.org L: http://lists.infradead.org/mailman/listinfo/linux-pcmcia T: git kernel.org:/pub/scm/linux/kernel/git/brodo/pcmcia-2.6.git S: Maintained @@ -2236,6 +2240,12 @@ M: tsbogend@alpha.franken.de L: netdev@vger.kernel.org S: Maintained +PER-TASK DELAY ACCOUNTING +P: Shailabh Nagar +M: nagar@watson.ibm.com +L: linux-kernel@vger.kernel.org +S: Maintained + PERSONALITY HANDLING P: Christoph Hellwig M: hch@infradead.org @@ -2763,6 +2773,12 @@ P: Deepak Saxena M: dsaxena@plexity.net S: Maintained +TASKSTATS STATISTICS INTERFACE +P: Shailabh Nagar +M: nagar@watson.ibm.com +L: linux-kernel@vger.kernel.org +S: Maintained + TI PARALLEL LINK CABLE DRIVER P: Romain Lievin M: roms@lpg.ticalc.org diff --git a/arch/i386/kernel/crash.c b/arch/i386/kernel/crash.c index 48f0f62f781..5b96f038367 100644 --- a/arch/i386/kernel/crash.c +++ b/arch/i386/kernel/crash.c @@ -90,7 +90,7 @@ static void crash_save_self(struct pt_regs *regs) crash_save_this_cpu(regs, cpu); } -#ifdef CONFIG_SMP +#if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC) static atomic_t waiting_for_crash_ipi; static int crash_nmi_callback(struct pt_regs *regs, int cpu) diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 7864395c144..f1682206d30 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -1327,7 +1327,10 @@ legacy_init_iomem_resources(struct resource *code_resource, struct resource *dat res->start = e820.map[i].addr; res->end = res->start + e820.map[i].size - 1; res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; - request_resource(&iomem_resource, res); + if (request_resource(&iomem_resource, res)) { + kfree(res); + continue; + } if (e820.map[i].type == E820_RAM) { /* * We don't know which RAM region contains kernel data, diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 5cfd4f42eeb..313ac1f7dc5 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -324,35 +324,35 @@ void show_registers(struct pt_regs *regs) static void handle_BUG(struct pt_regs *regs) { + unsigned long eip = regs->eip; unsigned short ud2; - unsigned short line; - char *file; - char c; - unsigned long eip; - - eip = regs->eip; if (eip < PAGE_OFFSET) - goto no_bug; + return; if (__get_user(ud2, (unsigned short __user *)eip)) - goto no_bug; + return; if (ud2 != 0x0b0f) - goto no_bug; - if (__get_user(line, (unsigned short __user *)(eip + 2))) - goto bug; - if (__get_user(file, (char * __user *)(eip + 4)) || - (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) - file = "<bad filename>"; + return; printk(KERN_EMERG "------------[ cut here ]------------\n"); - printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); -no_bug: - return; +#ifdef CONFIG_DEBUG_BUGVERBOSE + do { + unsigned short line; + char *file; + char c; - /* Here we know it was a BUG but file-n-line is unavailable */ -bug: - printk(KERN_EMERG "Kernel BUG\n"); + if (__get_user(line, (unsigned short __user *)(eip + 2))) + break; + if (__get_user(file, (char * __user *)(eip + 4)) || + (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) + file = "<bad filename>"; + + printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); + return; + } while (0); +#endif + printk(KERN_EMERG "Kernel BUG at [verbose debug info unavailable]\n"); } /* This is gone through when something in the kernel diff --git a/arch/i386/lib/usercopy.c b/arch/i386/lib/usercopy.c index 4b75212ab6d..efc7e7d5f4d 100644 --- a/arch/i386/lib/usercopy.c +++ b/arch/i386/lib/usercopy.c @@ -843,7 +843,6 @@ unsigned long __copy_from_user_ll_nocache_nozero(void *to, const void __user *fr unsigned long copy_to_user(void __user *to, const void *from, unsigned long n) { - might_sleep(); BUG_ON((long) n < 0); if (access_ok(VERIFY_WRITE, to, n)) n = __copy_to_user(to, from, n); @@ -870,7 +869,6 @@ EXPORT_SYMBOL(copy_to_user); unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) { - might_sleep(); BUG_ON((long) n < 0); if (access_ok(VERIFY_READ, from, n)) n = __copy_from_user(to, from, n); diff --git a/arch/um/Makefile-x86_64 b/arch/um/Makefile-x86_64 index dffd1184c95..9558a7cf34d 100644 --- a/arch/um/Makefile-x86_64 +++ b/arch/um/Makefile-x86_64 @@ -11,6 +11,7 @@ USER_CFLAGS += -fno-builtin -m64 CHECKFLAGS += -m64 AFLAGS += -m64 LDFLAGS += -m elf_x86_64 +CPPFLAGS += -m64 ELF_ARCH := i386:x86-64 ELF_FORMAT := elf64-x86-64 diff --git a/arch/um/include/longjmp.h b/arch/um/include/longjmp.h index 8e7053013f7..1b5c0131a12 100644 --- a/arch/um/include/longjmp.h +++ b/arch/um/include/longjmp.h @@ -8,8 +8,8 @@ longjmp(*buf, val); \ } while(0) -#define UML_SETJMP(buf, enable) ({ \ - int n; \ +#define UML_SETJMP(buf) ({ \ + int n, enable; \ enable = get_signals(); \ n = setjmp(*buf); \ if(n != 0) \ diff --git a/arch/um/include/os.h b/arch/um/include/os.h index b6c52496e15..5316e8a4a4f 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -15,9 +15,9 @@ #include "irq_user.h" #include "sysdep/tls.h" -#define OS_TYPE_FILE 1 -#define OS_TYPE_DIR 2 -#define OS_TYPE_SYMLINK 3 +#define OS_TYPE_FILE 1 +#define OS_TYPE_DIR 2 +#define OS_TYPE_SYMLINK 3 #define OS_TYPE_CHARDEV 4 #define OS_TYPE_BLOCKDEV 5 #define OS_TYPE_FIFO 6 @@ -61,68 +61,68 @@ struct openflags { }; #define OPENFLAGS() ((struct openflags) { .r = 0, .w = 0, .s = 0, .c = 0, \ - .t = 0, .a = 0, .e = 0, .cl = 0 }) + .t = 0, .a = 0, .e = 0, .cl = 0 }) static inline struct openflags of_read(struct openflags flags) { - flags.r = 1; - return(flags); + flags.r = 1; + return flags; } static inline struct openflags of_write(struct openflags flags) { - flags.w = 1; - return(flags); + flags.w = 1; + return flags; } static inline struct openflags of_rdwr(struct openflags flags) { - return(of_read(of_write(flags))); + return of_read(of_write(flags)); } static inline struct openflags of_set_rw(struct openflags flags, int r, int w) { flags.r = r; flags.w = w; - return(flags); + return flags; } static inline struct openflags of_sync(struct openflags flags) -{ - flags.s = 1; - return(flags); +{ + flags.s = 1; + return flags; } static inline struct openflags of_create(struct openflags flags) -{ - flags.c = 1; - return(flags); +{ + flags.c = 1; + return flags; } - + static inline struct openflags of_trunc(struct openflags flags) -{ - flags.t = 1; - return(flags); +{ + flags.t = 1; + return flags; } - + static inline struct openflags of_append(struct openflags flags) -{ - flags.a = 1; - return(flags); +{ + flags.a = 1; + return flags; } - + static inline struct openflags of_excl(struct openflags flags) -{ - flags.e = 1; - return(flags); +{ + flags.e = 1; + return flags; } static inline struct openflags of_cloexec(struct openflags flags) -{ - flags.cl = 1; - return(flags); +{ + flags.cl = 1; + return flags; } - + /* file.c */ extern int os_stat_file(const char *file_name, struct uml_stat *buf); extern int os_stat_fd(const int fd, struct uml_stat *buf); @@ -204,7 +204,7 @@ extern int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr); extern int os_map_memory(void *virt, int fd, unsigned long long off, unsigned long len, int r, int w, int x); -extern int os_protect_memory(void *addr, unsigned long len, +extern int os_protect_memory(void *addr, unsigned long len, int r, int w, int x); extern int os_unmap_memory(void *addr, int len); extern int os_drop_memory(void *addr, int length); diff --git a/arch/um/kernel/vmlinux.lds.S b/arch/um/kernel/vmlinux.lds.S index 72acdce205e..f8aeb448aab 100644 --- a/arch/um/kernel/vmlinux.lds.S +++ b/arch/um/kernel/vmlinux.lds.S @@ -1,5 +1,3 @@ -/* in case the preprocessor is a 32bit one */ -#undef i386 #ifdef CONFIG_LD_SCRIPT_STATIC #include "uml.lds.S" #else diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index b1cda818f5b..b98d3ca2cd1 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -273,12 +273,12 @@ void init_new_thread_signals(void) int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr) { jmp_buf buf; - int n, enable; + int n; *jmp_ptr = &buf; - n = UML_SETJMP(&buf, enable); + n = UML_SETJMP(&buf); if(n != 0) - return(n); + return n; (*fn)(arg); - return(0); + return 0; } diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index bf35572d9cf..7baf90fda58 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -435,7 +435,6 @@ void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, { unsigned long flags; jmp_buf switch_buf, fork_buf; - int enable; *switch_buf_ptr = &switch_buf; *fork_buf_ptr = &fork_buf; @@ -450,7 +449,7 @@ void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, */ flags = get_signals(); block_signals(); - if(UML_SETJMP(&fork_buf, enable) == 0) + if(UML_SETJMP(&fork_buf) == 0) new_thread_proc(stack, handler); remove_sigstack(); @@ -467,21 +466,19 @@ void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, void thread_wait(void *sw, void *fb) { jmp_buf buf, **switch_buf = sw, *fork_buf; - int enable; *switch_buf = &buf; fork_buf = fb; - if(UML_SETJMP(&buf, enable) == 0) + if(UML_SETJMP(&buf) == 0) siglongjmp(*fork_buf, INIT_JMP_REMOVE_SIGSTACK); } void switch_threads(void *me, void *next) { jmp_buf my_buf, **me_ptr = me, *next_buf = next; - int enable; *me_ptr = &my_buf; - if(UML_SETJMP(&my_buf, enable) == 0) + if(UML_SETJMP(&my_buf) == 0) UML_LONGJMP(next_buf, 1); } @@ -495,14 +492,14 @@ static jmp_buf *cb_back; int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr) { jmp_buf **switch_buf = switch_buf_ptr; - int n, enable; + int n; set_handler(SIGWINCH, (__sighandler_t) sig_handler, SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGALRM, SIGVTALRM, -1); *fork_buf_ptr = &initial_jmpbuf; - n = UML_SETJMP(&initial_jmpbuf, enable); + n = UML_SETJMP(&initial_jmpbuf); switch(n){ case INIT_JMP_NEW_THREAD: new_thread_proc((void *) stack, new_thread_handler); @@ -529,14 +526,13 @@ int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr) void initial_thread_cb_skas(void (*proc)(void *), void *arg) { jmp_buf here; - int enable; cb_proc = proc; cb_arg = arg; cb_back = &here; block_signals(); - if(UML_SETJMP(&here, enable) == 0) + if(UML_SETJMP(&here) == 0) UML_LONGJMP(&initial_jmpbuf, INIT_JMP_CALLBACK); unblock_signals(); diff --git a/arch/um/os-Linux/uaccess.c b/arch/um/os-Linux/uaccess.c index e523719330b..865f6a6a259 100644 --- a/arch/um/os-Linux/uaccess.c +++ b/arch/um/os-Linux/uaccess.c @@ -14,11 +14,10 @@ unsigned long __do_user_copy(void *to, const void *from, int n, int n), int *faulted_out) { unsigned long *faddrp = (unsigned long *) fault_addr, ret; - int enable; jmp_buf jbuf; *fault_catcher = &jbuf; - if(UML_SETJMP(&jbuf, enable) == 0){ + if(UML_SETJMP(&jbuf) == 0){ (*op)(to, from, n); ret = 0; *faulted_out = 0; diff --git a/block/ioctl.c b/block/ioctl.c index 9cfa2e1ecb2..309760b7e37 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -72,7 +72,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user bdevp = bdget_disk(disk, part); if (!bdevp) return -ENOMEM; - mutex_lock(&bdevp->bd_mutex); + mutex_lock_nested(&bdevp->bd_mutex, BD_MUTEX_PARTITION); if (bdevp->bd_openers) { mutex_unlock(&bdevp->bd_mutex); bdput(bdevp); @@ -82,7 +82,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user fsync_bdev(bdevp); invalidate_bdev(bdevp, 0); - mutex_lock(&bdev->bd_mutex); + mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_WHOLE); delete_partition(disk, part); mutex_unlock(&bdev->bd_mutex); mutex_unlock(&bdevp->bd_mutex); diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 93d94749310..b5382cedf0c 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -400,6 +400,16 @@ config BLK_DEV_RAM_SIZE what are you doing. If you are using IBM S/390, then set this to 8192. +config BLK_DEV_RAM_BLOCKSIZE + int "Default RAM disk block size (bytes)" + depends on BLK_DEV_RAM + default "1024" + help + The default value is 1024 kilobytes. PAGE_SIZE is a much more + efficient choice however. The default is kept to ensure initrd + setups function - apparently needed by the rd_load_image routine + that supposes the filesystem in the image uses a 1024 blocksize. + config BLK_DEV_INITRD bool "Initial RAM filesystem and RAM disk (initramfs/initrd) support" depends on BROKEN || !FRV diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 757f42dd8e8..78082edc14b 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -1739,8 +1739,6 @@ static void getgeometry(int ctlr) (log_index < id_ctlr_buf->nr_drvs) && (log_unit < NWD); log_unit++) { - struct gendisk *disk = ida_gendisk[ctlr][log_unit]; - size = sizeof(sense_log_drv_stat_t); /* diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 3cf246abb5e..a3f64bfe6b5 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -84,7 +84,7 @@ int rd_size = CONFIG_BLK_DEV_RAM_SIZE; /* Size of the RAM disks */ * behaviour. The default is still BLOCK_SIZE (needed by rd_load_image that * supposes the filesystem in the image uses a BLOCK_SIZE blocksize). */ -static int rd_blocksize = BLOCK_SIZE; /* blocksize of the RAM disks */ +static int rd_blocksize = CONFIG_BLK_DEV_RAM_BLOCKSIZE; /* * Copyright (C) 2000 Linus Torvalds. diff --git a/drivers/char/nsc_gpio.c b/drivers/char/nsc_gpio.c index 5b91e4e2564..7719bd75810 100644 --- a/drivers/char/nsc_gpio.c +++ b/drivers/char/nsc_gpio.c @@ -68,13 +68,11 @@ ssize_t nsc_gpio_write(struct file *file, const char __user *data, amp->gpio_config(m, ~1, 0); break; case 'T': - dev_dbg(dev, "GPIO%d output is push pull\n", - m); + dev_dbg(dev, "GPIO%d output is push pull\n", m); amp->gpio_config(m, ~2, 2); break; case 't': - dev_dbg(dev, "GPIO%d output is open drain\n", - m); + dev_dbg(dev, "GPIO%d output is open drain\n", m); amp->gpio_config(m, ~2, 0); break; case 'P': diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c index 11bd78c8062..645eb81cb5a 100644 --- a/drivers/char/pc8736x_gpio.c +++ b/drivers/char/pc8736x_gpio.c @@ -212,22 +212,21 @@ static void pc8736x_gpio_change(unsigned index) pc8736x_gpio_set(index, !pc8736x_gpio_current(index)); } -static struct nsc_gpio_ops pc8736x_access = { +static struct nsc_gpio_ops pc8736x_gpio_ops = { .owner = THIS_MODULE, .gpio_config = pc8736x_gpio_configure, .gpio_dump = nsc_gpio_dump, .gpio_get = pc8736x_gpio_get, .gpio_set = pc8736x_gpio_set, - .gpio_set_high = pc8736x_gpio_set_high, - .gpio_set_low = pc8736x_gpio_set_low, .gpio_change = pc8736x_gpio_change, .gpio_current = pc8736x_gpio_current }; +EXPORT_SYMBOL(pc8736x_gpio_ops); static int pc8736x_gpio_open(struct inode *inode, struct file *file) { unsigned m = iminor(inode); - file->private_data = &pc8736x_access; + file->private_data = &pc8736x_gpio_ops; dev_dbg(&pdev->dev, "open %d\n", m); @@ -236,7 +235,7 @@ static int pc8736x_gpio_open(struct inode *inode, struct file *file) return nonseekable_open(inode, file); } -static const struct file_operations pc8736x_gpio_fops = { +static const struct file_operations pc8736x_gpio_fileops = { .owner = THIS_MODULE, .open = pc8736x_gpio_open, .write = nsc_gpio_write, @@ -278,7 +277,7 @@ static int __init pc8736x_gpio_init(void) dev_err(&pdev->dev, "no device found\n"); goto undo_platform_dev_add; } - pc8736x_access.dev = &pdev->dev; + pc8736x_gpio_ops.dev = &pdev->dev; /* Verify that chip and it's GPIO unit are both enabled. My BIOS does this, so I take minimum action here @@ -328,7 +327,7 @@ static int __init pc8736x_gpio_init(void) pc8736x_init_shadow(); /* ignore minor errs, and succeed */ - cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fops); + cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops); cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT); return 0; @@ -355,7 +354,5 @@ static void __exit pc8736x_gpio_cleanup(void) platform_device_put(pdev); } -EXPORT_SYMBOL(pc8736x_access); - module_init(pc8736x_gpio_init); module_exit(pc8736x_gpio_cleanup); diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c index 425c58719db..b956c7babd1 100644 --- a/drivers/char/scx200_gpio.c +++ b/drivers/char/scx200_gpio.c @@ -5,7 +5,6 @@ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */ -#include <linux/config.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/module.h> @@ -22,37 +21,37 @@ #include <linux/scx200_gpio.h> #include <linux/nsc_gpio.h> -#define NAME "scx200_gpio" -#define DEVNAME NAME +#define DRVNAME "scx200_gpio" static struct platform_device *pdev; MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>"); -MODULE_DESCRIPTION("NatSemi SCx200 GPIO Pin Driver"); +MODULE_DESCRIPTION("NatSemi/AMD SCx200 GPIO Pin Driver"); MODULE_LICENSE("GPL"); static int major = 0; /* default to dynamic major */ module_param(major, int, 0); MODULE_PARM_DESC(major, "Major device number"); -struct nsc_gpio_ops scx200_access = { +#define MAX_PINS 32 /* 64 later, when known ok */ + +struct nsc_gpio_ops scx200_gpio_ops = { .owner = THIS_MODULE, .gpio_config = scx200_gpio_configure, .gpio_dump = nsc_gpio_dump, .gpio_get = scx200_gpio_get, .gpio_set = scx200_gpio_set, - .gpio_set_high = scx200_gpio_set_high, - .gpio_set_low = scx200_gpio_set_low, .gpio_change = scx200_gpio_change, .gpio_current = scx200_gpio_current }; +EXPORT_SYMBOL(scx200_gpio_ops); static int scx200_gpio_open(struct inode *inode, struct file *file) { unsigned m = iminor(inode); - file->private_data = &scx200_access; + file->private_data = &scx200_gpio_ops; - if (m > 63) + if (m >= MAX_PINS) return -EINVAL; return nonseekable_open(inode, file); } @@ -62,8 +61,7 @@ static int scx200_gpio_release(struct inode *inode, struct file *file) return 0; } - -static const struct file_operations scx200_gpio_fops = { +static const struct file_operations scx200_gpio_fileops = { .owner = THIS_MODULE, .write = nsc_gpio_write, .read = nsc_gpio_read, @@ -71,21 +69,20 @@ static const struct file_operations scx200_gpio_fops = { .release = scx200_gpio_release, }; -struct cdev *scx200_devices; -static int num_pins = 32; +struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */ static int __init scx200_gpio_init(void) { - int rc, i; - dev_t dev = MKDEV(major, 0); + int rc; + dev_t devid; if (!scx200_gpio_present()) { - printk(KERN_ERR NAME ": no SCx200 gpio present\n"); + printk(KERN_ERR DRVNAME ": no SCx200 gpio present\n"); return -ENODEV; } /* support dev_dbg() with pdev->dev */ - pdev = platform_device_alloc(DEVNAME, 0); + pdev = platform_device_alloc(DRVNAME, 0); if (!pdev) return -ENOMEM; @@ -94,37 +91,25 @@ static int __init scx200_gpio_init(void) goto undo_malloc; /* nsc_gpio uses dev_dbg(), so needs this */ - scx200_access.dev = &pdev->dev; - - if (major) - rc = register_chrdev_region(dev, num_pins, "scx200_gpio"); - else { - rc = alloc_chrdev_region(&dev, 0, num_pins, "scx200_gpio"); - major = MAJOR(dev); + scx200_gpio_ops.dev = &pdev->dev; + + if (major) { + devid = MKDEV(major, 0); + rc = register_chrdev_region(devid, MAX_PINS, "scx200_gpio"); + } else { + rc = alloc_chrdev_region(&devid, 0, MAX_PINS, "scx200_gpio"); + major = MAJOR(devid); } if (rc < 0) { dev_err(&pdev->dev, "SCx200 chrdev_region err: %d\n", rc); goto undo_platform_device_add; } - scx200_devices = kzalloc(num_pins * sizeof(struct cdev), GFP_KERNEL); - if (!scx200_devices) { - rc = -ENOMEM; - goto undo_chrdev_region; - } - for (i = 0; i < num_pins; i++) { - struct cdev *cdev = &scx200_devices[i]; - cdev_init(cdev, &scx200_gpio_fops); - cdev->owner = THIS_MODULE; - rc = cdev_add(cdev, MKDEV(major, i), 1); - /* tolerate 'minor' errors */ - if (rc) - dev_err(&pdev->dev, "Error %d on minor %d", rc, i); - } + + cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops); + cdev_add(&scx200_gpio_cdev, devid, MAX_PINS); return 0; /* succeed */ -undo_chrdev_region: - unregister_chrdev_region(dev, num_pins); undo_platform_device_add: platform_device_del(pdev); undo_malloc: @@ -135,10 +120,11 @@ undo_malloc: static void __exit scx200_gpio_cleanup(void) { - kfree(scx200_devices); - unregister_chrdev_region(MKDEV(major, 0), num_pins); + cdev_del(&scx200_gpio_cdev); + /* cdev_put(&scx200_gpio_cdev); */ + + unregister_chrdev_region(MKDEV(major, 0), MAX_PINS); platform_device_unregister(pdev); - /* kfree(pdev); */ } module_init(scx200_gpio_init); diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 6889e7db3af..a082a2e3425 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1141,6 +1141,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend put_device(dev); clear_bit(chip->dev_num, dev_mask); kfree(chip); + kfree(devname); return NULL; } diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 3232b193259..ee7ac6f43c6 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -424,6 +424,7 @@ static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs *regs) iowrite32(interrupt, chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality)); + ioread32(chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality)); return IRQ_HANDLED; } @@ -431,23 +432,19 @@ static int interrupts = 1; module_param(interrupts, bool, 0444); MODULE_PARM_DESC(interrupts, "Enable interrupts"); -static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, - const struct pnp_device_id *pnp_id) +static int tpm_tis_init(struct device *dev, resource_size_t start, + resource_size_t len) { u32 vendor, intfcaps, intmask; int rc, i; - unsigned long start, len; struct tpm_chip *chip; - start = pnp_mem_start(pnp_dev, 0); - len = pnp_mem_len(pnp_dev, 0); - if (!start) start = TIS_MEM_BASE; if (!len) len = TIS_MEM_LEN; - if (!(chip = tpm_register_hardware(&pnp_dev->dev, &tpm_tis))) + if (!(chip = tpm_register_hardware(dev, &tpm_tis))) return -ENODEV; chip->vendor.iobase = ioremap(start, len); @@ -464,7 +461,7 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - dev_info(&pnp_dev->dev, + dev_info(dev, "1.2 TPM (device-id 0x%X, rev-id %d)\n", vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); @@ -472,26 +469,26 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, intfcaps = ioread32(chip->vendor.iobase + TPM_INTF_CAPS(chip->vendor.locality)); - dev_dbg(&pnp_dev->dev, "TPM interface capabilities (0x%x):\n", + dev_dbg(dev, "TPM interface capabilities (0x%x):\n", intfcaps); if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) - dev_dbg(&pnp_dev->dev, "\tBurst Count Static\n"); + dev_dbg(dev, "\tBurst Count Static\n"); if (intfcaps & TPM_INTF_CMD_READY_INT) - dev_dbg(&pnp_dev->dev, "\tCommand Ready Int Support\n"); + dev_dbg(dev, "\tCommand Ready Int Support\n"); if (intfcaps & TPM_INTF_INT_EDGE_FALLING) - dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Falling\n"); + dev_dbg(dev, "\tInterrupt Edge Falling\n"); if (intfcaps & TPM_INTF_INT_EDGE_RISING) - dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Rising\n"); + dev_dbg(dev, "\tInterrupt Edge Rising\n"); if (intfcaps & TPM_INTF_INT_LEVEL_LOW) - dev_dbg(&pnp_dev->dev, "\tInterrupt Level Low\n"); + dev_dbg(dev, "\tInterrupt Level Low\n"); if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) - dev_dbg(&pnp_dev->dev, "\tInterrupt Level High\n"); + dev_dbg(dev, "\tInterrupt Level High\n"); if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) - dev_dbg(&pnp_dev->dev, "\tLocality Change Int Support\n"); + dev_dbg(dev, "\tLocality Change Int Support\n"); if (intfcaps & TPM_INTF_STS_VALID_INT) - dev_dbg(&pnp_dev->dev, "\tSts Valid Int Support\n"); + dev_dbg(dev, "\tSts Valid Int Support\n"); if (intfcaps & TPM_INTF_DATA_AVAIL_INT) - dev_dbg(&pnp_dev->dev, "\tData Avail Int Support\n"); + dev_dbg(dev, "\tData Avail Int Support\n"); if (request_locality(chip, 0) != 0) { rc = -ENODEV; @@ -594,6 +591,16 @@ out_err: return rc; } +static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, + const struct pnp_device_id *pnp_id) +{ + resource_size_t start, len; + start = pnp_mem_start(pnp_dev, 0); + len = pnp_mem_len(pnp_dev, 0); + + return tpm_tis_init(&pnp_dev->dev, start, len); +} + static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg) { return tpm_pm_suspend(&dev->dev, msg); @@ -628,8 +635,36 @@ module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444); MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); +static struct device_driver tis_drv = { + .name = "tpm_tis", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + .suspend = tpm_pm_suspend, + .resume = tpm_pm_resume, +}; + +static struct platform_device *pdev; + +static int force; +module_param(force, bool, 0444); +MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry"); static int __init init_tis(void) { + int rc; + + if (force) { + rc = driver_register(&tis_drv); + if (rc < 0) + return rc; + if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) + return PTR_ERR(pdev); + if((rc=tpm_tis_init(&pdev->dev, 0, 0)) != 0) { + platform_device_unregister(pdev); + driver_unregister(&tis_drv); + } + return rc; + } + return pnp_register_driver(&tis_pnp_driver); } @@ -654,7 +689,11 @@ static void __exit cleanup_tis(void) tpm_remove_hardware(chip->dev); } spin_unlock(&tis_lock); - pnp_unregister_driver(&tis_pnp_driver); + if (force) { + platform_device_unregister(pdev); + driver_unregister(&tis_drv); + } else + pnp_unregister_driver(&tis_pnp_driver); } module_init(init_tis); diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 3f6705f3083..f85c97f7500 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -701,7 +701,7 @@ static void cm_reset_to_idle(struct cm_id_private *cm_id_priv) } } -void ib_destroy_cm_id(struct ib_cm_id *cm_id) +static void cm_destroy_id(struct ib_cm_id *cm_id, int err) { struct cm_id_private *cm_id_priv; struct cm_work *work; @@ -735,12 +735,22 @@ retest: sizeof cm_id_priv->av.port->cm_dev->ca_guid, NULL, 0); break; + case IB_CM_REQ_RCVD: + if (err == -ENOMEM) { + /* Do not reject to allow future retries. */ + cm_reset_to_idle(cm_id_priv); + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + } else { + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + ib_send_cm_rej(cm_id, IB_CM_REJ_CONSUMER_DEFINED, + NULL, 0, NULL, 0); + } + break; case IB_CM_MRA_REQ_RCVD: case IB_CM_REP_SENT: case IB_CM_MRA_REP_RCVD: ib_cancel_mad(cm_id_priv->av.port->mad_agent, cm_id_priv->msg); /* Fall through */ - case IB_CM_REQ_RCVD: case IB_CM_MRA_REQ_SENT: case IB_CM_REP_RCVD: case IB_CM_MRA_REP_SENT: @@ -775,6 +785,11 @@ retest: kfree(cm_id_priv->private_data); kfree(cm_id_priv); } + +void ib_destroy_cm_id(struct ib_cm_id *cm_id) +{ + cm_destroy_id(cm_id, 0); +} EXPORT_SYMBOL(ib_destroy_cm_id); int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask, @@ -1163,7 +1178,7 @@ static void cm_process_work(struct cm_id_private *cm_id_priv, } cm_deref_id(cm_id_priv); if (ret) - ib_destroy_cm_id(&cm_id_priv->id); + cm_destroy_id(&cm_id_priv->id, ret); } static void cm_format_mra(struct cm_mra_msg *mra_msg, diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 863f64befc7..d6f99d5720f 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -262,14 +262,14 @@ static void cma_detach_from_dev(struct rdma_id_private *id_priv) static int cma_acquire_ib_dev(struct rdma_id_private *id_priv) { struct cma_device *cma_dev; - union ib_gid *gid; + union ib_gid gid; int ret = -ENODEV; - gid = ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr); + ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr, &gid), mutex_lock(&lock); list_for_each_entry(cma_dev, &dev_list, list) { - ret = ib_find_cached_gid(cma_dev->device, gid, + ret = ib_find_cached_gid(cma_dev->device, &gid, &id_priv->id.port_num, NULL); if (!ret) { cma_attach_to_dev(id_priv, cma_dev); @@ -812,6 +812,7 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) cma_modify_qp_err(&id_priv->id); status = ib_event->param.rej_rcvd.reason; event = RDMA_CM_EVENT_REJECTED; + private_data_len = IB_CM_REJ_PRIVATE_DATA_SIZE; break; default: printk(KERN_ERR "RDMA CMA: unexpected IB CM event: %d", @@ -1134,8 +1135,8 @@ static int cma_query_ib_route(struct rdma_id_private *id_priv, int timeout_ms, struct ib_sa_path_rec path_rec; memset(&path_rec, 0, sizeof path_rec); - path_rec.sgid = *ib_addr_get_sgid(addr); - path_rec.dgid = *ib_addr_get_dgid(addr); + ib_addr_get_sgid(addr, &path_rec.sgid); + ib_addr_get_dgid(addr, &path_rec.dgid); path_rec.pkey = cpu_to_be16(ib_addr_get_pkey(addr)); path_rec.numb_path = 1; @@ -1263,7 +1264,7 @@ static int cma_bind_loopback(struct rdma_id_private *id_priv) { struct cma_device *cma_dev; struct ib_port_attr port_attr; - union ib_gid *gid; + union ib_gid gid; u16 pkey; int ret; u8 p; @@ -1284,8 +1285,7 @@ static int cma_bind_loopback(struct rdma_id_private *id_priv) } port_found: - gid = ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr); - ret = ib_get_cached_gid(cma_dev->device, p, 0, gid); + ret = ib_get_cached_gid(cma_dev->device, p, 0, &gid); if (ret) goto out; @@ -1293,6 +1293,7 @@ port_found: if (ret) goto out; + ib_addr_set_sgid(&id_priv->id.route.addr.dev_addr, &gid); ib_addr_set_pkey(&id_priv->id.route.addr.dev_addr, pkey); id_priv->id.port_num = p; cma_attach_to_dev(id_priv, cma_dev); @@ -1339,6 +1340,7 @@ static int cma_resolve_loopback(struct rdma_id_private *id_priv) { struct cma_work *work; struct sockaddr_in *src_in, *dst_in; + union ib_gid gid; int ret; work = kzalloc(sizeof *work, GFP_KERNEL); @@ -1351,8 +1353,8 @@ static int cma_resolve_loopback(struct rdma_id_private *id_priv) goto err; } - ib_addr_set_dgid(&id_priv->id.route.addr.dev_addr, - ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr)); + ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr, &gid); + ib_addr_set_dgid(&id_priv->id.route.addr.dev_addr, &gid); if (cma_zero_addr(&id_priv->id.route.addr.src_addr)) { src_in = (struct sockaddr_in *)&id_priv->id.route.addr.src_addr; diff --git a/drivers/infiniband/core/fmr_pool.c b/drivers/infiniband/core/fmr_pool.c index 615fe9cc6c5..86a3b2d401d 100644 --- a/drivers/infiniband/core/fmr_pool.c +++ b/drivers/infiniband/core/fmr_pool.c @@ -426,7 +426,7 @@ EXPORT_SYMBOL(ib_flush_fmr_pool); struct ib_pool_fmr *ib_fmr_pool_map_phys(struct ib_fmr_pool *pool_handle, u64 *page_list, int list_len, - u64 *io_virtual_address) + u64 io_virtual_address) { struct ib_fmr_pool *pool = pool_handle; struct ib_pool_fmr *fmr; @@ -440,7 +440,7 @@ struct ib_pool_fmr *ib_fmr_pool_map_phys(struct ib_fmr_pool *pool_handle, fmr = ib_fmr_cache_lookup(pool, page_list, list_len, - *io_virtual_address); + io_virtual_address); if (fmr) { /* found in cache */ ++fmr->ref_count; @@ -464,7 +464,7 @@ struct ib_pool_fmr *ib_fmr_pool_map_phys(struct ib_fmr_pool *pool_handle, spin_unlock_irqrestore(&pool->pool_lock, flags); result = ib_map_phys_fmr(fmr->fmr, page_list, list_len, - *io_virtual_address); + io_virtual_address); if (result) { spin_lock_irqsave(&pool->pool_lock, flags); @@ -481,7 +481,7 @@ struct ib_pool_fmr *ib_fmr_pool_map_phys(struct ib_fmr_pool *pool_handle, fmr->ref_count = 1; if (pool->cache_bucket) { - fmr->io_virtual_address = *io_virtual_address; + fmr->io_virtual_address = io_virtual_address; fmr->page_list_len = list_len; memcpy(fmr->page_list, page_list, list_len * sizeof(*page_list)); diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index e911c99ff84..aeda484ffd8 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -488,13 +488,13 @@ static void init_mad(struct ib_sa_mad *mad, struct ib_mad_agent *agent) spin_unlock_irqrestore(&tid_lock, flags); } -static int send_mad(struct ib_sa_query *query, int timeout_ms) +static int send_mad(struct ib_sa_query *query, int timeout_ms, gfp_t gfp_mask) { unsigned long flags; int ret, id; retry: - if (!idr_pre_get(&query_idr, GFP_ATOMIC)) + if (!idr_pre_get(&query_idr, gfp_mask)) return -ENOMEM; spin_lock_irqsave(&idr_lock, flags); ret = idr_get_new(&query_idr, query, &id); @@ -630,7 +630,7 @@ int ib_sa_path_rec_get(struct ib_device *device, u8 port_num, *sa_query = &query->sa_query; - ret = send_mad(&query->sa_query, timeout_ms); + ret = send_mad(&query->sa_query, timeout_ms, gfp_mask); if (ret < 0) goto err2; @@ -752,7 +752,7 @@ int ib_sa_service_rec_query(struct ib_device *device, u8 port_num, u8 method, *sa_query = &query->sa_query; - ret = send_mad(&query->sa_query, timeout_ms); + ret = send_mad(&query->sa_query, timeout_ms, gfp_mask); if (ret < 0) goto err2; @@ -844,7 +844,7 @@ int ib_sa_mcmember_rec_query(struct ib_device *device, u8 port_num, *sa_query = &query->sa_query; - ret = send_mad(&query->sa_query, timeout_ms); + ret = send_mad(&query->sa_query, timeout_ms, gfp_mask); if (ret < 0) goto err2; diff --git a/drivers/infiniband/hw/mthca/mthca_av.c b/drivers/infiniband/hw/mthca/mthca_av.c index b12aa03be25..e215041b2db 100644 --- a/drivers/infiniband/hw/mthca/mthca_av.c +++ b/drivers/infiniband/hw/mthca/mthca_av.c @@ -303,9 +303,10 @@ int mthca_ah_query(struct ib_ah *ibah, struct ib_ah_attr *attr) memset(attr, 0, sizeof *attr); attr->dlid = be16_to_cpu(ah->av->dlid); attr->sl = be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 28; - attr->static_rate = ah->av->msg_sr & 0x7; - attr->src_path_bits = ah->av->g_slid & 0x7F; attr->port_num = be32_to_cpu(ah->av->port_pd) >> 24; + attr->static_rate = mthca_rate_to_ib(dev, ah->av->msg_sr & 0x7, + attr->port_num); + attr->src_path_bits = ah->av->g_slid & 0x7F; attr->ah_flags = mthca_ah_grh_present(ah) ? IB_AH_GRH : 0; if (attr->ah_flags) { diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index 490fc783bb0..cd8b6721ac9 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -222,9 +222,8 @@ static void *get_send_wqe(struct mthca_qp *qp, int n) (PAGE_SIZE - 1)); } -static void mthca_wq_init(struct mthca_wq *wq) +static void mthca_wq_reset(struct mthca_wq *wq) { - /* mthca_alloc_qp_common() initializes the locks */ wq->next_ind = 0; wq->last_comp = wq->max - 1; wq->head = 0; @@ -845,10 +844,10 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn, qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); - mthca_wq_init(&qp->sq); + mthca_wq_reset(&qp->sq); qp->sq.last = get_send_wqe(qp, qp->sq.max - 1); - mthca_wq_init(&qp->rq); + mthca_wq_reset(&qp->rq); qp->rq.last = get_recv_wqe(qp, qp->rq.max - 1); if (mthca_is_memfree(dev)) { @@ -1112,9 +1111,9 @@ static int mthca_alloc_qp_common(struct mthca_dev *dev, qp->atomic_rd_en = 0; qp->resp_depth = 0; qp->sq_policy = send_policy; - mthca_wq_init(&qp->sq); - mthca_wq_init(&qp->rq); - /* these are initialized separately so lockdep can tell them apart */ + mthca_wq_reset(&qp->sq); + mthca_wq_reset(&qp->rq); + spin_lock_init(&qp->sq.lock); spin_lock_init(&qp->rq.lock); diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index ff117bbf81b..72febf1f8ff 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -594,7 +594,7 @@ int iser_reg_page_vec(struct iser_conn *ib_conn, mem = ib_fmr_pool_map_phys(ib_conn->fmr_pool, page_list, page_vec->length, - &io_addr); + io_addr); if (IS_ERR(mem)) { status = (int)PTR_ERR(mem); diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 4e22afef720..8f472e7113b 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -615,9 +615,10 @@ static int srp_map_fmr(struct srp_device *dev, struct scatterlist *scat, (sg_dma_address(&scat[i]) & dev->fmr_page_mask) + j; req->fmr = ib_fmr_pool_map_phys(dev->fmr_pool, - dma_pages, page_cnt, &io_addr); + dma_pages, page_cnt, io_addr); if (IS_ERR(req->fmr)) { ret = PTR_ERR(req->fmr); + req->fmr = NULL; goto out; } diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c index 35ee52f9b79..713c4a8aa77 100644 --- a/drivers/leds/leds-net48xx.c +++ b/drivers/leds/leds-net48xx.c @@ -18,6 +18,7 @@ #include <asm/io.h> #include <linux/scx200_gpio.h> +#define DRVNAME "net48xx-led" #define NET48XX_ERROR_LED_GPIO 20 static struct platform_device *pdev; @@ -66,13 +67,13 @@ static int net48xx_led_remove(struct platform_device *pdev) } static struct platform_driver net48xx_led_driver = { - .driver.owner = THIS_MODULE, .probe = net48xx_led_probe, .remove = net48xx_led_remove, .suspend = net48xx_led_suspend, .resume = net48xx_led_resume, .driver = { - .name = "net48xx-led", + .name = DRVNAME, + .owner = THIS_MODULE, }, }; @@ -89,7 +90,7 @@ static int __init net48xx_led_init(void) if (ret < 0) goto out; - pdev = platform_device_register_simple("net48xx-led", -1, NULL, 0); + pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); if (IS_ERR(pdev)) { ret = PTR_ERR(pdev); platform_driver_unregister(&net48xx_led_driver); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f5b9f187a93..7ff1d88094b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -121,6 +121,16 @@ config RTC_DRV_DS1553 This driver can also be built as a module. If so, the module will be called rtc-ds1553. +config RTC_DRV_ISL1208 + tristate "Intersil 1208" + depends on RTC_CLASS && I2C + help + If you say yes here you get support for the + Intersil 1208 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-isl1208. + config RTC_DRV_DS1672 tristate "Dallas/Maxim DS1672" depends on RTC_CLASS && I2C diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 54220714ff4..bbcfb09d81d 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o +obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c new file mode 100644 index 00000000000..f324d0a635d --- /dev/null +++ b/drivers/rtc/rtc-isl1208.c @@ -0,0 +1,591 @@ +/* + * Intersil ISL1208 rtc class driver + * + * Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/bcd.h> +#include <linux/rtc.h> + +#define DRV_NAME "isl1208" +#define DRV_VERSION "0.2" + +/* Register map */ +/* rtc section */ +#define ISL1208_REG_SC 0x00 +#define ISL1208_REG_MN 0x01 +#define ISL1208_REG_HR 0x02 +#define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */ +#define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */ +#define ISL1208_REG_DT 0x03 +#define ISL1208_REG_MO 0x04 +#define ISL1208_REG_YR 0x05 +#define ISL1208_REG_DW 0x06 +#define ISL1208_RTC_SECTION_LEN 7 + +/* control/status section */ +#define ISL1208_REG_SR 0x07 +#define ISL1208_REG_SR_ARST (1<<7) /* auto reset */ +#define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */ +#define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */ +#define ISL1208_REG_SR_ALM (1<<2) /* alarm */ +#define ISL1208_REG_SR_BAT (1<<1) /* battery */ +#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ +#define ISL1208_REG_INT 0x08 +#define ISL1208_REG_09 0x09 /* reserved */ +#define ISL1208_REG_ATR 0x0a +#define ISL1208_REG_DTR 0x0b + +/* alarm section */ +#define ISL1208_REG_SCA 0x0c +#define ISL1208_REG_MNA 0x0d +#define ISL1208_REG_HRA 0x0e +#define ISL1208_REG_DTA 0x0f +#define ISL1208_REG_MOA 0x10 +#define ISL1208_REG_DWA 0x11 +#define ISL1208_ALARM_SECTION_LEN 6 + +/* user section */ +#define ISL1208_REG_USR1 0x12 +#define ISL1208_REG_USR2 0x13 +#define ISL1208_USR_SECTION_LEN 2 + +/* i2c configuration */ +#define ISL1208_I2C_ADDR 0xde + +static unsigned short normal_i2c[] = { + ISL1208_I2C_ADDR>>1, I2C_CLIENT_END +}; +I2C_CLIENT_INSMOD; /* defines addr_data */ + +static int isl1208_attach_adapter(struct i2c_adapter *adapter); +static int isl1208_detach_client(struct i2c_client *client); + +static struct i2c_driver isl1208_driver = { + .driver = { + .name = DRV_NAME, + }, + .id = I2C_DRIVERID_ISL1208, + .attach_adapter = &isl1208_attach_adapter, + .detach_client = &isl1208_detach_client, +}; + +/* block read */ +static int +isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], + unsigned len) +{ + u8 reg_addr[1] = { reg }; + struct i2c_msg msgs[2] = { + { client->addr, client->flags, sizeof(reg_addr), reg_addr }, + { client->addr, client->flags | I2C_M_RD, len, buf } + }; + int ret; + + BUG_ON(len == 0); + BUG_ON(reg > ISL1208_REG_USR2); + BUG_ON(reg + len > ISL1208_REG_USR2 + 1); + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret > 0) + ret = 0; + return ret; +} + +/* block write */ +static int +isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[], + unsigned len) +{ + u8 i2c_buf[ISL1208_REG_USR2 + 2]; + struct i2c_msg msgs[1] = { + { client->addr, client->flags, len + 1, i2c_buf } + }; + int ret; + + BUG_ON(len == 0); + BUG_ON(reg > ISL1208_REG_USR2); + BUG_ON(reg + len > ISL1208_REG_USR2 + 1); + + i2c_buf[0] = reg; + memcpy(&i2c_buf[1], &buf[0], len); + + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret > 0) + ret = 0; + return ret; +} + +/* simple check to see wether we have a isl1208 */ +static int isl1208_i2c_validate_client(struct i2c_client *client) +{ + u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; + u8 zero_mask[ISL1208_RTC_SECTION_LEN] = { + 0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8 + }; + int i; + int ret; + + ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); + if (ret < 0) + return ret; + + for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) { + if (regs[i] & zero_mask[i]) /* check if bits are cleared */ + return -ENODEV; + } + + return 0; +} + +static int isl1208_i2c_get_sr(struct i2c_client *client) +{ + return i2c_smbus_read_byte_data(client, ISL1208_REG_SR) == -1 ? -EIO:0; +} + +static int isl1208_i2c_get_atr(struct i2c_client *client) +{ + int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR); + + if (atr < 0) + return -EIO; + + /* The 6bit value in the ATR register controls the load + * capacitance C_load * in steps of 0.25pF + * + * bit (1<<5) of the ATR register is inverted + * + * C_load(ATR=0x20) = 4.50pF + * C_load(ATR=0x00) = 12.50pF + * C_load(ATR=0x1f) = 20.25pF + * + */ + + atr &= 0x3f; /* mask out lsb */ + atr ^= 1<<5; /* invert 6th bit */ + atr += 2*9; /* add offset of 4.5pF; unit[atr] = 0.25pF */ + + return atr; +} + +static int isl1208_i2c_get_dtr(struct i2c_client *client) +{ + int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR); + + if (dtr < 0) + return -EIO; + + /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */ + dtr = ((dtr & 0x3) * 20) * (dtr & (1<<2) ? -1 : 1); + + return dtr; +} + +static int isl1208_i2c_get_usr(struct i2c_client *client) +{ + u8 buf[ISL1208_USR_SECTION_LEN] = { 0, }; + int ret; + + ret = isl1208_i2c_read_regs (client, ISL1208_REG_USR1, buf, + ISL1208_USR_SECTION_LEN); + if (ret < 0) + return ret; + + return (buf[1] << 8) | buf[0]; +} + +static int isl1208_i2c_set_usr(struct i2c_client *client, u16 usr) +{ + u8 buf[ISL1208_USR_SECTION_LEN]; + + buf[0] = usr & 0xff; + buf[1] = (usr >> 8) & 0xff; + + return isl1208_i2c_set_regs (client, ISL1208_REG_USR1, buf, + ISL1208_USR_SECTION_LEN); +} + +static int isl1208_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct i2c_client *const client = to_i2c_client(dev); + int sr, dtr, atr, usr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + + seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)\n", + (sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "", + (sr & ISL1208_REG_SR_BAT) ? " BAT" : "", + (sr & ISL1208_REG_SR_ALM) ? " ALM" : "", + (sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "", + (sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "", + (sr & ISL1208_REG_SR_ARST) ? " ARST" : "", + sr); + + seq_printf(seq, "batt_status\t: %s\n", + (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay"); + + dtr = isl1208_i2c_get_dtr(client); + if (dtr >= 0 -1) + seq_printf(seq, "digital_trim\t: %d ppm\n", dtr); + + atr = isl1208_i2c_get_atr(client); + if (atr >= 0) + seq_printf(seq, "analog_trim\t: %d.%.2d pF\n", + atr>>2, (atr&0x3)*25); + + usr = isl1208_i2c_get_usr(client); + if (usr >= 0) + seq_printf(seq, "user_data\t: 0x%.4x\n", usr); + + return 0; +} + + +static int isl1208_i2c_read_time(struct i2c_client *client, + struct rtc_time *tm) +{ + int sr; + u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return -EIO; + } + + sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); + if (sr < 0) { + dev_err(&client->dev, "%s: reading RTC section failed\n", + __func__); + return sr; + } + + tm->tm_sec = BCD2BIN(regs[ISL1208_REG_SC]); + tm->tm_min = BCD2BIN(regs[ISL1208_REG_MN]); + { /* HR field has a more complex interpretation */ + const u8 _hr = regs[ISL1208_REG_HR]; + if (_hr & ISL1208_REG_HR_MIL) /* 24h format */ + tm->tm_hour = BCD2BIN(_hr & 0x3f); + else { // 12h format + tm->tm_hour = BCD2BIN(_hr & 0x1f); + if (_hr & ISL1208_REG_HR_PM) /* PM flag set */ + tm->tm_hour += 12; + } + } + + tm->tm_mday = BCD2BIN(regs[ISL1208_REG_DT]); + tm->tm_mon = BCD2BIN(regs[ISL1208_REG_MO]) - 1; /* rtc starts at 1 */ + tm->tm_year = BCD2BIN(regs[ISL1208_REG_YR]) + 100; + tm->tm_wday = BCD2BIN(regs[ISL1208_REG_DW]); + + return 0; +} + +static int isl1208_i2c_read_alarm(struct i2c_client *client, + struct rtc_wkalrm *alarm) +{ + struct rtc_time *const tm = &alarm->time; + u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; + int sr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + + sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs, + ISL1208_ALARM_SECTION_LEN); + if (sr < 0) { + dev_err(&client->dev, "%s: reading alarm section failed\n", + __func__); + return sr; + } + + /* MSB of each alarm register is an enable bit */ + tm->tm_sec = BCD2BIN(regs[ISL1208_REG_SCA-ISL1208_REG_SCA] & 0x7f); + tm->tm_min = BCD2BIN(regs[ISL1208_REG_MNA-ISL1208_REG_SCA] & 0x7f); + tm->tm_hour = BCD2BIN(regs[ISL1208_REG_HRA-ISL1208_REG_SCA] & 0x3f); + tm->tm_mday = BCD2BIN(regs[ISL1208_REG_DTA-ISL1208_REG_SCA] & 0x3f); + tm->tm_mon = BCD2BIN(regs[ISL1208_REG_MOA-ISL1208_REG_SCA] & 0x1f)-1; + tm->tm_wday = BCD2BIN(regs[ISL1208_REG_DWA-ISL1208_REG_SCA] & 0x03); + + return 0; +} + +static int isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return isl1208_i2c_read_time(to_i2c_client(dev), tm); +} + +static int isl1208_i2c_set_time(struct i2c_client *client, + struct rtc_time const *tm) +{ + int sr; + u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; + + regs[ISL1208_REG_SC] = BIN2BCD(tm->tm_sec); + regs[ISL1208_REG_MN] = BIN2BCD(tm->tm_min); + regs[ISL1208_REG_HR] = BIN2BCD(tm->tm_hour) | ISL1208_REG_HR_MIL; + + regs[ISL1208_REG_DT] = BIN2BCD(tm->tm_mday); + regs[ISL1208_REG_MO] = BIN2BCD(tm->tm_mon + 1); + regs[ISL1208_REG_YR] = BIN2BCD(tm->tm_year - 100); + + regs[ISL1208_REG_DW] = BIN2BCD(tm->tm_wday & 7); + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + + /* set WRTC */ + sr = i2c_smbus_write_byte_data (client, ISL1208_REG_SR, + sr | ISL1208_REG_SR_WRTC); + if (sr < 0) { + dev_err(&client->dev, "%s: writing SR failed\n", __func__); + return sr; + } + + /* write RTC registers */ + sr = isl1208_i2c_set_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); + if (sr < 0) { + dev_err(&client->dev, "%s: writing RTC section failed\n", + __func__); + return sr; + } + + /* clear WRTC again */ + sr = i2c_smbus_write_byte_data (client, ISL1208_REG_SR, + sr & ~ISL1208_REG_SR_WRTC); + if (sr < 0) { + dev_err(&client->dev, "%s: writing SR failed\n", __func__); + return sr; + } + + return 0; +} + + +static int isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return isl1208_i2c_set_time(to_i2c_client(dev), tm); +} + +static int isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm); +} + +static struct rtc_class_ops isl1208_rtc_ops = { + .proc = isl1208_rtc_proc, + .read_time = isl1208_rtc_read_time, + .set_time = isl1208_rtc_set_time, + .read_alarm = isl1208_rtc_read_alarm, + //.set_alarm = isl1208_rtc_set_alarm, +}; + +/* sysfs interface */ + +static ssize_t isl1208_sysfs_show_atrim(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int atr; + + atr = isl1208_i2c_get_atr(to_i2c_client(dev)); + if (atr < 0) + return atr; + + return sprintf(buf, "%d.%.2d pF\n", atr>>2, (atr&0x3)*25); +} +static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL); + +static ssize_t isl1208_sysfs_show_dtrim(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int dtr; + + dtr = isl1208_i2c_get_dtr(to_i2c_client(dev)); + if (dtr < 0) + return dtr; + + return sprintf(buf, "%d ppm\n", dtr); +} +static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL); + +static ssize_t isl1208_sysfs_show_usr(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int usr; + + usr = isl1208_i2c_get_usr(to_i2c_client(dev)); + if (usr < 0) + return usr; + + return sprintf(buf, "0x%.4x\n", usr); +} + +static ssize_t isl1208_sysfs_store_usr(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int usr = -1; + + if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) { + if (sscanf(buf, "%x", &usr) != 1) + return -EINVAL; + } else { + if (sscanf(buf, "%d", &usr) != 1) + return -EINVAL; + } + + if (usr < 0 || usr > 0xffff) + return -EINVAL; + + return isl1208_i2c_set_usr(to_i2c_client(dev), usr) ? -EIO : count; +} +static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr, + isl1208_sysfs_store_usr); + +static int +isl1208_probe(struct i2c_adapter *adapter, int addr, int kind) +{ + int rc = 0; + struct i2c_client *new_client = NULL; + struct rtc_device *rtc = NULL; + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + rc = -ENODEV; + goto failout; + } + + new_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (new_client == NULL) { + rc = -ENOMEM; + goto failout; + } + + new_client->addr = addr; + new_client->adapter = adapter; + new_client->driver = &isl1208_driver; + new_client->flags = 0; + strcpy(new_client->name, DRV_NAME); + + if (kind < 0) { + rc = isl1208_i2c_validate_client(new_client); + if (rc < 0) + goto failout; + } + + rc = i2c_attach_client(new_client); + if (rc < 0) + goto failout; + + dev_info(&new_client->dev, + "chip found, driver version " DRV_VERSION "\n"); + + rtc = rtc_device_register(isl1208_driver.driver.name, + &new_client->dev, + &isl1208_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + rc = PTR_ERR(rtc); + goto failout_detach; + } + + i2c_set_clientdata(new_client, rtc); + + rc = isl1208_i2c_get_sr(new_client); + if (rc < 0) { + dev_err(&new_client->dev, "reading status failed\n"); + goto failout_unregister; + } + + if (rc & ISL1208_REG_SR_RTCF) + dev_warn(&new_client->dev, "rtc power failure detected, " + "please set clock.\n"); + + rc = device_create_file(&new_client->dev, &dev_attr_atrim); + if (rc < 0) + goto failout_unregister; + rc = device_create_file(&new_client->dev, &dev_attr_dtrim); + if (rc < 0) + goto failout_atrim; + rc = device_create_file(&new_client->dev, &dev_attr_usr); + if (rc < 0) + goto failout_dtrim; + + return 0; + + failout_dtrim: + device_remove_file(&new_client->dev, &dev_attr_dtrim); + failout_atrim: + device_remove_file(&new_client->dev, &dev_attr_atrim); + failout_unregister: + rtc_device_unregister(rtc); + failout_detach: + i2c_detach_client(new_client); + failout: + kfree(new_client); + return rc; +} + +static int +isl1208_attach_adapter (struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, isl1208_probe); +} + +static int +isl1208_detach_client(struct i2c_client *client) +{ + int rc; + struct rtc_device *const rtc = i2c_get_clientdata(client); + + if (rtc) + rtc_device_unregister(rtc); /* do we need to kfree? */ + + rc = i2c_detach_client(client); + if (rc) + return rc; + + kfree(client); + + return 0; +} + +/* module management */ + +static int __init isl1208_init(void) +{ + return i2c_add_driver(&isl1208_driver); +} + +static void __exit isl1208_exit(void) +{ + i2c_del_driver(&isl1208_driver); +} + +MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); +MODULE_DESCRIPTION("Intersil ISL1208 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(isl1208_init); +module_exit(isl1208_exit); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3badb48d662..6533b0f3923 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1518,6 +1518,26 @@ config FB_PXA_PARAMETERS <file:Documentation/fb/pxafb.txt> describes the available parameters. +config FB_MBX + tristate "2700G LCD framebuffer support" + depends on FB && ARCH_PXA + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Framebuffer driver for the Intel 2700G (Marathon) Graphics + Accelerator + +config FB_MBX_DEBUG + bool "Enable debugging info via debugfs" + depends on FB_MBX && DEBUG_FS + default n + ---help--- + Enable this if you want debugging information using the debug + filesystem (debugfs) + + If unsure, say N. + config FB_W100 tristate "W100 frame buffer support" depends on FB && PXA_SHARPSL diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6283d015f8f..95563c9c6b9 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_FB_SIS) += sis/ obj-$(CONFIG_FB_KYRO) += kyro/ obj-$(CONFIG_FB_SAVAGE) += savage/ obj-$(CONFIG_FB_GEODE) += geode/ +obj-$(CONFIG_FB_MBX) += mbx/ obj-$(CONFIG_FB_I810) += vgastate.o obj-$(CONFIG_FB_NEOMAGIC) += neofb.o vgastate.o obj-$(CONFIG_FB_VIRGE) += virgefb.o diff --git a/drivers/video/mbx/Makefile b/drivers/video/mbx/Makefile new file mode 100644 index 00000000000..16c1165cf9c --- /dev/null +++ b/drivers/video/mbx/Makefile @@ -0,0 +1,4 @@ +# Makefile for the 2700G controller driver. + +obj-$(CONFIG_FB_MBX) += mbxfb.o +obj-$(CONFIG_FB_MBX_DEBUG) += mbxfbdebugfs.o diff --git a/drivers/video/mbx/mbxdebugfs.c b/drivers/video/mbx/mbxdebugfs.c new file mode 100644 index 00000000000..84aab3ad024 --- /dev/null +++ b/drivers/video/mbx/mbxdebugfs.c @@ -0,0 +1,188 @@ +#include <linux/debugfs.h> + +#define BIG_BUFFER_SIZE (1024) + +static char big_buffer[BIG_BUFFER_SIZE]; + +struct mbxfb_debugfs_data { + struct dentry *dir; + struct dentry *sysconf; + struct dentry *clock; + struct dentry *display; + struct dentry *gsctl; +}; + +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->u.generic_ip; + return 0; +} + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return count; +} + +static ssize_t sysconf_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char * s = big_buffer; + + s += sprintf(s, "SYSCFG = %08lx\n", SYSCFG); + s += sprintf(s, "PFBASE = %08lx\n", PFBASE); + s += sprintf(s, "PFCEIL = %08lx\n", PFCEIL); + s += sprintf(s, "POLLFLAG = %08lx\n", POLLFLAG); + s += sprintf(s, "SYSRST = %08lx\n", SYSRST); + + return simple_read_from_buffer(userbuf, count, ppos, + big_buffer, s-big_buffer); +} + + +static ssize_t gsctl_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char * s = big_buffer; + + s += sprintf(s, "GSCTRL = %08lx\n", GSCTRL); + s += sprintf(s, "VSCTRL = %08lx\n", VSCTRL); + s += sprintf(s, "GBBASE = %08lx\n", GBBASE); + s += sprintf(s, "VBBASE = %08lx\n", VBBASE); + s += sprintf(s, "GDRCTRL = %08lx\n", GDRCTRL); + s += sprintf(s, "VCMSK = %08lx\n", VCMSK); + s += sprintf(s, "GSCADR = %08lx\n", GSCADR); + s += sprintf(s, "VSCADR = %08lx\n", VSCADR); + s += sprintf(s, "VUBASE = %08lx\n", VUBASE); + s += sprintf(s, "VVBASE = %08lx\n", VVBASE); + s += sprintf(s, "GSADR = %08lx\n", GSADR); + s += sprintf(s, "VSADR = %08lx\n", VSADR); + s += sprintf(s, "HCCTRL = %08lx\n", HCCTRL); + s += sprintf(s, "HCSIZE = %08lx\n", HCSIZE); + s += sprintf(s, "HCPOS = %08lx\n", HCPOS); + s += sprintf(s, "HCBADR = %08lx\n", HCBADR); + s += sprintf(s, "HCCKMSK = %08lx\n", HCCKMSK); + s += sprintf(s, "GPLUT = %08lx\n", GPLUT); + + return simple_read_from_buffer(userbuf, count, ppos, + big_buffer, s-big_buffer); +} + +static ssize_t display_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char * s = big_buffer; + + s += sprintf(s, "DSCTRL = %08lx\n", DSCTRL); + s += sprintf(s, "DHT01 = %08lx\n", DHT01); + s += sprintf(s, "DHT02 = %08lx\n", DHT02); + s += sprintf(s, "DHT03 = %08lx\n", DHT03); + s += sprintf(s, "DVT01 = %08lx\n", DVT01); + s += sprintf(s, "DVT02 = %08lx\n", DVT02); + s += sprintf(s, "DVT03 = %08lx\n", DVT03); + s += sprintf(s, "DBCOL = %08lx\n", DBCOL); + s += sprintf(s, "BGCOLOR = %08lx\n", BGCOLOR); + s += sprintf(s, "DINTRS = %08lx\n", DINTRS); + s += sprintf(s, "DINTRE = %08lx\n", DINTRE); + s += sprintf(s, "DINTRCNT = %08lx\n", DINTRCNT); + s += sprintf(s, "DSIG = %08lx\n", DSIG); + s += sprintf(s, "DMCTRL = %08lx\n", DMCTRL); + s += sprintf(s, "CLIPCTRL = %08lx\n", CLIPCTRL); + s += sprintf(s, "SPOCTRL = %08lx\n", SPOCTRL); + s += sprintf(s, "SVCTRL = %08lx\n", SVCTRL); + s += sprintf(s, "DLSTS = %08lx\n", DLSTS); + s += sprintf(s, "DLLCTRL = %08lx\n", DLLCTRL); + s += sprintf(s, "DVLNUM = %08lx\n", DVLNUM); + s += sprintf(s, "DUCTRL = %08lx\n", DUCTRL); + s += sprintf(s, "DVECTRL = %08lx\n", DVECTRL); + s += sprintf(s, "DHDET = %08lx\n", DHDET); + s += sprintf(s, "DVDET = %08lx\n", DVDET); + s += sprintf(s, "DODMSK = %08lx\n", DODMSK); + s += sprintf(s, "CSC01 = %08lx\n", CSC01); + s += sprintf(s, "CSC02 = %08lx\n", CSC02); + s += sprintf(s, "CSC03 = %08lx\n", CSC03); + s += sprintf(s, "CSC04 = %08lx\n", CSC04); + s += sprintf(s, "CSC05 = %08lx\n", CSC05); + + return simple_read_from_buffer(userbuf, count, ppos, + big_buffer, s-big_buffer); +} + +static ssize_t clock_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char * s = big_buffer; + + s += sprintf(s, "SYSCLKSRC = %08lx\n", SYSCLKSRC); + s += sprintf(s, "PIXCLKSRC = %08lx\n", PIXCLKSRC); + s += sprintf(s, "CLKSLEEP = %08lx\n", CLKSLEEP); + s += sprintf(s, "COREPLL = %08lx\n", COREPLL); + s += sprintf(s, "DISPPLL = %08lx\n", DISPPLL); + s += sprintf(s, "PLLSTAT = %08lx\n", PLLSTAT); + s += sprintf(s, "VOVRCLK = %08lx\n", VOVRCLK); + s += sprintf(s, "PIXCLK = %08lx\n", PIXCLK); + s += sprintf(s, "MEMCLK = %08lx\n", MEMCLK); + s += sprintf(s, "M24CLK = %08lx\n", M24CLK); + s += sprintf(s, "MBXCLK = %08lx\n", MBXCLK); + s += sprintf(s, "SDCLK = %08lx\n", SDCLK); + s += sprintf(s, "PIXCLKDIV = %08lx\n", PIXCLKDIV); + + return simple_read_from_buffer(userbuf, count, ppos, + big_buffer, s-big_buffer); +} + +static struct file_operations sysconf_fops = { + .read = sysconf_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + +static struct file_operations clock_fops = { + .read = clock_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + +static struct file_operations display_fops = { + .read = display_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + +static struct file_operations gsctl_fops = { + .read = gsctl_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + + +static void __devinit mbxfb_debugfs_init(struct fb_info *fbi) +{ + struct mbxfb_info *mfbi = fbi->par; + struct mbxfb_debugfs_data *dbg; + + dbg = kzalloc(sizeof(struct mbxfb_debugfs_data), GFP_KERNEL); + mfbi->debugfs_data = dbg; + + dbg->dir = debugfs_create_dir("mbxfb", NULL); + dbg->sysconf = debugfs_create_file("sysconf", 0444, dbg->dir, + fbi, &sysconf_fops); + dbg->clock = debugfs_create_file("clock", 0444, dbg->dir, + fbi, &clock_fops); + dbg->display = debugfs_create_file("display", 0444, dbg->dir, + fbi, &display_fops); + dbg->gsctl = debugfs_create_file("gsctl", 0444, dbg->dir, + fbi, &gsctl_fops); +} + +static void __devexit mbxfb_debugfs_remove(struct fb_info *fbi) +{ + struct mbxfb_info *mfbi = fbi->par; + struct mbxfb_debugfs_data *dbg = mfbi->debugfs_data; + + debugfs_remove(dbg->gsctl); + debugfs_remove(dbg->display); + debugfs_remove(dbg->clock); + debugfs_remove(dbg->sysconf); + debugfs_remove(dbg->dir); +} diff --git a/drivers/video/mbx/mbxfb.c b/drivers/video/mbx/mbxfb.c new file mode 100644 index 00000000000..6849ab75d40 --- /dev/null +++ b/drivers/video/mbx/mbxfb.c @@ -0,0 +1,683 @@ +/* + * linux/drivers/video/mbx/mbxfb.c + * + * Copyright (C) 2006 Compulab, Ltd. + * Mike Rapoport <mike@compulab.co.il> + * + * Based on pxafb.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * Intel 2700G (Marathon) Graphics Accelerator Frame Buffer Driver + * + */ + +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/io.h> + +#include <video/mbxfb.h> + +#include "regs.h" +#include "reg_bits.h" + +static unsigned long virt_base_2700; + +#define MIN_XRES 16 +#define MIN_YRES 16 +#define MAX_XRES 2048 +#define MAX_YRES 2048 + +#define MAX_PALETTES 16 + +/* FIXME: take care of different chip revisions with different sizes + of ODFB */ +#define MEMORY_OFFSET 0x60000 + +struct mbxfb_info { + struct device *dev; + + struct resource *fb_res; + struct resource *fb_req; + + struct resource *reg_res; + struct resource *reg_req; + + void __iomem *fb_virt_addr; + unsigned long fb_phys_addr; + + void __iomem *reg_virt_addr; + unsigned long reg_phys_addr; + + int (*platform_probe) (struct fb_info * fb); + int (*platform_remove) (struct fb_info * fb); + + u32 pseudo_palette[MAX_PALETTES]; +#ifdef CONFIG_FB_MBX_DEBUG + void *debugfs_data; +#endif + +}; + +static struct fb_var_screeninfo mbxfb_default __devinitdata = { + .xres = 640, + .yres = 480, + .xres_virtual = 640, + .yres_virtual = 480, + .bits_per_pixel = 16, + .red = {11, 5, 0}, + .green = {5, 6, 0}, + .blue = {0, 5, 0}, + .activate = FB_ACTIVATE_TEST, + .height = -1, + .width = -1, + .pixclock = 40000, + .left_margin = 48, + .right_margin = 16, + .upper_margin = 33, + .lower_margin = 10, + .hsync_len = 96, + .vsync_len = 2, + .vmode = FB_VMODE_NONINTERLACED, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, +}; + +static struct fb_fix_screeninfo mbxfb_fix __devinitdata = { + .id = "MBX", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +struct pixclock_div { + u8 m; + u8 n; + u8 p; +}; + +static unsigned int mbxfb_get_pixclock(unsigned int pixclock_ps, + struct pixclock_div *div) +{ + u8 m, n, p; + unsigned int err = 0; + unsigned int min_err = ~0x0; + unsigned int clk; + unsigned int best_clk = 0; + unsigned int ref_clk = 13000; /* FIXME: take from platform data */ + unsigned int pixclock; + + /* convert pixclock to KHz */ + pixclock = PICOS2KHZ(pixclock_ps); + + for (m = 1; m < 64; m++) { + for (n = 1; n < 8; n++) { + for (p = 0; p < 8; p++) { + clk = (ref_clk * m) / (n * (1 << p)); + err = (clk > pixclock) ? (clk - pixclock) : + (pixclock - clk); + if (err < min_err) { + min_err = err; + best_clk = clk; + div->m = m; + div->n = n; + div->p = p; + } + } + } + } + return KHZ2PICOS(best_clk); +} + +static int mbxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int trans, struct fb_info *info) +{ + u32 val, ret = 1; + + if (regno < MAX_PALETTES) { + u32 *pal = info->pseudo_palette; + + val = (red & 0xf800) | ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + pal[regno] = val; + ret = 0; + } + + return ret; +} + +static int mbxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct pixclock_div div; + + var->pixclock = mbxfb_get_pixclock(var->pixclock, &div); + + if (var->xres < MIN_XRES) + var->xres = MIN_XRES; + if (var->yres < MIN_YRES) + var->yres = MIN_YRES; + if (var->xres > MAX_XRES) + return -EINVAL; + if (var->yres > MAX_YRES) + return -EINVAL; + var->xres_virtual = max(var->xres_virtual, var->xres); + var->yres_virtual = max(var->yres_virtual, var->yres); + + switch (var->bits_per_pixel) { + /* 8 bits-per-pixel is not supported yet */ + case 8: + return -EINVAL; + case 16: + var->green.length = (var->green.length == 5) ? 5 : 6; + var->red.length = 5; + var->blue.length = 5; + var->transp.length = 6 - var->green.length; + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 5 + var->green.length; + var->transp.offset = (5 + var->red.offset) & 15; + break; + case 24: /* RGB 888 */ + case 32: /* RGBA 8888 */ + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.length = var->bits_per_pixel - 24; + var->transp.offset = (var->transp.length) ? 24 : 0; + break; + } + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + + return 0; +} + +static int mbxfb_set_par(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct pixclock_div div; + ushort hbps, ht, hfps, has; + ushort vbps, vt, vfps, vas; + u32 gsctrl = readl(GSCTRL); + u32 gsadr = readl(GSADR); + + info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; + + /* setup color mode */ + gsctrl &= ~(FMsk(GSCTRL_GPIXFMT)); + /* FIXME: add *WORKING* support for 8-bits per color */ + if (info->var.bits_per_pixel == 8) { + return -EINVAL; + } else { + fb_dealloc_cmap(&info->cmap); + gsctrl &= ~GSCTRL_LUT_EN; + + info->fix.visual = FB_VISUAL_TRUECOLOR; + switch (info->var.bits_per_pixel) { + case 16: + if (info->var.green.length == 5) + gsctrl |= GSCTRL_GPIXFMT_ARGB1555; + else + gsctrl |= GSCTRL_GPIXFMT_RGB565; + break; + case 24: + gsctrl |= GSCTRL_GPIXFMT_RGB888; + break; + case 32: + gsctrl |= GSCTRL_GPIXFMT_ARGB8888; + break; + } + } + + /* setup resolution */ + gsctrl &= ~(FMsk(GSCTRL_GSWIDTH) | FMsk(GSCTRL_GSHEIGHT)); + gsctrl |= Gsctrl_Width(info->var.xres - 1) | + Gsctrl_Height(info->var.yres - 1); + writel(gsctrl, GSCTRL); + udelay(1000); + + gsadr &= ~(FMsk(GSADR_SRCSTRIDE)); + gsadr |= Gsadr_Srcstride(info->var.xres * info->var.bits_per_pixel / + (8 * 16) - 1); + writel(gsadr, GSADR); + udelay(1000); + + /* setup timings */ + var->pixclock = mbxfb_get_pixclock(info->var.pixclock, &div); + + writel((Disp_Pll_M(div.m) | Disp_Pll_N(div.n) | + Disp_Pll_P(div.p) | DISP_PLL_EN), DISPPLL); + + hbps = var->hsync_len; + has = hbps + var->left_margin; + hfps = has + var->xres; + ht = hfps + var->right_margin; + + vbps = var->vsync_len; + vas = vbps + var->upper_margin; + vfps = vas + var->yres; + vt = vfps + var->lower_margin; + + writel((Dht01_Hbps(hbps) | Dht01_Ht(ht)), DHT01); + writel((Dht02_Hlbs(has) | Dht02_Has(has)), DHT02); + writel((Dht03_Hfps(hfps) | Dht03_Hrbs(hfps)), DHT03); + writel((Dhdet_Hdes(has) | Dhdet_Hdef(hfps)), DHDET); + + writel((Dvt01_Vbps(vbps) | Dvt01_Vt(vt)), DVT01); + writel((Dvt02_Vtbs(vas) | Dvt02_Vas(vas)), DVT02); + writel((Dvt03_Vfps(vfps) | Dvt03_Vbbs(vfps)), DVT03); + writel((Dvdet_Vdes(vas) | Dvdet_Vdef(vfps)), DVDET); + writel((Dvectrl_Vevent(vfps) | Dvectrl_Vfetch(vbps)), DVECTRL); + + writel((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); + + return 0; +} + +static int mbxfb_blank(int blank, struct fb_info *info) +{ + switch (blank) { + case FB_BLANK_POWERDOWN: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + writel((readl(DSCTRL) & ~DSCTRL_SYNCGEN_EN), DSCTRL); + udelay(1000); + writel((readl(PIXCLK) & ~PIXCLK_EN), PIXCLK); + udelay(1000); + writel((readl(VOVRCLK) & ~VOVRCLK_EN), VOVRCLK); + udelay(1000); + break; + case FB_BLANK_UNBLANK: + writel((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); + udelay(1000); + writel((readl(PIXCLK) | PIXCLK_EN), PIXCLK); + udelay(1000); + break; + } + return 0; +} + +static struct fb_ops mbxfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = mbxfb_check_var, + .fb_set_par = mbxfb_set_par, + .fb_setcolreg = mbxfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = mbxfb_blank, +}; + +/* + Enable external SDRAM controller. Assume that all clocks are active + by now. +*/ +static void __devinit setup_memc(struct fb_info *fbi) +{ + struct mbxfb_info *mfbi = fbi->par; + unsigned long tmp; + int i; + + /* FIXME: use platfrom specific parameters */ + /* setup SDRAM controller */ + writel((LMCFG_LMC_DS | LMCFG_LMC_TS | LMCFG_LMD_TS | + LMCFG_LMA_TS), + LMCFG); + udelay(1000); + + writel(LMPWR_MC_PWR_ACT, LMPWR); + udelay(1000); + + /* setup SDRAM timings */ + writel((Lmtim_Tras(7) | Lmtim_Trp(3) | Lmtim_Trcd(3) | + Lmtim_Trc(9) | Lmtim_Tdpl(2)), + LMTIM); + udelay(1000); + /* setup SDRAM refresh rate */ + writel(0xc2b, LMREFRESH); + udelay(1000); + /* setup SDRAM type parameters */ + writel((LMTYPE_CASLAT_3 | LMTYPE_BKSZ_2 | LMTYPE_ROWSZ_11 | + LMTYPE_COLSZ_8), + LMTYPE); + udelay(1000); + /* enable memory controller */ + writel(LMPWR_MC_PWR_ACT, LMPWR); + udelay(1000); + + /* perform dummy reads */ + for ( i = 0; i < 16; i++ ) { + tmp = readl(fbi->screen_base); + } +} + +static void enable_clocks(struct fb_info *fbi) +{ + /* enable clocks */ + writel(SYSCLKSRC_PLL_2, SYSCLKSRC); + udelay(1000); + writel(PIXCLKSRC_PLL_1, PIXCLKSRC); + udelay(1000); + writel(0x00000000, CLKSLEEP); + udelay(1000); + writel((Core_Pll_M(0x17) | Core_Pll_N(0x3) | Core_Pll_P(0x0) | + CORE_PLL_EN), + COREPLL); + udelay(1000); + writel((Disp_Pll_M(0x1b) | Disp_Pll_N(0x7) | Disp_Pll_P(0x1) | + DISP_PLL_EN), + DISPPLL); + + writel(0x00000000, VOVRCLK); + udelay(1000); + writel(PIXCLK_EN, PIXCLK); + udelay(1000); + writel(MEMCLK_EN, MEMCLK); + udelay(1000); + writel(0x00000006, M24CLK); + udelay(1000); + writel(0x00000006, MBXCLK); + udelay(1000); + writel(SDCLK_EN, SDCLK); + udelay(1000); + writel(0x00000001, PIXCLKDIV); + udelay(1000); +} + +static void __devinit setup_graphics(struct fb_info *fbi) +{ + unsigned long gsctrl; + + gsctrl = GSCTRL_GAMMA_EN | Gsctrl_Width(fbi->var.xres - 1) | + Gsctrl_Height(fbi->var.yres - 1); + switch (fbi->var.bits_per_pixel) { + case 16: + if (fbi->var.green.length == 5) + gsctrl |= GSCTRL_GPIXFMT_ARGB1555; + else + gsctrl |= GSCTRL_GPIXFMT_RGB565; + break; + case 24: + gsctrl |= GSCTRL_GPIXFMT_RGB888; + break; + case 32: + gsctrl |= GSCTRL_GPIXFMT_ARGB8888; + break; + } + + writel(gsctrl, GSCTRL); + udelay(1000); + writel(0x00000000, GBBASE); + udelay(1000); + writel(0x00ffffff, GDRCTRL); + udelay(1000); + writel((GSCADR_STR_EN | Gscadr_Gbase_Adr(0x6000)), GSCADR); + udelay(1000); + writel(0x00000000, GPLUT); + udelay(1000); +} + +static void __devinit setup_display(struct fb_info *fbi) +{ + unsigned long dsctrl = 0; + + dsctrl = DSCTRL_BLNK_POL; + if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) + dsctrl |= DSCTRL_HS_POL; + if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) + dsctrl |= DSCTRL_VS_POL; + writel(dsctrl, DSCTRL); + udelay(1000); + writel(0xd0303010, DMCTRL); + udelay(1000); + writel((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); +} + +static void __devinit enable_controller(struct fb_info *fbi) +{ + writel(SYSRST_RST, SYSRST); + udelay(1000); + + + enable_clocks(fbi); + setup_memc(fbi); + setup_graphics(fbi); + setup_display(fbi); +} + +#ifdef CONFIG_PM +/* + * Power management hooks. Note that we won't be called from IRQ context, + * unlike the blank functions above, so we may sleep. + */ +static int mbxfb_suspend(struct platform_device *dev, pm_message_t state) +{ + /* make frame buffer memory enter self-refresh mode */ + writel(LMPWR_MC_PWR_SRM, LMPWR); + while (LMPWRSTAT != LMPWRSTAT_MC_PWR_SRM) + ; /* empty statement */ + + /* reset the device, since it's initial state is 'mostly sleeping' */ + writel(SYSRST_RST, SYSRST); + return 0; +} + +static int mbxfb_resume(struct platform_device *dev) +{ + struct fb_info *fbi = platform_get_drvdata(dev); + + enable_clocks(fbi); +/* setup_graphics(fbi); */ +/* setup_display(fbi); */ + + writel((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); + return 0; +} +#else +#define mbxfb_suspend NULL +#define mbxfb_resume NULL +#endif + +/* debugfs entries */ +#ifndef CONFIG_FB_MBX_DEBUG +#define mbxfb_debugfs_init(x) do {} while(0) +#define mbxfb_debugfs_remove(x) do {} while(0) +#endif + +#define res_size(_r) (((_r)->end - (_r)->start) + 1) + +static int __devinit mbxfb_probe(struct platform_device *dev) +{ + int ret; + struct fb_info *fbi; + struct mbxfb_info *mfbi; + struct mbxfb_platform_data *pdata; + + dev_dbg(dev, "mbxfb_probe\n"); + + fbi = framebuffer_alloc(sizeof(struct mbxfb_info), &dev->dev); + if (fbi == NULL) { + dev_err(&dev->dev, "framebuffer_alloc failed\n"); + return -ENOMEM; + } + + mfbi = fbi->par; + fbi->pseudo_palette = mfbi->pseudo_palette; + pdata = dev->dev.platform_data; + if (pdata->probe) + mfbi->platform_probe = pdata->probe; + if (pdata->remove) + mfbi->platform_remove = pdata->remove; + + mfbi->fb_res = platform_get_resource(dev, IORESOURCE_MEM, 0); + mfbi->reg_res = platform_get_resource(dev, IORESOURCE_MEM, 1); + + if (!mfbi->fb_res || !mfbi->reg_res) { + dev_err(&dev->dev, "no resources found\n"); + ret = -ENODEV; + goto err1; + } + + mfbi->fb_req = request_mem_region(mfbi->fb_res->start, + res_size(mfbi->fb_res), dev->name); + if (mfbi->fb_req == NULL) { + dev_err(&dev->dev, "failed to claim framebuffer memory\n"); + ret = -EINVAL; + goto err1; + } + mfbi->fb_phys_addr = mfbi->fb_res->start; + + mfbi->reg_req = request_mem_region(mfbi->reg_res->start, + res_size(mfbi->reg_res), dev->name); + if (mfbi->reg_req == NULL) { + dev_err(&dev->dev, "failed to claim Marathon registers\n"); + ret = -EINVAL; + goto err2; + } + mfbi->reg_phys_addr = mfbi->reg_res->start; + + mfbi->reg_virt_addr = ioremap_nocache(mfbi->reg_phys_addr, + res_size(mfbi->reg_req)); + if (!mfbi->reg_virt_addr) { + dev_err(&dev->dev, "failed to ioremap Marathon registers\n"); + ret = -EINVAL; + goto err3; + } + virt_base_2700 = (unsigned long)mfbi->reg_virt_addr; + + mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr, + res_size(mfbi->fb_req)); + if (!mfbi->reg_virt_addr) { + dev_err(&dev->dev, "failed to ioremap frame buffer\n"); + ret = -EINVAL; + goto err4; + } + + /* FIXME: get from platform */ + fbi->screen_base = (char __iomem *)(mfbi->fb_virt_addr + 0x60000); + fbi->screen_size = 8 * 1024 * 1024; /* 8 Megs */ + fbi->fbops = &mbxfb_ops; + + fbi->var = mbxfb_default; + fbi->fix = mbxfb_fix; + fbi->fix.smem_start = mfbi->fb_phys_addr + 0x60000; + fbi->fix.smem_len = 8 * 1024 * 1024; + fbi->fix.line_length = 640 * 2; + + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret < 0) { + dev_err(&dev->dev, "fb_alloc_cmap failed\n"); + ret = -EINVAL; + goto err5; + } + + platform_set_drvdata(dev, fbi); + + printk(KERN_INFO "fb%d: mbx frame buffer device\n", fbi->node); + + if (mfbi->platform_probe) + mfbi->platform_probe(fbi); + + enable_controller(fbi); + + mbxfb_debugfs_init(fbi); + + ret = register_framebuffer(fbi); + if (ret < 0) { + dev_err(&dev->dev, "register_framebuffer failed\n"); + ret = -EINVAL; + goto err6; + } + + return 0; + +err6: + fb_dealloc_cmap(&fbi->cmap); +err5: + iounmap(mfbi->fb_virt_addr); +err4: + iounmap(mfbi->reg_virt_addr); +err3: + release_mem_region(mfbi->reg_res->start, res_size(mfbi->reg_res)); +err2: + release_mem_region(mfbi->fb_res->start, res_size(mfbi->fb_res)); +err1: + framebuffer_release(fbi); + + return ret; +} + +static int __devexit mbxfb_remove(struct platform_device *dev) +{ + struct fb_info *fbi = platform_get_drvdata(dev); + + writel(SYSRST_RST, SYSRST); + udelay(1000); + + mbxfb_debugfs_remove(fbi); + + if (fbi) { + struct mbxfb_info *mfbi = fbi->par; + + unregister_framebuffer(fbi); + if (mfbi) { + if (mfbi->platform_remove) + mfbi->platform_remove(fbi); + + if (mfbi->fb_virt_addr) + iounmap(mfbi->fb_virt_addr); + if (mfbi->reg_virt_addr) + iounmap(mfbi->reg_virt_addr); + if (mfbi->reg_req) + release_mem_region(mfbi->reg_req->start, + res_size(mfbi->reg_req)); + if (mfbi->fb_req) + release_mem_region(mfbi->fb_req->start, + res_size(mfbi->fb_req)); + } + framebuffer_release(fbi); + } + + return 0; +} + +static struct platform_driver mbxfb_driver = { + .probe = mbxfb_probe, + .remove = mbxfb_remove, + .suspend = mbxfb_suspend, + .resume = mbxfb_resume, + .driver = { + .name = "mbx-fb", + }, +}; + +int __devinit mbxfb_init(void) +{ + return platform_driver_register(&mbxfb_driver); +} + +static void __devexit mbxfb_exit(void) +{ + platform_driver_unregister(&mbxfb_driver); +} + +module_init(mbxfb_init); +module_exit(mbxfb_exit); + +MODULE_DESCRIPTION("loadable framebuffer driver for Marathon device"); +MODULE_AUTHOR("Mike Rapoport, Compulab"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mbx/reg_bits.h b/drivers/video/mbx/reg_bits.h new file mode 100644 index 00000000000..c226a8e4531 --- /dev/null +++ b/drivers/video/mbx/reg_bits.h @@ -0,0 +1,418 @@ +#ifndef __REG_BITS_2700G_ +#define __REG_BITS_2700G_ + +/* use defines from asm-arm/arch-pxa/bitfields.h for bit fields access */ +#define UData(Data) ((unsigned long) (Data)) +#define Fld(Size, Shft) (((Size) << 16) + (Shft)) +#define FSize(Field) ((Field) >> 16) +#define FShft(Field) ((Field) & 0x0000FFFF) +#define FMsk(Field) (((UData (1) << FSize (Field)) - 1) << FShft (Field)) +#define FAlnMsk(Field) ((UData (1) << FSize (Field)) - 1) +#define F1stBit(Field) (UData (1) << FShft (Field)) + +#define SYSRST_RST (1 << 0) + +/* SYSCLKSRC - SYSCLK Source Control Register */ +#define SYSCLKSRC_SEL Fld(2,0) +#define SYSCLKSRC_REF ((0x0) << FShft(SYSCLKSRC_SEL)) +#define SYSCLKSRC_PLL_1 ((0x1) << FShft(SYSCLKSRC_SEL)) +#define SYSCLKSRC_PLL_2 ((0x2) << FShft(SYSCLKSRC_SEL)) + +/* PIXCLKSRC - PIXCLK Source Control Register */ +#define PIXCLKSRC_SEL Fld(2,0) +#define PIXCLKSRC_REF ((0x0) << FShft(PIXCLKSRC_SEL)) +#define PIXCLKSRC_PLL_1 ((0x1) << FShft(PIXCLKSRC_SEL)) +#define PIXCLKSRC_PLL_2 ((0x2) << FShft(PIXCLKSRC_SEL)) + +/* Clock Disable Register */ +#define CLKSLEEP_SLP (1 << 0) + +/* Core PLL Control Register */ +#define CORE_PLL_M Fld(6,7) +#define Core_Pll_M(x) ((x) << FShft(CORE_PLL_M)) +#define CORE_PLL_N Fld(3,4) +#define Core_Pll_N(x) ((x) << FShft(CORE_PLL_N)) +#define CORE_PLL_P Fld(3,1) +#define Core_Pll_P(x) ((x) << FShft(CORE_PLL_P)) +#define CORE_PLL_EN (1 << 0) + +/* Display PLL Control Register */ +#define DISP_PLL_M Fld(6,7) +#define Disp_Pll_M(x) ((x) << FShft(DISP_PLL_M)) +#define DISP_PLL_N Fld(3,4) +#define Disp_Pll_N(x) ((x) << FShft(DISP_PLL_N)) +#define DISP_PLL_P Fld(3,1) +#define Disp_Pll_P(x) ((x) << FShft(DISP_PLL_P)) +#define DISP_PLL_EN (1 << 0) + +/* PLL status register */ +#define PLLSTAT_CORE_PLL_LOST_L (1 << 3) +#define PLLSTAT_CORE_PLL_LSTS (1 << 2) +#define PLLSTAT_DISP_PLL_LOST_L (1 << 1) +#define PLLSTAT_DISP_PLL_LSTS (1 << 0) + +/* Video and scale clock control register */ +#define VOVRCLK_EN (1 << 0) + +/* Pixel clock control register */ +#define PIXCLK_EN (1 << 0) + +/* Memory clock control register */ +#define MEMCLK_EN (1 << 0) + +/* MBX clock control register */ +#define MBXCLK_DIV Fld(2,2) +#define MBXCLK_DIV_1 ((0x0) << FShft(MBXCLK_DIV)) +#define MBXCLK_DIV_2 ((0x1) << FShft(MBXCLK_DIV)) +#define MBXCLK_DIV_3 ((0x2) << FShft(MBXCLK_DIV)) +#define MBXCLK_DIV_4 ((0x3) << FShft(MBXCLK_DIV)) +#define MBXCLK_EN Fld(2,0) +#define MBXCLK_EN_NONE ((0x0) << FShft(MBXCLK_EN)) +#define MBXCLK_EN_2D ((0x1) << FShft(MBXCLK_EN)) +#define MBXCLK_EN_BOTH ((0x2) << FShft(MBXCLK_EN)) + +/* M24 clock control register */ +#define M24CLK_DIV Fld(2,1) +#define M24CLK_DIV_1 ((0x0) << FShft(M24CLK_DIV)) +#define M24CLK_DIV_2 ((0x1) << FShft(M24CLK_DIV)) +#define M24CLK_DIV_3 ((0x2) << FShft(M24CLK_DIV)) +#define M24CLK_DIV_4 ((0x3) << FShft(M24CLK_DIV)) +#define M24CLK_EN (1 << 0) + +/* SDRAM clock control register */ +#define SDCLK_EN (1 << 0) + +/* PixClk Divisor Register */ +#define PIXCLKDIV_PD Fld(9,0) +#define Pixclkdiv_Pd(x) ((x) << FShft(PIXCLKDIV_PD)) + +/* LCD Config control register */ +#define LCDCFG_IN_FMT Fld(3,28) +#define Lcdcfg_In_Fmt(x) ((x) << FShft(LCDCFG_IN_FMT)) +#define LCDCFG_LCD1DEN_POL (1 << 27) +#define LCDCFG_LCD1FCLK_POL (1 << 26) +#define LCDCFG_LCD1LCLK_POL (1 << 25) +#define LCDCFG_LCD1D_POL (1 << 24) +#define LCDCFG_LCD2DEN_POL (1 << 23) +#define LCDCFG_LCD2FCLK_POL (1 << 22) +#define LCDCFG_LCD2LCLK_POL (1 << 21) +#define LCDCFG_LCD2D_POL (1 << 20) +#define LCDCFG_LCD1_TS (1 << 19) +#define LCDCFG_LCD1D_DS (1 << 18) +#define LCDCFG_LCD1C_DS (1 << 17) +#define LCDCFG_LCD1_IS_IN (1 << 16) +#define LCDCFG_LCD2_TS (1 << 3) +#define LCDCFG_LCD2D_DS (1 << 2) +#define LCDCFG_LCD2C_DS (1 << 1) +#define LCDCFG_LCD2_IS_IN (1 << 0) + +/* On-Die Frame Buffer Power Control Register */ +#define ODFBPWR_SLOW (1 << 2) +#define ODFBPWR_MODE Fld(2,0) +#define ODFBPWR_MODE_ACT ((0x0) << FShft(ODFBPWR_MODE)) +#define ODFBPWR_MODE_ACT_LP ((0x1) << FShft(ODFBPWR_MODE)) +#define ODFBPWR_MODE_SLEEP ((0x2) << FShft(ODFBPWR_MODE)) +#define ODFBPWR_MODE_SHUTD ((0x3) << FShft(ODFBPWR_MODE)) + +/* On-Die Frame Buffer Power State Status Register */ +#define ODFBSTAT_ACT (1 << 2) +#define ODFBSTAT_SLP (1 << 1) +#define ODFBSTAT_SDN (1 << 0) + +/* LMRST - Local Memory (SDRAM) Reset */ +#define LMRST_MC_RST (1 << 0) + +/* LMCFG - Local Memory (SDRAM) Configuration Register */ +#define LMCFG_LMC_DS (1 << 5) +#define LMCFG_LMD_DS (1 << 4) +#define LMCFG_LMA_DS (1 << 3) +#define LMCFG_LMC_TS (1 << 2) +#define LMCFG_LMD_TS (1 << 1) +#define LMCFG_LMA_TS (1 << 0) + +/* LMPWR - Local Memory (SDRAM) Power Control Register */ +#define LMPWR_MC_PWR_CNT Fld(2,0) +#define LMPWR_MC_PWR_ACT ((0x0) << FShft(LMPWR_MC_PWR_CNT)) /* Active */ +#define LMPWR_MC_PWR_SRM ((0x1) << FShft(LMPWR_MC_PWR_CNT)) /* Self-refresh */ +#define LMPWR_MC_PWR_DPD ((0x3) << FShft(LMPWR_MC_PWR_CNT)) /* deep power down */ + +/* LMPWRSTAT - Local Memory (SDRAM) Power Status Register */ +#define LMPWRSTAT_MC_PWR_CNT Fld(2,0) +#define LMPWRSTAT_MC_PWR_ACT ((0x0) << FShft(LMPWRSTAT_MC_PWR_CNT)) /* Active */ +#define LMPWRSTAT_MC_PWR_SRM ((0x1) << FShft(LMPWRSTAT_MC_PWR_CNT)) /* Self-refresh */ +#define LMPWRSTAT_MC_PWR_DPD ((0x3) << FShft(LMPWRSTAT_MC_PWR_CNT)) /* deep power down */ + +/* LMTYPE - Local Memory (SDRAM) Type Register */ +#define LMTYPE_CASLAT Fld(3,10) +#define LMTYPE_CASLAT_1 ((0x1) << FShft(LMTYPE_CASLAT)) +#define LMTYPE_CASLAT_2 ((0x2) << FShft(LMTYPE_CASLAT)) +#define LMTYPE_CASLAT_3 ((0x3) << FShft(LMTYPE_CASLAT)) +#define LMTYPE_BKSZ Fld(2,8) +#define LMTYPE_BKSZ_1 ((0x1) << FShft(LMTYPE_BKSZ)) +#define LMTYPE_BKSZ_2 ((0x2) << FShft(LMTYPE_BKSZ)) +#define LMTYPE_ROWSZ Fld(4,4) +#define LMTYPE_ROWSZ_11 ((0xb) << FShft(LMTYPE_ROWSZ)) +#define LMTYPE_ROWSZ_12 ((0xc) << FShft(LMTYPE_ROWSZ)) +#define LMTYPE_ROWSZ_13 ((0xd) << FShft(LMTYPE_ROWSZ)) +#define LMTYPE_COLSZ Fld(4,0) +#define LMTYPE_COLSZ_7 ((0x7) << FShft(LMTYPE_COLSZ)) +#define LMTYPE_COLSZ_8 ((0x8) << FShft(LMTYPE_COLSZ)) +#define LMTYPE_COLSZ_9 ((0x9) << FShft(LMTYPE_COLSZ)) +#define LMTYPE_COLSZ_10 ((0xa) << FShft(LMTYPE_COLSZ)) +#define LMTYPE_COLSZ_11 ((0xb) << FShft(LMTYPE_COLSZ)) +#define LMTYPE_COLSZ_12 ((0xc) << FShft(LMTYPE_COLSZ)) + +/* LMTIM - Local Memory (SDRAM) Timing Register */ +#define LMTIM_TRAS Fld(4,16) +#define Lmtim_Tras(x) ((x) << FShft(LMTIM_TRAS)) +#define LMTIM_TRP Fld(4,12) +#define Lmtim_Trp(x) ((x) << FShft(LMTIM_TRP)) +#define LMTIM_TRCD Fld(4,8) +#define Lmtim_Trcd(x) ((x) << FShft(LMTIM_TRCD)) +#define LMTIM_TRC Fld(4,4) +#define Lmtim_Trc(x) ((x) << FShft(LMTIM_TRC)) +#define LMTIM_TDPL Fld(4,0) +#define Lmtim_Tdpl(x) ((x) << FShft(LMTIM_TDPL)) + +/* LMREFRESH - Local Memory (SDRAM) tREF Control Register */ +#define LMREFRESH_TREF Fld(2,0) +#define Lmrefresh_Tref(x) ((x) << FShft(LMREFRESH_TREF)) + +/* GSCTRL - Graphics surface control register */ +#define GSCTRL_LUT_EN (1 << 31) +#define GSCTRL_GPIXFMT Fld(4,27) +#define GSCTRL_GPIXFMT_INDEXED ((0x0) << FShft(GSCTRL_GPIXFMT)) +#define GSCTRL_GPIXFMT_ARGB4444 ((0x4) << FShft(GSCTRL_GPIXFMT)) +#define GSCTRL_GPIXFMT_ARGB1555 ((0x5) << FShft(GSCTRL_GPIXFMT)) +#define GSCTRL_GPIXFMT_RGB888 ((0x6) << FShft(GSCTRL_GPIXFMT)) +#define GSCTRL_GPIXFMT_RGB565 ((0x7) << FShft(GSCTRL_GPIXFMT)) +#define GSCTRL_GPIXFMT_ARGB8888 ((0x8) << FShft(GSCTRL_GPIXFMT)) +#define GSCTRL_GAMMA_EN (1 << 26) + +#define GSCTRL_GSWIDTH Fld(11,11) +#define Gsctrl_Width(Pixel) /* Display Width [1..2048 pix.] */ \ + (((Pixel) - 1) << FShft(GSCTRL_GSWIDTH)) + +#define GSCTRL_GSHEIGHT Fld(11,0) +#define Gsctrl_Height(Pixel) /* Display Height [1..2048 pix.] */ \ + (((Pixel) - 1) << FShft(GSCTRL_GSHEIGHT)) + +/* GBBASE fileds */ +#define GBBASE_GLALPHA Fld(8,24) +#define Gbbase_Glalpha(x) ((x) << FShft(GBBASE_GLALPHA)) + +#define GBBASE_COLKEY Fld(24,0) +#define Gbbase_Colkey(x) ((x) << FShft(GBBASE_COLKEY)) + +/* GDRCTRL fields */ +#define GDRCTRL_PIXDBL (1 << 31) +#define GDRCTRL_PIXHLV (1 << 30) +#define GDRCTRL_LNDBL (1 << 29) +#define GDRCTRL_LNHLV (1 << 28) +#define GDRCTRL_COLKEYM Fld(24,0) +#define Gdrctrl_Colkeym(x) ((x) << FShft(GDRCTRL_COLKEYM)) + +/* GSCADR graphics stream control address register fields */ +#define GSCADR_STR_EN (1 << 31) +#define GSCADR_COLKEY_EN (1 << 30) +#define GSCADR_COLKEYSCR (1 << 29) +#define GSCADR_BLEND_M Fld(2,27) +#define GSCADR_BLEND_NONE ((0x0) << FShft(GSCADR_BLEND_M)) +#define GSCADR_BLEND_INV ((0x1) << FShft(GSCADR_BLEND_M)) +#define GSCADR_BLEND_GLOB ((0x2) << FShft(GSCADR_BLEND_M)) +#define GSCADR_BLEND_PIX ((0x3) << FShft(GSCADR_BLEND_M)) +#define GSCADR_BLEND_POS Fld(2,24) +#define GSCADR_BLEND_GFX ((0x0) << FShft(GSCADR_BLEND_POS)) +#define GSCADR_BLEND_VID ((0x1) << FShft(GSCADR_BLEND_POS)) +#define GSCADR_BLEND_CUR ((0x2) << FShft(GSCADR_BLEND_POS)) +#define GSCADR_GBASE_ADR Fld(23,0) +#define Gscadr_Gbase_Adr(x) ((x) << FShft(GSCADR_GBASE_ADR)) + +/* GSADR graphics stride address register fields */ +#define GSADR_SRCSTRIDE Fld(10,22) +#define Gsadr_Srcstride(x) ((x) << FShft(GSADR_SRCSTRIDE)) +#define GSADR_XSTART Fld(11,11) +#define Gsadr_Xstart(x) ((x) << FShft(GSADR_XSTART)) +#define GSADR_YSTART Fld(11,0) +#define Gsadr_Ystart(y) ((y) << FShft(GSADR_YSTART)) + +/* GPLUT graphics palette register fields */ +#define GPLUT_LUTADR Fld(8,24) +#define Gplut_Lutadr(x) ((x) << FShft(GPLUT_LUTADR)) +#define GPLUT_LUTDATA Fld(24,0) +#define Gplut_Lutdata(x) ((x) << FShft(GPLUT_LUTDATA)) + +/* HCCTRL - Hardware Cursor Register fields */ +#define HCCTRL_CUR_EN (1 << 31) +#define HCCTRL_COLKEY_EN (1 << 29) +#define HCCTRL_COLKEYSRC (1 << 28) +#define HCCTRL_BLEND_M Fld(2,26) +#define HCCTRL_BLEND_NONE ((0x0) << FShft(HCCTRL_BLEND_M)) +#define HCCTRL_BLEND_INV ((0x1) << FShft(HCCTRL_BLEND_M)) +#define HCCTRL_BLEND_GLOB ((0x2) << FShft(HCCTRL_BLEND_M)) +#define HCCTRL_BLEND_PIX ((0x3) << FShft(HCCTRL_BLEND_M)) +#define HCCTRL_CPIXFMT Fld(3,23) +#define HCCTRL_CPIXFMT_RGB332 ((0x3) << FShft(HCCTRL_CPIXFMT)) +#define HCCTRL_CPIXFMT_ARGB4444 ((0x4) << FShft(HCCTRL_CPIXFMT)) +#define HCCTRL_CPIXFMT_ARGB1555 ((0x5) << FShft(HCCTRL_CPIXFMT)) +#define HCCTRL_CBASE_ADR Fld(23,0) +#define Hcctrl_Cbase_Adr(x) ((x) << FShft(HCCTRL_CBASE_ADR)) + +/* HCSIZE Hardware Cursor Size Register fields */ +#define HCSIZE_BLEND_POS Fld(2,29) +#define HCSIZE_BLEND_GFX ((0x0) << FShft(HCSIZE_BLEND_POS)) +#define HCSIZE_BLEND_VID ((0x1) << FShft(HCSIZE_BLEND_POS)) +#define HCSIZE_BLEND_CUR ((0x2) << FShft(HCSIZE_BLEND_POS)) +#define HCSIZE_CWIDTH Fld(3,16) +#define Hcsize_Cwidth(x) ((x) << FShft(HCSIZE_CWIDTH)) +#define HCSIZE_CHEIGHT Fld(3,0) +#define Hcsize_Cheight(x) ((x) << FShft(HCSIZE_CHEIGHT)) + +/* HCPOS Hardware Cursor Position Register fields */ +#define HCPOS_SWITCHSRC (1 << 30) +#define HCPOS_CURBLINK Fld(6,24) +#define Hcpos_Curblink(x) ((x) << FShft(HCPOS_CURBLINK)) +#define HCPOS_XSTART Fld(12,12) +#define Hcpos_Xstart(x) ((x) << FShft(HCPOS_XSTART)) +#define HCPOS_YSTART Fld(12,0) +#define Hcpos_Ystart(y) ((y) << FShft(HCPOS_YSTART)) + +/* HCBADR Hardware Cursor Blend Address Register */ +#define HCBADR_GLALPHA Fld(8,24) +#define Hcbadr_Glalpha(x) ((x) << FShft(HCBADR_GLALPHA)) +#define HCBADR_COLKEY Fld(24,0) +#define Hcbadr_Colkey(x) ((x) << FShft(HCBADR_COLKEY)) + +/* HCCKMSK - Hardware Cursor Color Key Mask Register */ +#define HCCKMSK_COLKEY_M Fld(24,0) +#define Hcckmsk_Colkey_M(x) ((x) << FShft(HCCKMSK_COLKEY_M)) + +/* DSCTRL - Display sync control register */ +#define DSCTRL_SYNCGEN_EN (1 << 31) +#define DSCTRL_DPL_RST (1 << 29) +#define DSCTRL_PWRDN_M (1 << 28) +#define DSCTRL_UPDSYNCCNT (1 << 26) +#define DSCTRL_UPDINTCNT (1 << 25) +#define DSCTRL_UPDCNT (1 << 24) +#define DSCTRL_UPDWAIT Fld(4,16) +#define Dsctrl_Updwait(x) ((x) << FShft(DSCTRL_UPDWAIT)) +#define DSCTRL_CLKPOL (1 << 11) +#define DSCTRL_CSYNC_EN (1 << 10) +#define DSCTRL_VS_SLAVE (1 << 7) +#define DSCTRL_HS_SLAVE (1 << 6) +#define DSCTRL_BLNK_POL (1 << 5) +#define DSCTRL_BLNK_DIS (1 << 4) +#define DSCTRL_VS_POL (1 << 3) +#define DSCTRL_VS_DIS (1 << 2) +#define DSCTRL_HS_POL (1 << 1) +#define DSCTRL_HS_DIS (1 << 0) + +/* DHT01 - Display horizontal timing register 01 */ +#define DHT01_HBPS Fld(12,16) +#define Dht01_Hbps(x) ((x) << FShft(DHT01_HBPS)) +#define DHT01_HT Fld(12,0) +#define Dht01_Ht(x) ((x) << FShft(DHT01_HT)) + +/* DHT02 - Display horizontal timing register 02 */ +#define DHT02_HAS Fld(12,16) +#define Dht02_Has(x) ((x) << FShft(DHT02_HAS)) +#define DHT02_HLBS Fld(12,0) +#define Dht02_Hlbs(x) ((x) << FShft(DHT02_HLBS)) + +/* DHT03 - Display horizontal timing register 03 */ +#define DHT03_HFPS Fld(12,16) +#define Dht03_Hfps(x) ((x) << FShft(DHT03_HFPS)) +#define DHT03_HRBS Fld(12,0) +#define Dht03_Hrbs(x) ((x) << FShft(DHT03_HRBS)) + +/* DVT01 - Display vertical timing register 01 */ +#define DVT01_VBPS Fld(12,16) +#define Dvt01_Vbps(x) ((x) << FShft(DVT01_VBPS)) +#define DVT01_VT Fld(12,0) +#define Dvt01_Vt(x) ((x) << FShft(DVT01_VT)) + +/* DVT02 - Display vertical timing register 02 */ +#define DVT02_VAS Fld(12,16) +#define Dvt02_Vas(x) ((x) << FShft(DVT02_VAS)) +#define DVT02_VTBS Fld(12,0) +#define Dvt02_Vtbs(x) ((x) << FShft(DVT02_VTBS)) + +/* DVT03 - Display vertical timing register 03 */ +#define DVT03_VFPS Fld(12,16) +#define Dvt03_Vfps(x) ((x) << FShft(DVT03_VFPS)) +#define DVT03_VBBS Fld(12,0) +#define Dvt03_Vbbs(x) ((x) << FShft(DVT03_VBBS)) + +/* DVECTRL - display vertical event control register */ +#define DVECTRL_VEVENT Fld(12,16) +#define Dvectrl_Vevent(x) ((x) << FShft(DVECTRL_VEVENT)) +#define DVECTRL_VFETCH Fld(12,0) +#define Dvectrl_Vfetch(x) ((x) << FShft(DVECTRL_VFETCH)) + +/* DHDET - display horizontal DE timing register */ +#define DHDET_HDES Fld(12,16) +#define Dhdet_Hdes(x) ((x) << FShft(DHDET_HDES)) +#define DHDET_HDEF Fld(12,0) +#define Dhdet_Hdef(x) ((x) << FShft(DHDET_HDEF)) + +/* DVDET - display vertical DE timing register */ +#define DVDET_VDES Fld(12,16) +#define Dvdet_Vdes(x) ((x) << FShft(DVDET_VDES)) +#define DVDET_VDEF Fld(12,0) +#define Dvdet_Vdef(x) ((x) << FShft(DVDET_VDEF)) + +/* DODMSK - display output data mask register */ +#define DODMSK_MASK_LVL (1 << 31) +#define DODMSK_BLNK_LVL (1 << 30) +#define DODMSK_MASK_B Fld(8,16) +#define Dodmsk_Mask_B(x) ((x) << FShft(DODMSK_MASK_B)) +#define DODMSK_MASK_G Fld(8,8) +#define Dodmsk_Mask_G(x) ((x) << FShft(DODMSK_MASK_G)) +#define DODMSK_MASK_R Fld(8,0) +#define Dodmsk_Mask_R(x) ((x) << FShft(DODMSK_MASK_R)) + +/* DBCOL - display border color control register */ +#define DBCOL_BORDCOL Fld(24,0) +#define Dbcol_Bordcol(x) ((x) << FShft(DBCOL_BORDCOL)) + +/* DVLNUM - display vertical line number register */ +#define DVLNUM_VLINE Fld(12,0) +#define Dvlnum_Vline(x) ((x) << FShft(DVLNUM_VLINE)) + +/* DMCTRL - Display Memory Control Register */ +#define DMCTRL_MEM_REF Fld(2,30) +#define DMCTRL_MEM_REF_ACT ((0x0) << FShft(DMCTRL_MEM_REF)) +#define DMCTRL_MEM_REF_HB ((0x1) << FShft(DMCTRL_MEM_REF)) +#define DMCTRL_MEM_REF_VB ((0x2) << FShft(DMCTRL_MEM_REF)) +#define DMCTRL_MEM_REF_BOTH ((0x3) << FShft(DMCTRL_MEM_REF)) +#define DMCTRL_UV_THRHLD Fld(6,24) +#define Dmctrl_Uv_Thrhld(x) ((x) << FShft(DMCTRL_UV_THRHLD)) +#define DMCTRL_V_THRHLD Fld(7,16) +#define Dmctrl_V_Thrhld(x) ((x) << FShft(DMCTRL_V_THRHLD)) +#define DMCTRL_D_THRHLD Fld(7,8) +#define Dmctrl_D_Thrhld(x) ((x) << FShft(DMCTRL_D_THRHLD)) +#define DMCTRL_BURSTLEN Fld(6,0) +#define Dmctrl_Burstlen(x) ((x) << FShft(DMCTRL_BURSTLEN)) + + +/* DLSTS - display load status register */ +#define DLSTS_RLD_ADONE (1 << 23) +/* #define DLSTS_RLD_ADOUT Fld(23,0) */ + +/* DLLCTRL - display list load control register */ +#define DLLCTRL_RLD_ADRLN Fld(8,24) +#define Dllctrl_Rld_Adrln(x) ((x) << FShft(DLLCTRL_RLD_ADRLN)) + +/* SPOCTRL - Scale Pitch/Order Control Register */ +#define SPOCTRL_H_SC_BP (1 << 31) +#define SPOCTRL_V_SC_BP (1 << 30) +#define SPOCTRL_HV_SC_OR (1 << 29) +#define SPOCTRL_VS_UR_C (1 << 27) +#define SPOCTRL_VORDER Fld(2,16) +#define SPOCTRL_VORDER_1TAP ((0x0) << FShft(SPOCTRL_VORDER)) +#define SPOCTRL_VORDER_2TAP ((0x1) << FShft(SPOCTRL_VORDER)) +#define SPOCTRL_VORDER_4TAP ((0x3) << FShft(SPOCTRL_VORDER)) +#define SPOCTRL_VPITCH Fld(16,0) +#define Spoctrl_Vpitch(x) ((x) << FShft(SPOCTRL_VPITCH)) + +#endif /* __REG_BITS_2700G_ */ diff --git a/drivers/video/mbx/regs.h b/drivers/video/mbx/regs.h new file mode 100644 index 00000000000..ad20be07666 --- /dev/null +++ b/drivers/video/mbx/regs.h @@ -0,0 +1,195 @@ +#ifndef __REGS_2700G_ +#define __REGS_2700G_ + +/* extern unsigned long virt_base_2700; */ +/* #define __REG_2700G(x) (*(volatile unsigned long*)((x)+virt_base_2700)) */ +#define __REG_2700G(x) ((x)+virt_base_2700) + +/* System Configuration Registers (0x0000_0000 0x0000_0010) */ +#define SYSCFG __REG_2700G(0x00000000) +#define PFBASE __REG_2700G(0x00000004) +#define PFCEIL __REG_2700G(0x00000008) +#define POLLFLAG __REG_2700G(0x0000000c) +#define SYSRST __REG_2700G(0x00000010) + +/* Interrupt Control Registers (0x0000_0014 0x0000_002F) */ +#define NINTPW __REG_2700G(0x00000014) +#define MINTENABLE __REG_2700G(0x00000018) +#define MINTSTAT __REG_2700G(0x0000001c) +#define SINTENABLE __REG_2700G(0x00000020) +#define SINTSTAT __REG_2700G(0x00000024) +#define SINTCLR __REG_2700G(0x00000028) + +/* Clock Control Registers (0x0000_002C 0x0000_005F) */ +#define SYSCLKSRC __REG_2700G(0x0000002c) +#define PIXCLKSRC __REG_2700G(0x00000030) +#define CLKSLEEP __REG_2700G(0x00000034) +#define COREPLL __REG_2700G(0x00000038) +#define DISPPLL __REG_2700G(0x0000003c) +#define PLLSTAT __REG_2700G(0x00000040) +#define VOVRCLK __REG_2700G(0x00000044) +#define PIXCLK __REG_2700G(0x00000048) +#define MEMCLK __REG_2700G(0x0000004c) +#define M24CLK __REG_2700G(0x00000054) +#define MBXCLK __REG_2700G(0x00000054) +#define SDCLK __REG_2700G(0x00000058) +#define PIXCLKDIV __REG_2700G(0x0000005c) + +/* LCD Port Control Register (0x0000_0060 0x0000_006F) */ +#define LCD_CONFIG __REG_2700G(0x00000060) + +/* On-Die Frame Buffer Registers (0x0000_0064 0x0000_006B) */ +#define ODFBPWR __REG_2700G(0x00000064) +#define ODFBSTAT __REG_2700G(0x00000068) + +/* GPIO Registers (0x0000_006C 0x0000_007F) */ +#define GPIOCGF __REG_2700G(0x0000006c) +#define GPIOHI __REG_2700G(0x00000070) +#define GPIOLO __REG_2700G(0x00000074) +#define GPIOSTAT __REG_2700G(0x00000078) + +/* Pulse Width Modulator (PWM) Registers (0x0000_0200 0x0000_02FF) */ +#define PWMRST __REG_2700G(0x00000200) +#define PWMCFG __REG_2700G(0x00000204) +#define PWM0DIV __REG_2700G(0x00000210) +#define PWM0DUTY __REG_2700G(0x00000214) +#define PWM0PER __REG_2700G(0x00000218) +#define PWM1DIV __REG_2700G(0x00000220) +#define PWM1DUTY __REG_2700G(0x00000224) +#define PWM1PER __REG_2700G(0x00000228) + +/* Identification (ID) Registers (0x0000_0300 0x0000_0FFF) */ +#define ID __REG_2700G(0x00000FF0) + +/* Local Memory (SDRAM) Interface Registers (0x0000_1000 0x0000_1FFF) */ +#define LMRST __REG_2700G(0x00001000) +#define LMCFG __REG_2700G(0x00001004) +#define LMPWR __REG_2700G(0x00001008) +#define LMPWRSTAT __REG_2700G(0x0000100c) +#define LMCEMR __REG_2700G(0x00001010) +#define LMTYPE __REG_2700G(0x00001014) +#define LMTIM __REG_2700G(0x00001018) +#define LMREFRESH __REG_2700G(0x0000101c) +#define LMPROTMIN __REG_2700G(0x00001020) +#define LMPROTMAX __REG_2700G(0x00001024) +#define LMPROTCFG __REG_2700G(0x00001028) +#define LMPROTERR __REG_2700G(0x0000102c) + +/* Plane Controller Registers (0x0000_2000 0x0000_2FFF) */ +#define GSCTRL __REG_2700G(0x00002000) +#define VSCTRL __REG_2700G(0x00002004) +#define GBBASE __REG_2700G(0x00002020) +#define VBBASE __REG_2700G(0x00002024) +#define GDRCTRL __REG_2700G(0x00002040) +#define VCMSK __REG_2700G(0x00002044) +#define GSCADR __REG_2700G(0x00002060) +#define VSCADR __REG_2700G(0x00002064) +#define VUBASE __REG_2700G(0x00002084) +#define VVBASE __REG_2700G(0x000020a4) +#define GSADR __REG_2700G(0x000020c0) +#define VSADR __REG_2700G(0x000020c4) +#define HCCTRL __REG_2700G(0x00002100) +#define HCSIZE __REG_2700G(0x00002110) +#define HCPOS __REG_2700G(0x00002120) +#define HCBADR __REG_2700G(0x00002130) +#define HCCKMSK __REG_2700G(0x00002140) +#define GPLUT __REG_2700G(0x00002150) +#define DSCTRL __REG_2700G(0x00002154) +#define DHT01 __REG_2700G(0x00002158) +#define DHT02 __REG_2700G(0x0000215c) +#define DHT03 __REG_2700G(0x00002160) +#define DVT01 __REG_2700G(0x00002164) +#define DVT02 __REG_2700G(0x00002168) +#define DVT03 __REG_2700G(0x0000216c) +#define DBCOL __REG_2700G(0x00002170) +#define BGCOLOR __REG_2700G(0x00002174) +#define DINTRS __REG_2700G(0x00002178) +#define DINTRE __REG_2700G(0x0000217c) +#define DINTRCNT __REG_2700G(0x00002180) +#define DSIG __REG_2700G(0x00002184) +#define DMCTRL __REG_2700G(0x00002188) +#define CLIPCTRL __REG_2700G(0x0000218c) +#define SPOCTRL __REG_2700G(0x00002190) +#define SVCTRL __REG_2700G(0x00002194) + +/* 0x0000_2198 */ +/* 0x0000_21A8 VSCOEFF[0:4] Video Scalar Vertical Coefficient [0:4] 4.14.5 */ +#define VSCOEFF0 __REG_2700G(0x00002198) +#define VSCOEFF1 __REG_2700G(0x0000219c) +#define VSCOEFF2 __REG_2700G(0x000021a0) +#define VSCOEFF3 __REG_2700G(0x000021a4) +#define VSCOEFF4 __REG_2700G(0x000021a8) + +#define SHCTRL __REG_2700G(0x000021b0) + +/* 0x0000_21B4 */ +/* 0x0000_21D4 HSCOEFF[0:8] Video Scalar Horizontal Coefficient [0:8] 4.14.7 */ +#define HSCOEFF0 __REG_2700G(0x000021b4) +#define HSCOEFF1 __REG_2700G(0x000021b8) +#define HSCOEFF2 __REG_2700G(0x000021bc) +#define HSCOEFF3 __REG_2700G(0x000021b0) +#define HSCOEFF4 __REG_2700G(0x000021c4) +#define HSCOEFF5 __REG_2700G(0x000021c8) +#define HSCOEFF6 __REG_2700G(0x000021cc) +#define HSCOEFF7 __REG_2700G(0x000021d0) +#define HSCOEFF8 __REG_2700G(0x000021d4) + +#define SSSIZE __REG_2700G(0x000021D8) + +/* 0x0000_2200 */ +/* 0x0000_2240 VIDGAM[0:16] Video Gamma LUT Index [0:16] 4.15.2 */ +#define VIDGAM0 __REG_2700G(0x00002200) +#define VIDGAM1 __REG_2700G(0x00002204) +#define VIDGAM2 __REG_2700G(0x00002208) +#define VIDGAM3 __REG_2700G(0x0000220c) +#define VIDGAM4 __REG_2700G(0x00002210) +#define VIDGAM5 __REG_2700G(0x00002214) +#define VIDGAM6 __REG_2700G(0x00002218) +#define VIDGAM7 __REG_2700G(0x0000221c) +#define VIDGAM8 __REG_2700G(0x00002220) +#define VIDGAM9 __REG_2700G(0x00002224) +#define VIDGAM10 __REG_2700G(0x00002228) +#define VIDGAM11 __REG_2700G(0x0000222c) +#define VIDGAM12 __REG_2700G(0x00002230) +#define VIDGAM13 __REG_2700G(0x00002234) +#define VIDGAM14 __REG_2700G(0x00002238) +#define VIDGAM15 __REG_2700G(0x0000223c) +#define VIDGAM16 __REG_2700G(0x00002240) + +/* 0x0000_2250 */ +/* 0x0000_2290 GFXGAM[0:16] Graphics Gamma LUT Index [0:16] 4.15.3 */ +#define GFXGAM0 __REG_2700G(0x00002250) +#define GFXGAM1 __REG_2700G(0x00002254) +#define GFXGAM2 __REG_2700G(0x00002258) +#define GFXGAM3 __REG_2700G(0x0000225c) +#define GFXGAM4 __REG_2700G(0x00002260) +#define GFXGAM5 __REG_2700G(0x00002264) +#define GFXGAM6 __REG_2700G(0x00002268) +#define GFXGAM7 __REG_2700G(0x0000226c) +#define GFXGAM8 __REG_2700G(0x00002270) +#define GFXGAM9 __REG_2700G(0x00002274) +#define GFXGAM10 __REG_2700G(0x00002278) +#define GFXGAM11 __REG_2700G(0x0000227c) +#define GFXGAM12 __REG_2700G(0x00002280) +#define GFXGAM13 __REG_2700G(0x00002284) +#define GFXGAM14 __REG_2700G(0x00002288) +#define GFXGAM15 __REG_2700G(0x0000228c) +#define GFXGAM16 __REG_2700G(0x00002290) + +#define DLSTS __REG_2700G(0x00002300) +#define DLLCTRL __REG_2700G(0x00002304) +#define DVLNUM __REG_2700G(0x00002308) +#define DUCTRL __REG_2700G(0x0000230c) +#define DVECTRL __REG_2700G(0x00002310) +#define DHDET __REG_2700G(0x00002314) +#define DVDET __REG_2700G(0x00002318) +#define DODMSK __REG_2700G(0x0000231c) +#define CSC01 __REG_2700G(0x00002330) +#define CSC02 __REG_2700G(0x00002334) +#define CSC03 __REG_2700G(0x00002338) +#define CSC04 __REG_2700G(0x0000233c) +#define CSC05 __REG_2700G(0x00002340) + +#define FB_MEMORY_START __REG_2700G(0x00060000) + +#endif /* __REGS_2700G_ */ diff --git a/fs/char_dev.c b/fs/char_dev.c index a4cbc6706ef..3483d3cf808 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -182,6 +182,28 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, return 0; } +/** + * register_chrdev() - Register a major number for character devices. + * @major: major device number or 0 for dynamic allocation + * @name: name of this range of devices + * @fops: file operations associated with this devices + * + * If @major == 0 this functions will dynamically allocate a major and return + * its number. + * + * If @major > 0 this function will attempt to reserve a device with the given + * major number and will return zero on success. + * + * Returns a -ve errno on failure. + * + * The name of this device has nothing to do with the name of the device in + * /dev. It only helps to keep track of the different owners of devices. If + * your module name has only one type of devices it's ok to use e.g. the name + * of the module here. + * + * This function registers a range of 256 minor numbers. The first minor number + * is 0. + */ int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) { diff --git a/fs/namei.c b/fs/namei.c index c9750d755af..e01070d7bf5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1712,8 +1712,14 @@ do_link: if (error) goto exit_dput; error = __do_follow_link(&path, nd); - if (error) + if (error) { + /* Does someone understand code flow here? Or it is only + * me so stupid? Anathema to whoever designed this non-sense + * with "intent.open". + */ + release_open_intent(nd); return error; + } nd->flags &= ~LOOKUP_PARENT; if (nd->last_type == LAST_BIND) goto ok; diff --git a/fs/proc/array.c b/fs/proc/array.c index 7495d3e2077..0b615d62a15 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -74,6 +74,7 @@ #include <linux/times.h> #include <linux/cpuset.h> #include <linux/rcupdate.h> +#include <linux/delayacct.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -411,7 +412,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ %lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ -%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu\n", +%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n", task->pid, tcomm, state, @@ -455,7 +456,8 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) task->exit_signal, task_cpu(task), task->rt_priority, - task->policy); + task->policy, + (unsigned long long)delayacct_blkio_ticks(task)); if(mm) mmput(mm); return res; diff --git a/fs/proc/base.c b/fs/proc/base.c index 0cb8f20d000..474eae34506 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1338,8 +1338,8 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) } else { inode->i_uid = 0; inode->i_gid = 0; - inode->i_mode = 0; } + inode->i_mode &= ~(S_ISUID | S_ISGID); security_task_to_inode(task, inode); put_task_struct(task); return 1; @@ -1390,6 +1390,7 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) inode->i_uid = 0; inode->i_gid = 0; } + inode->i_mode &= ~(S_ISUID | S_ISGID); security_task_to_inode(task, inode); put_task_struct(task); return 1; diff --git a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c index 5d8a8cfebc7..c533ec1bcae 100644 --- a/fs/reiserfs/procfs.c +++ b/fs/reiserfs/procfs.c @@ -492,9 +492,17 @@ static void add_file(struct super_block *sb, char *name, int reiserfs_proc_info_init(struct super_block *sb) { + char b[BDEVNAME_SIZE]; + char *s; + + /* Some block devices use /'s */ + strlcpy(b, reiserfs_bdevname(sb), BDEVNAME_SIZE); + s = strchr(b, '/'); + if (s) + *s = '!'; + spin_lock_init(&__PINFO(sb).lock); - REISERFS_SB(sb)->procdir = - proc_mkdir(reiserfs_bdevname(sb), proc_info_root); + REISERFS_SB(sb)->procdir = proc_mkdir(b, proc_info_root); if (REISERFS_SB(sb)->procdir) { REISERFS_SB(sb)->procdir->owner = THIS_MODULE; REISERFS_SB(sb)->procdir->data = sb; @@ -508,13 +516,22 @@ int reiserfs_proc_info_init(struct super_block *sb) return 0; } reiserfs_warning(sb, "reiserfs: cannot create /proc/%s/%s", - proc_info_root_name, reiserfs_bdevname(sb)); + proc_info_root_name, b); return 1; } int reiserfs_proc_info_done(struct super_block *sb) { struct proc_dir_entry *de = REISERFS_SB(sb)->procdir; + char b[BDEVNAME_SIZE]; + char *s; + + /* Some block devices use /'s */ + strlcpy(b, reiserfs_bdevname(sb), BDEVNAME_SIZE); + s = strchr(b, '/'); + if (s) + *s = '!'; + if (de) { remove_proc_entry("journal", de); remove_proc_entry("oidmap", de); @@ -528,7 +545,7 @@ int reiserfs_proc_info_done(struct super_block *sb) __PINFO(sb).exiting = 1; spin_unlock(&__PINFO(sb).lock); if (proc_info_root) { - remove_proc_entry(reiserfs_bdevname(sb), proc_info_root); + remove_proc_entry(b, proc_info_root); REISERFS_SB(sb)->procdir = NULL; } return 0; diff --git a/include/asm-alpha/barrier.h b/include/asm-alpha/barrier.h index 681ff581afa..384dc08d6f5 100644 --- a/include/asm-alpha/barrier.h +++ b/include/asm-alpha/barrier.h @@ -30,7 +30,4 @@ __asm__ __volatile__("mb": : :"memory") #define set_mb(var, value) \ do { var = value; mb(); } while (0) -#define set_wmb(var, value) \ -do { var = value; wmb(); } while (0) - #endif /* __BARRIER_H */ diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h index 6001febfe63..0947cbf9b69 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -176,7 +176,6 @@ extern unsigned int user_debug; #define wmb() mb() #define read_barrier_depends() do { } while(0) #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t"); /* diff --git a/include/asm-arm26/system.h b/include/asm-arm26/system.h index d1f69d70619..00ae32aa1db 100644 --- a/include/asm-arm26/system.h +++ b/include/asm-arm26/system.h @@ -90,7 +90,6 @@ extern unsigned int user_debug; #define read_barrier_depends() do { } while(0) #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) /* * We assume knowledge of how diff --git a/include/asm-cris/system.h b/include/asm-cris/system.h index b1c593b6dbf..b869f6161aa 100644 --- a/include/asm-cris/system.h +++ b/include/asm-cris/system.h @@ -17,7 +17,6 @@ extern struct task_struct *resume(struct task_struct *prev, struct task_struct * #define wmb() mb() #define read_barrier_depends() do { } while(0) #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #ifdef CONFIG_SMP #define smp_mb() mb() diff --git a/include/asm-frv/system.h b/include/asm-frv/system.h index 351863dfd06..1166899317d 100644 --- a/include/asm-frv/system.h +++ b/include/asm-frv/system.h @@ -179,7 +179,6 @@ do { \ #define rmb() asm volatile ("membar" : : :"memory") #define wmb() asm volatile ("membar" : : :"memory") #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #define smp_mb() mb() #define smp_rmb() rmb() diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm index d8d0bcecd23..6b16dda1811 100644 --- a/include/asm-generic/Kbuild.asm +++ b/include/asm-generic/Kbuild.asm @@ -1,11 +1,8 @@ unifdef-y += a.out.h auxvec.h byteorder.h errno.h fcntl.h ioctl.h \ - ioctls.h ipcbuf.h irq.h mman.h msgbuf.h param.h poll.h \ + ioctls.h ipcbuf.h mman.h msgbuf.h param.h poll.h \ posix_types.h ptrace.h resource.h sembuf.h shmbuf.h shmparam.h \ sigcontext.h siginfo.h signal.h socket.h sockios.h stat.h \ statfs.h termbits.h termios.h timex.h types.h unistd.h user.h -# These really shouldn't be exported -unifdef-y += atomic.h io.h - # These probably shouldn't be exported unifdef-y += elf.h page.h diff --git a/include/asm-h8300/system.h b/include/asm-h8300/system.h index 134e0929fce..5084a9d4292 100644 --- a/include/asm-h8300/system.h +++ b/include/asm-h8300/system.h @@ -84,7 +84,6 @@ asmlinkage void resume(void); #define wmb() asm volatile ("" : : :"memory") #define set_rmb(var, value) do { xchg(&var, value); } while (0) #define set_mb(var, value) set_rmb(var, value) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #ifdef CONFIG_SMP #define smp_mb() mb() diff --git a/include/asm-i386/system.h b/include/asm-i386/system.h index 2db168ef949..49928eb33f8 100644 --- a/include/asm-i386/system.h +++ b/include/asm-i386/system.h @@ -453,8 +453,6 @@ static inline unsigned long long __cmpxchg64(volatile void *ptr, unsigned long l #define set_mb(var, value) do { var = value; barrier(); } while (0) #endif -#define set_wmb(var, value) do { var = value; wmb(); } while (0) - #include <linux/irqflags.h> /* diff --git a/include/asm-ia64/system.h b/include/asm-ia64/system.h index 65db43ce4de..fc9677bc87e 100644 --- a/include/asm-ia64/system.h +++ b/include/asm-ia64/system.h @@ -98,12 +98,11 @@ extern struct ia64_boot_param { #endif /* - * XXX check on these---I suspect what Linus really wants here is + * XXX check on this ---I suspect what Linus really wants here is * acquire vs release semantics but we can't discuss this stuff with * Linus just yet. Grrr... */ #define set_mb(var, value) do { (var) = (value); mb(); } while (0) -#define set_wmb(var, value) do { (var) = (value); mb(); } while (0) #define safe_halt() ia64_pal_halt_light() /* PAL_HALT_LIGHT */ diff --git a/include/asm-m32r/system.h b/include/asm-m32r/system.h index 311cebf44ef..9e618afec6e 100644 --- a/include/asm-m32r/system.h +++ b/include/asm-m32r/system.h @@ -336,7 +336,6 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) #endif #define set_mb(var, value) do { xchg(&var, value); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #define arch_align_stack(x) (x) diff --git a/include/asm-m68k/system.h b/include/asm-m68k/system.h index d6dd8052cd6..131a0cb0f49 100644 --- a/include/asm-m68k/system.h +++ b/include/asm-m68k/system.h @@ -80,7 +80,6 @@ static inline int irqs_disabled(void) #define wmb() barrier() #define read_barrier_depends() do { } while(0) #define set_mb(var, value) do { xchg(&var, value); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #define smp_mb() barrier() #define smp_rmb() barrier() diff --git a/include/asm-m68knommu/system.h b/include/asm-m68knommu/system.h index 2bbe2db00a2..2a814498672 100644 --- a/include/asm-m68knommu/system.h +++ b/include/asm-m68knommu/system.h @@ -106,7 +106,6 @@ asmlinkage void resume(void); #define wmb() asm volatile ("" : : :"memory") #define set_rmb(var, value) do { xchg(&var, value); } while (0) #define set_mb(var, value) set_rmb(var, value) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #ifdef CONFIG_SMP #define smp_mb() mb() diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h index 13c98dde82d..dcb4701d572 100644 --- a/include/asm-mips/system.h +++ b/include/asm-mips/system.h @@ -143,9 +143,6 @@ #define set_mb(var, value) \ do { var = value; mb(); } while (0) -#define set_wmb(var, value) \ -do { var = value; wmb(); } while (0) - /* * switch_to(n) should switch tasks to task nr n, first * checking that n isn't the current task, in which case it does nothing. diff --git a/include/asm-parisc/system.h b/include/asm-parisc/system.h index 5fe2d2329ab..74f037a39e6 100644 --- a/include/asm-parisc/system.h +++ b/include/asm-parisc/system.h @@ -143,8 +143,6 @@ static inline void set_eiem(unsigned long val) #define read_barrier_depends() do { } while(0) #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) - #ifndef CONFIG_PA20 /* Because kmalloc only guarantees 8-byte alignment for kmalloc'd data, diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h index c6569516ba3..7307aa77567 100644 --- a/include/asm-powerpc/system.h +++ b/include/asm-powerpc/system.h @@ -39,7 +39,6 @@ #define read_barrier_depends() do { } while(0) #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #ifdef __KERNEL__ #ifdef CONFIG_SMP diff --git a/include/asm-ppc/system.h b/include/asm-ppc/system.h index fb49c0c49ea..738943584c0 100644 --- a/include/asm-ppc/system.h +++ b/include/asm-ppc/system.h @@ -33,7 +33,6 @@ #define read_barrier_depends() do { } while(0) #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #ifdef CONFIG_SMP #define smp_mb() mb() diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 9ab186ffde2..36a3a85d611 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -299,7 +299,6 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #ifdef __s390x__ diff --git a/include/asm-sh/system.h b/include/asm-sh/system.h index ce2e60664a8..ad35ad4958f 100644 --- a/include/asm-sh/system.h +++ b/include/asm-sh/system.h @@ -101,7 +101,6 @@ extern void __xchg_called_with_bad_pointer(void); #endif #define set_mb(var, value) do { xchg(&var, value); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) /* Interrupt Control */ static __inline__ void local_irq_enable(void) diff --git a/include/asm-sh64/system.h b/include/asm-sh64/system.h index 7606f6e1f01..87ef6f1ad5a 100644 --- a/include/asm-sh64/system.h +++ b/include/asm-sh64/system.h @@ -66,7 +66,6 @@ extern void __xchg_called_with_bad_pointer(void); #define set_rmb(var, value) do { xchg(&var, value); } while (0) #define set_mb(var, value) set_rmb(var, value) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) /* Interrupt Control */ #ifndef HARD_CLI diff --git a/include/asm-sparc/system.h b/include/asm-sparc/system.h index cb7dda1e5e9..100c3eaf3c1 100644 --- a/include/asm-sparc/system.h +++ b/include/asm-sparc/system.h @@ -199,7 +199,6 @@ static inline unsigned long getipl(void) #define wmb() mb() #define read_barrier_depends() do { } while(0) #define set_mb(__var, __value) do { __var = __value; mb(); } while(0) -#define set_wmb(__var, __value) set_mb(__var, __value) #define smp_mb() __asm__ __volatile__("":::"memory") #define smp_rmb() __asm__ __volatile__("":::"memory") #define smp_wmb() __asm__ __volatile__("":::"memory") diff --git a/include/asm-sparc64/system.h b/include/asm-sparc64/system.h index 4ca68600c67..a8b7432c9a7 100644 --- a/include/asm-sparc64/system.h +++ b/include/asm-sparc64/system.h @@ -123,8 +123,6 @@ do { __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" \ #define read_barrier_depends() do { } while(0) #define set_mb(__var, __value) \ do { __var = __value; membar_storeload_storestore(); } while(0) -#define set_wmb(__var, __value) \ - do { __var = __value; wmb(); } while(0) #ifdef CONFIG_SMP #define smp_mb() mb() diff --git a/include/asm-v850/system.h b/include/asm-v850/system.h index 7091af4b786..da39916f10b 100644 --- a/include/asm-v850/system.h +++ b/include/asm-v850/system.h @@ -68,7 +68,6 @@ static inline int irqs_disabled (void) #define read_barrier_depends() ((void)0) #define set_rmb(var, value) do { xchg (&var, value); } while (0) #define set_mb(var, value) set_rmb (var, value) -#define set_wmb(var, value) do { var = value; wmb (); } while (0) #define smp_mb() mb () #define smp_rmb() rmb () diff --git a/include/asm-x86_64/system.h b/include/asm-x86_64/system.h index f67f2873a92..6bf170bceae 100644 --- a/include/asm-x86_64/system.h +++ b/include/asm-x86_64/system.h @@ -240,7 +240,6 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, #endif #define read_barrier_depends() do {} while(0) #define set_mb(var, value) do { (void) xchg(&var, value); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #define warn_if_not_ulong(x) do { unsigned long foo; (void) (&(x) == &foo); } while (0) diff --git a/include/asm-xtensa/system.h b/include/asm-xtensa/system.h index f986170bd2a..932bda92a21 100644 --- a/include/asm-xtensa/system.h +++ b/include/asm-xtensa/system.h @@ -99,7 +99,6 @@ static inline void disable_coprocessor(int i) #endif #define set_mb(var, value) do { var = value; mb(); } while (0) -#define set_wmb(var, value) do { var = value; wmb(); } while (0) #if !defined (__ASSEMBLY__) diff --git a/include/linux/delayacct.h b/include/linux/delayacct.h new file mode 100644 index 00000000000..7e8b6011b8f --- /dev/null +++ b/include/linux/delayacct.h @@ -0,0 +1,119 @@ +/* delayacct.h - per-task delay accounting + * + * Copyright (C) Shailabh Nagar, IBM Corp. 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + */ + +#ifndef _LINUX_DELAYACCT_H +#define _LINUX_DELAYACCT_H + +#include <linux/sched.h> +#include <linux/taskstats_kern.h> + +/* + * Per-task flags relevant to delay accounting + * maintained privately to avoid exhausting similar flags in sched.h:PF_* + * Used to set current->delays->flags + */ +#define DELAYACCT_PF_SWAPIN 0x00000001 /* I am doing a swapin */ + +#ifdef CONFIG_TASK_DELAY_ACCT + +extern int delayacct_on; /* Delay accounting turned on/off */ +extern kmem_cache_t *delayacct_cache; +extern void delayacct_init(void); +extern void __delayacct_tsk_init(struct task_struct *); +extern void __delayacct_tsk_exit(struct task_struct *); +extern void __delayacct_blkio_start(void); +extern void __delayacct_blkio_end(void); +extern int __delayacct_add_tsk(struct taskstats *, struct task_struct *); +extern __u64 __delayacct_blkio_ticks(struct task_struct *); + +static inline void delayacct_set_flag(int flag) +{ + if (current->delays) + current->delays->flags |= flag; +} + +static inline void delayacct_clear_flag(int flag) +{ + if (current->delays) + current->delays->flags &= ~flag; +} + +static inline void delayacct_tsk_init(struct task_struct *tsk) +{ + /* reinitialize in case parent's non-null pointer was dup'ed*/ + tsk->delays = NULL; + if (unlikely(delayacct_on)) + __delayacct_tsk_init(tsk); +} + +static inline void delayacct_tsk_exit(struct task_struct *tsk) +{ + if (tsk->delays) + __delayacct_tsk_exit(tsk); +} + +static inline void delayacct_blkio_start(void) +{ + if (current->delays) + __delayacct_blkio_start(); +} + +static inline void delayacct_blkio_end(void) +{ + if (current->delays) + __delayacct_blkio_end(); +} + +static inline int delayacct_add_tsk(struct taskstats *d, + struct task_struct *tsk) +{ + if (likely(!delayacct_on)) + return -EINVAL; + if (!tsk->delays) + return 0; + return __delayacct_add_tsk(d, tsk); +} + +static inline __u64 delayacct_blkio_ticks(struct task_struct *tsk) +{ + if (tsk->delays) + return __delayacct_blkio_ticks(tsk); + return 0; +} + +#else +static inline void delayacct_set_flag(int flag) +{} +static inline void delayacct_clear_flag(int flag) +{} +static inline void delayacct_init(void) +{} +static inline void delayacct_tsk_init(struct task_struct *tsk) +{} +static inline void delayacct_tsk_exit(struct task_struct *tsk) +{} +static inline void delayacct_blkio_start(void) +{} +static inline void delayacct_blkio_end(void) +{} +static inline int delayacct_add_tsk(struct taskstats *d, + struct task_struct *tsk) +{ return 0; } +static inline __u64 delayacct_blkio_ticks(struct task_struct *tsk) +{ return 0; } +#endif /* CONFIG_TASK_DELAY_ACCT */ + +#endif diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 21338bb3441..9418519a55d 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -115,6 +115,7 @@ #define I2C_DRIVERID_BT866 85 /* Conexant bt866 video encoder */ #define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */ #define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */ +#define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */ #define I2C_DRIVERID_I2CDEV 900 #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index eef0876d830..383627ad328 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -23,8 +23,8 @@ struct vlan_collection; struct vlan_dev_info; struct hlist_node; -#include <linux/proc_fs.h> /* for proc_dir_entry */ #include <linux/netdevice.h> +#include <linux/etherdevice.h> #define VLAN_HLEN 4 /* The additional bytes (on top of the Ethernet header) * that VLAN requires. @@ -185,7 +185,8 @@ static inline int __vlan_hwaccel_rx(struct sk_buff *skb, * This allows the VLAN to have a different MAC than the underlying * device, and still route correctly. */ - if (!memcmp(eth_hdr(skb)->h_dest, skb->dev->dev_addr, ETH_ALEN)) + if (!compare_ether_addr(eth_hdr(skb)->h_dest, + skb->dev->dev_addr)) skb->pkt_type = PACKET_HOST; break; }; diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 7cce5dfa092..1c65e7a9f18 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -28,7 +28,6 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), void kthread_bind(struct task_struct *k, unsigned int cpu); int kthread_stop(struct task_struct *k); -int kthread_stop_sem(struct task_struct *k, struct semaphore *s); int kthread_should_stop(void); #endif /* _LINUX_KTHREAD_H */ diff --git a/include/linux/list.h b/include/linux/list.h index 6b74adf5297..65a5b5ceda4 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -265,6 +265,17 @@ static inline void list_move_tail(struct list_head *list, } /** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** * list_empty - tests whether a list is empty * @head: the list to test. */ diff --git a/include/linux/module.h b/include/linux/module.h index d06c74fb8c2..0dfb794c52d 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -362,10 +362,8 @@ int is_module_address(unsigned long addr); /* Returns module and fills in value, defined and namebuf, or NULL if symnum out of range. */ -struct module *module_get_kallsym(unsigned int symnum, - unsigned long *value, - char *type, - char namebuf[128]); +struct module *module_get_kallsym(unsigned int symnum, unsigned long *value, + char *type, char *name, size_t namelen); /* Look for this name: can be of form module:name. */ unsigned long module_kallsyms_lookup_name(const char *name); @@ -535,8 +533,8 @@ static inline const char *module_address_lookup(unsigned long addr, static inline struct module *module_get_kallsym(unsigned int symnum, unsigned long *value, - char *type, - char namebuf[128]) + char *type, char *name, + size_t namelen) { return NULL; } diff --git a/include/linux/namei.h b/include/linux/namei.h index 58cb3d3d44b..45511a5918d 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -11,7 +11,7 @@ struct open_intent { struct file *file; }; -enum { MAX_NESTED_LINKS = 5 }; +enum { MAX_NESTED_LINKS = 8 }; struct nameidata { struct dentry *dentry; diff --git a/include/linux/nsc_gpio.h b/include/linux/nsc_gpio.h index 135742cfada..7da0cf3702e 100644 --- a/include/linux/nsc_gpio.h +++ b/include/linux/nsc_gpio.h @@ -25,8 +25,6 @@ struct nsc_gpio_ops { void (*gpio_dump) (struct nsc_gpio_ops *amp, unsigned iminor); int (*gpio_get) (unsigned iminor); void (*gpio_set) (unsigned iminor, int state); - void (*gpio_set_high)(unsigned iminor); - void (*gpio_set_low) (unsigned iminor); void (*gpio_change) (unsigned iminor); int (*gpio_current) (unsigned iminor); struct device* dev; /* for dev_dbg() support, set in init */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 1c876e27ff9..6afa72e080c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -463,6 +463,10 @@ struct signal_struct { #ifdef CONFIG_BSD_PROCESS_ACCT struct pacct_struct pacct; /* per-process accounting information */ #endif +#ifdef CONFIG_TASKSTATS + spinlock_t stats_lock; + struct taskstats *stats; +#endif }; /* Context switch must be unlocked if interrupts are to be enabled */ @@ -537,7 +541,7 @@ extern struct user_struct root_user; struct backing_dev_info; struct reclaim_state; -#ifdef CONFIG_SCHEDSTATS +#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) struct sched_info { /* cumulative counters */ unsigned long cpu_time, /* time spent on the cpu */ @@ -548,9 +552,53 @@ struct sched_info { unsigned long last_arrival, /* when we last ran on a cpu */ last_queued; /* when we were last queued to run */ }; +#endif /* defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) */ +#ifdef CONFIG_SCHEDSTATS extern struct file_operations proc_schedstat_operations; +#endif /* CONFIG_SCHEDSTATS */ + +#ifdef CONFIG_TASK_DELAY_ACCT +struct task_delay_info { + spinlock_t lock; + unsigned int flags; /* Private per-task flags */ + + /* For each stat XXX, add following, aligned appropriately + * + * struct timespec XXX_start, XXX_end; + * u64 XXX_delay; + * u32 XXX_count; + * + * Atomicity of updates to XXX_delay, XXX_count protected by + * single lock above (split into XXX_lock if contention is an issue). + */ + + /* + * XXX_count is incremented on every XXX operation, the delay + * associated with the operation is added to XXX_delay. + * XXX_delay contains the accumulated delay time in nanoseconds. + */ + struct timespec blkio_start, blkio_end; /* Shared by blkio, swapin */ + u64 blkio_delay; /* wait for sync block io completion */ + u64 swapin_delay; /* wait for swapin block io completion */ + u32 blkio_count; /* total count of the number of sync block */ + /* io operations performed */ + u32 swapin_count; /* total count of the number of swapin block */ + /* io operations performed */ +}; +#endif /* CONFIG_TASK_DELAY_ACCT */ + +static inline int sched_info_on(void) +{ +#ifdef CONFIG_SCHEDSTATS + return 1; +#elif defined(CONFIG_TASK_DELAY_ACCT) + extern int delayacct_on; + return delayacct_on; +#else + return 0; #endif +} enum idle_type { @@ -747,7 +795,7 @@ struct task_struct { cpumask_t cpus_allowed; unsigned int time_slice, first_time_slice; -#ifdef CONFIG_SCHEDSTATS +#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) struct sched_info sched_info; #endif @@ -945,6 +993,10 @@ struct task_struct { * cache last used pipe for splice */ struct pipe_inode_info *splice_pipe; +#ifdef CONFIG_TASK_DELAY_ACCT + spinlock_t delays_lock; + struct task_delay_info *delays; +#endif }; static inline pid_t process_group(struct task_struct *tsk) diff --git a/include/linux/taskstats.h b/include/linux/taskstats.h new file mode 100644 index 00000000000..f1cb6cddd19 --- /dev/null +++ b/include/linux/taskstats.h @@ -0,0 +1,137 @@ +/* taskstats.h - exporting per-task statistics + * + * Copyright (C) Shailabh Nagar, IBM Corp. 2006 + * (C) Balbir Singh, IBM Corp. 2006 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _LINUX_TASKSTATS_H +#define _LINUX_TASKSTATS_H + +/* Format for per-task data returned to userland when + * - a task exits + * - listener requests stats for a task + * + * The struct is versioned. Newer versions should only add fields to + * the bottom of the struct to maintain backward compatibility. + * + * + * To add new fields + * a) bump up TASKSTATS_VERSION + * b) add comment indicating new version number at end of struct + * c) add new fields after version comment; maintain 64-bit alignment + */ + +#define TASKSTATS_VERSION 1 + +struct taskstats { + + /* Version 1 */ + __u16 version; + __u16 padding[3]; /* Userspace should not interpret the padding + * field which can be replaced by useful + * fields if struct taskstats is extended. + */ + + /* Delay accounting fields start + * + * All values, until comment "Delay accounting fields end" are + * available only if delay accounting is enabled, even though the last + * few fields are not delays + * + * xxx_count is the number of delay values recorded + * xxx_delay_total is the corresponding cumulative delay in nanoseconds + * + * xxx_delay_total wraps around to zero on overflow + * xxx_count incremented regardless of overflow + */ + + /* Delay waiting for cpu, while runnable + * count, delay_total NOT updated atomically + */ + __u64 cpu_count; + __u64 cpu_delay_total; + + /* Following four fields atomically updated using task->delays->lock */ + + /* Delay waiting for synchronous block I/O to complete + * does not account for delays in I/O submission + */ + __u64 blkio_count; + __u64 blkio_delay_total; + + /* Delay waiting for page fault I/O (swap in only) */ + __u64 swapin_count; + __u64 swapin_delay_total; + + /* cpu "wall-clock" running time + * On some architectures, value will adjust for cpu time stolen + * from the kernel in involuntary waits due to virtualization. + * Value is cumulative, in nanoseconds, without a corresponding count + * and wraps around to zero silently on overflow + */ + __u64 cpu_run_real_total; + + /* cpu "virtual" running time + * Uses time intervals seen by the kernel i.e. no adjustment + * for kernel's involuntary waits due to virtualization. + * Value is cumulative, in nanoseconds, without a corresponding count + * and wraps around to zero silently on overflow + */ + __u64 cpu_run_virtual_total; + /* Delay accounting fields end */ + /* version 1 ends here */ +}; + + +/* + * Commands sent from userspace + * Not versioned. New commands should only be inserted at the enum's end + * prior to __TASKSTATS_CMD_MAX + */ + +enum { + TASKSTATS_CMD_UNSPEC = 0, /* Reserved */ + TASKSTATS_CMD_GET, /* user->kernel request/get-response */ + TASKSTATS_CMD_NEW, /* kernel->user event */ + __TASKSTATS_CMD_MAX, +}; + +#define TASKSTATS_CMD_MAX (__TASKSTATS_CMD_MAX - 1) + +enum { + TASKSTATS_TYPE_UNSPEC = 0, /* Reserved */ + TASKSTATS_TYPE_PID, /* Process id */ + TASKSTATS_TYPE_TGID, /* Thread group id */ + TASKSTATS_TYPE_STATS, /* taskstats structure */ + TASKSTATS_TYPE_AGGR_PID, /* contains pid + stats */ + TASKSTATS_TYPE_AGGR_TGID, /* contains tgid + stats */ + __TASKSTATS_TYPE_MAX, +}; + +#define TASKSTATS_TYPE_MAX (__TASKSTATS_TYPE_MAX - 1) + +enum { + TASKSTATS_CMD_ATTR_UNSPEC = 0, + TASKSTATS_CMD_ATTR_PID, + TASKSTATS_CMD_ATTR_TGID, + TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, + TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, + __TASKSTATS_CMD_ATTR_MAX, +}; + +#define TASKSTATS_CMD_ATTR_MAX (__TASKSTATS_CMD_ATTR_MAX - 1) + +/* NETLINK_GENERIC related info */ + +#define TASKSTATS_GENL_NAME "TASKSTATS" +#define TASKSTATS_GENL_VERSION 0x1 + +#endif /* _LINUX_TASKSTATS_H */ diff --git a/include/linux/taskstats_kern.h b/include/linux/taskstats_kern.h new file mode 100644 index 00000000000..16894b7edcc --- /dev/null +++ b/include/linux/taskstats_kern.h @@ -0,0 +1,89 @@ +/* taskstats_kern.h - kernel header for per-task statistics interface + * + * Copyright (C) Shailabh Nagar, IBM Corp. 2006 + * (C) Balbir Singh, IBM Corp. 2006 + */ + +#ifndef _LINUX_TASKSTATS_KERN_H +#define _LINUX_TASKSTATS_KERN_H + +#include <linux/taskstats.h> +#include <linux/sched.h> +#include <net/genetlink.h> + +#ifdef CONFIG_TASKSTATS +extern kmem_cache_t *taskstats_cache; +extern struct mutex taskstats_exit_mutex; + +static inline void taskstats_exit_free(struct taskstats *tidstats) +{ + if (tidstats) + kmem_cache_free(taskstats_cache, tidstats); +} + +static inline void taskstats_tgid_init(struct signal_struct *sig) +{ + spin_lock_init(&sig->stats_lock); + sig->stats = NULL; +} + +static inline void taskstats_tgid_alloc(struct signal_struct *sig) +{ + struct taskstats *stats; + unsigned long flags; + + stats = kmem_cache_zalloc(taskstats_cache, SLAB_KERNEL); + if (!stats) + return; + + spin_lock_irqsave(&sig->stats_lock, flags); + if (!sig->stats) { + sig->stats = stats; + stats = NULL; + } + spin_unlock_irqrestore(&sig->stats_lock, flags); + + if (stats) + kmem_cache_free(taskstats_cache, stats); +} + +static inline void taskstats_tgid_free(struct signal_struct *sig) +{ + struct taskstats *stats = NULL; + unsigned long flags; + + spin_lock_irqsave(&sig->stats_lock, flags); + if (sig->stats) { + stats = sig->stats; + sig->stats = NULL; + } + spin_unlock_irqrestore(&sig->stats_lock, flags); + if (stats) + kmem_cache_free(taskstats_cache, stats); +} + +extern void taskstats_exit_alloc(struct taskstats **, unsigned int *); +extern void taskstats_exit_send(struct task_struct *, struct taskstats *, int, unsigned int); +extern void taskstats_init_early(void); +extern void taskstats_tgid_alloc(struct signal_struct *); +#else +static inline void taskstats_exit_alloc(struct taskstats **ptidstats, unsigned int *mycpu) +{} +static inline void taskstats_exit_free(struct taskstats *ptidstats) +{} +static inline void taskstats_exit_send(struct task_struct *tsk, + struct taskstats *tidstats, + int group_dead, unsigned int cpu) +{} +static inline void taskstats_tgid_init(struct signal_struct *sig) +{} +static inline void taskstats_tgid_alloc(struct signal_struct *sig) +{} +static inline void taskstats_tgid_free(struct signal_struct *sig) +{} +static inline void taskstats_init_early(void) +{} +#endif /* CONFIG_TASKSTATS */ + +#endif + diff --git a/include/linux/time.h b/include/linux/time.h index c05f8bb9a32..a5b739967b7 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -71,6 +71,18 @@ extern unsigned long mktime(const unsigned int year, const unsigned int mon, extern void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec); /* + * sub = lhs - rhs, in normalized form + */ +static inline struct timespec timespec_sub(struct timespec lhs, + struct timespec rhs) +{ + struct timespec ts_delta; + set_normalized_timespec(&ts_delta, lhs.tv_sec - rhs.tv_sec, + lhs.tv_nsec - rhs.tv_nsec); + return ts_delta; +} + +/* * Returns true if the timespec is norm, false if denorm: */ #define timespec_valid(ts) \ diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index f6024ab4eff..71b6363caaa 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -11,6 +11,7 @@ struct vm_area_struct; #define VM_ALLOC 0x00000002 /* vmalloc() */ #define VM_MAP 0x00000004 /* vmap()ed pages */ #define VM_USERMAP 0x00000008 /* suitable for remap_vmalloc_range */ +#define VM_VPAGES 0x00000010 /* buffer for pages was vmalloc'ed */ /* bits [20..32] reserved for arch specific ioremap internals */ /* diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 805de50df00..8c228726426 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -150,4 +150,24 @@ static inline int genlmsg_unicast(struct sk_buff *skb, u32 pid) return nlmsg_unicast(genl_sock, skb, pid); } +/** + * gennlmsg_data - head of message payload + * @gnlh: genetlink messsage header + */ +static inline void *genlmsg_data(const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +/** + * genlmsg_len - length of message payload + * @gnlh: genetlink message header + */ +static inline int genlmsg_len(const struct genlmsghdr *gnlh) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *)((unsigned char *)gnlh - + NLMSG_HDRLEN); + return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); +} + #endif /* __NET_GENERIC_NETLINK_H */ diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h index fcb5ba87dcc..0ff67398928 100644 --- a/include/rdma/ib_addr.h +++ b/include/rdma/ib_addr.h @@ -89,9 +89,10 @@ static inline void ib_addr_set_pkey(struct rdma_dev_addr *dev_addr, u16 pkey) dev_addr->broadcast[9] = (unsigned char) pkey; } -static inline union ib_gid *ib_addr_get_sgid(struct rdma_dev_addr *dev_addr) +static inline void ib_addr_get_sgid(struct rdma_dev_addr *dev_addr, + union ib_gid *gid) { - return (union ib_gid *) (dev_addr->src_dev_addr + 4); + memcpy(gid, dev_addr->src_dev_addr + 4, sizeof *gid); } static inline void ib_addr_set_sgid(struct rdma_dev_addr *dev_addr, @@ -100,9 +101,10 @@ static inline void ib_addr_set_sgid(struct rdma_dev_addr *dev_addr, memcpy(dev_addr->src_dev_addr + 4, gid, sizeof *gid); } -static inline union ib_gid *ib_addr_get_dgid(struct rdma_dev_addr *dev_addr) +static inline void ib_addr_get_dgid(struct rdma_dev_addr *dev_addr, + union ib_gid *gid) { - return (union ib_gid *) (dev_addr->dst_dev_addr + 4); + memcpy(gid, dev_addr->dst_dev_addr + 4, sizeof *gid); } static inline void ib_addr_set_dgid(struct rdma_dev_addr *dev_addr, diff --git a/include/rdma/ib_fmr_pool.h b/include/rdma/ib_fmr_pool.h index 4ace54cd0cc..00dadbf94e1 100644 --- a/include/rdma/ib_fmr_pool.h +++ b/include/rdma/ib_fmr_pool.h @@ -88,7 +88,7 @@ int ib_flush_fmr_pool(struct ib_fmr_pool *pool); struct ib_pool_fmr *ib_fmr_pool_map_phys(struct ib_fmr_pool *pool_handle, u64 *page_list, int list_len, - u64 *io_virtual_address); + u64 io_virtual_address); int ib_fmr_pool_unmap(struct ib_pool_fmr *fmr); diff --git a/include/video/mbxfb.h b/include/video/mbxfb.h new file mode 100644 index 00000000000..3bde0f5cd55 --- /dev/null +++ b/include/video/mbxfb.h @@ -0,0 +1,28 @@ +#ifndef __MBX_FB_H +#define __MBX_FB_H + +struct mbxfb_val { + unsigned int defval; + unsigned int min; + unsigned int max; +}; + +struct fb_info; + +struct mbxfb_platform_data { + /* Screen info */ + struct mbxfb_val xres; + struct mbxfb_val yres; + struct mbxfb_val bpp; + + /* Memory info */ + unsigned long memsize; /* if 0 use ODFB? */ + unsigned long timings1; + unsigned long timings2; + unsigned long timings3; + + int (*probe)(struct fb_info *fb); + int (*remove)(struct fb_info *fb); +}; + +#endif /* __MBX_FB_H */ diff --git a/init/Kconfig b/init/Kconfig index a5b073a103e..a099fc6526d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -158,6 +158,30 @@ config BSD_PROCESS_ACCT_V3 for processing it. A preliminary version of these tools is available at <http://www.physik3.uni-rostock.de/tim/kernel/utils/acct/>. +config TASKSTATS + bool "Export task/process statistics through netlink (EXPERIMENTAL)" + depends on NET + default n + help + Export selected statistics for tasks/processes through the + generic netlink interface. Unlike BSD process accounting, the + statistics are available during the lifetime of tasks/processes as + responses to commands. Like BSD accounting, they are sent to user + space on task exit. + + Say N if unsure. + +config TASK_DELAY_ACCT + bool "Enable per-task delay accounting (EXPERIMENTAL)" + depends on TASKSTATS + help + Collect information on time spent by a task waiting for system + resources like cpu, synchronous block I/O completion and swapping + in pages. Such statistics can help in setting a task's priorities + relative to other tasks for cpu, io, rss limits etc. + + Say N if unsure. + config SYSCTL bool "Sysctl support" if EMBEDDED default y diff --git a/init/main.c b/init/main.c index 628b8e9e841..8651a720a09 100644 --- a/init/main.c +++ b/init/main.c @@ -41,6 +41,8 @@ #include <linux/cpu.h> #include <linux/cpuset.h> #include <linux/efi.h> +#include <linux/taskstats_kern.h> +#include <linux/delayacct.h> #include <linux/unistd.h> #include <linux/rmap.h> #include <linux/mempolicy.h> @@ -574,6 +576,8 @@ asmlinkage void __init start_kernel(void) proc_root_init(); #endif cpuset_init(); + taskstats_init_early(); + delayacct_init(); check_bugs(); diff --git a/kernel/Makefile b/kernel/Makefile index 47dbcd570cd..d62ec66c1af 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -48,6 +48,8 @@ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o obj-$(CONFIG_RELAY) += relay.o +obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o +obj-$(CONFIG_TASKSTATS) += taskstats.o ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is diff --git a/kernel/acct.c b/kernel/acct.c index f18e0b8df3e..2a7c933651c 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -488,7 +488,7 @@ static void do_acct_process(struct file *file) old_encode_dev(tty_devnum(current->signal->tty)) : 0; read_unlock(&tasklist_lock); - spin_lock(¤t->sighand->siglock); + spin_lock_irq(¤t->sighand->siglock); ac.ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime))); ac.ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime))); ac.ac_flag = pacct->ac_flag; @@ -496,7 +496,7 @@ static void do_acct_process(struct file *file) ac.ac_minflt = encode_comp_t(pacct->ac_minflt); ac.ac_majflt = encode_comp_t(pacct->ac_majflt); ac.ac_exitcode = pacct->ac_exitcode; - spin_unlock(¤t->sighand->siglock); + spin_unlock_irq(¤t->sighand->siglock); ac.ac_io = encode_comp_t(0 /* current->io_usage */); /* %% */ ac.ac_rw = encode_comp_t(ac.ac_io / 1024); ac.ac_swaps = encode_comp_t(0); diff --git a/kernel/delayacct.c b/kernel/delayacct.c new file mode 100644 index 00000000000..f05392d6426 --- /dev/null +++ b/kernel/delayacct.c @@ -0,0 +1,178 @@ +/* delayacct.c - per-task delay accounting + * + * Copyright (C) Shailabh Nagar, IBM Corp. 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/sysctl.h> +#include <linux/delayacct.h> + +int delayacct_on __read_mostly; /* Delay accounting turned on/off */ +kmem_cache_t *delayacct_cache; + +static int __init delayacct_setup_enable(char *str) +{ + delayacct_on = 1; + return 1; +} +__setup("delayacct", delayacct_setup_enable); + +void delayacct_init(void) +{ + delayacct_cache = kmem_cache_create("delayacct_cache", + sizeof(struct task_delay_info), + 0, + SLAB_PANIC, + NULL, NULL); + delayacct_tsk_init(&init_task); +} + +void __delayacct_tsk_init(struct task_struct *tsk) +{ + spin_lock_init(&tsk->delays_lock); + /* No need to acquire tsk->delays_lock for allocation here unless + __delayacct_tsk_init called after tsk is attached to tasklist + */ + tsk->delays = kmem_cache_zalloc(delayacct_cache, SLAB_KERNEL); + if (tsk->delays) + spin_lock_init(&tsk->delays->lock); +} + +void __delayacct_tsk_exit(struct task_struct *tsk) +{ + struct task_delay_info *delays = tsk->delays; + spin_lock(&tsk->delays_lock); + tsk->delays = NULL; + spin_unlock(&tsk->delays_lock); + kmem_cache_free(delayacct_cache, delays); +} + +/* + * Start accounting for a delay statistic using + * its starting timestamp (@start) + */ + +static inline void delayacct_start(struct timespec *start) +{ + do_posix_clock_monotonic_gettime(start); +} + +/* + * Finish delay accounting for a statistic using + * its timestamps (@start, @end), accumalator (@total) and @count + */ + +static void delayacct_end(struct timespec *start, struct timespec *end, + u64 *total, u32 *count) +{ + struct timespec ts; + s64 ns; + + do_posix_clock_monotonic_gettime(end); + ts = timespec_sub(*end, *start); + ns = timespec_to_ns(&ts); + if (ns < 0) + return; + + spin_lock(¤t->delays->lock); + *total += ns; + (*count)++; + spin_unlock(¤t->delays->lock); +} + +void __delayacct_blkio_start(void) +{ + delayacct_start(¤t->delays->blkio_start); +} + +void __delayacct_blkio_end(void) +{ + if (current->delays->flags & DELAYACCT_PF_SWAPIN) + /* Swapin block I/O */ + delayacct_end(¤t->delays->blkio_start, + ¤t->delays->blkio_end, + ¤t->delays->swapin_delay, + ¤t->delays->swapin_count); + else /* Other block I/O */ + delayacct_end(¤t->delays->blkio_start, + ¤t->delays->blkio_end, + ¤t->delays->blkio_delay, + ¤t->delays->blkio_count); +} + +int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk) +{ + s64 tmp; + struct timespec ts; + unsigned long t1,t2,t3; + + spin_lock(&tsk->delays_lock); + + /* Though tsk->delays accessed later, early exit avoids + * unnecessary returning of other data + */ + if (!tsk->delays) + goto done; + + tmp = (s64)d->cpu_run_real_total; + cputime_to_timespec(tsk->utime + tsk->stime, &ts); + tmp += timespec_to_ns(&ts); + d->cpu_run_real_total = (tmp < (s64)d->cpu_run_real_total) ? 0 : tmp; + + /* + * No locking available for sched_info (and too expensive to add one) + * Mitigate by taking snapshot of values + */ + t1 = tsk->sched_info.pcnt; + t2 = tsk->sched_info.run_delay; + t3 = tsk->sched_info.cpu_time; + + d->cpu_count += t1; + + jiffies_to_timespec(t2, &ts); + tmp = (s64)d->cpu_delay_total + timespec_to_ns(&ts); + d->cpu_delay_total = (tmp < (s64)d->cpu_delay_total) ? 0 : tmp; + + tmp = (s64)d->cpu_run_virtual_total + (s64)jiffies_to_usecs(t3) * 1000; + d->cpu_run_virtual_total = + (tmp < (s64)d->cpu_run_virtual_total) ? 0 : tmp; + + /* zero XXX_total, non-zero XXX_count implies XXX stat overflowed */ + + spin_lock(&tsk->delays->lock); + tmp = d->blkio_delay_total + tsk->delays->blkio_delay; + d->blkio_delay_total = (tmp < d->blkio_delay_total) ? 0 : tmp; + tmp = d->swapin_delay_total + tsk->delays->swapin_delay; + d->swapin_delay_total = (tmp < d->swapin_delay_total) ? 0 : tmp; + d->blkio_count += tsk->delays->blkio_count; + d->swapin_count += tsk->delays->swapin_count; + spin_unlock(&tsk->delays->lock); + +done: + spin_unlock(&tsk->delays_lock); + return 0; +} + +__u64 __delayacct_blkio_ticks(struct task_struct *tsk) +{ + __u64 ret; + + spin_lock(&tsk->delays->lock); + ret = nsec_to_clock_t(tsk->delays->blkio_delay + + tsk->delays->swapin_delay); + spin_unlock(&tsk->delays->lock); + return ret; +} + diff --git a/kernel/exit.c b/kernel/exit.c index 6664c084783..dba194a8d41 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -25,6 +25,8 @@ #include <linux/mount.h> #include <linux/proc_fs.h> #include <linux/mempolicy.h> +#include <linux/taskstats_kern.h> +#include <linux/delayacct.h> #include <linux/cpuset.h> #include <linux/syscalls.h> #include <linux/signal.h> @@ -843,7 +845,9 @@ static void exit_notify(struct task_struct *tsk) fastcall NORET_TYPE void do_exit(long code) { struct task_struct *tsk = current; + struct taskstats *tidstats; int group_dead; + unsigned int mycpu; profile_task_exit(tsk); @@ -881,6 +885,8 @@ fastcall NORET_TYPE void do_exit(long code) current->comm, current->pid, preempt_count()); + taskstats_exit_alloc(&tidstats, &mycpu); + acct_update_integrals(tsk); if (tsk->mm) { update_hiwater_rss(tsk->mm); @@ -900,6 +906,10 @@ fastcall NORET_TYPE void do_exit(long code) #endif if (unlikely(tsk->audit_context)) audit_free(tsk); + taskstats_exit_send(tsk, tidstats, group_dead, mycpu); + taskstats_exit_free(tidstats); + delayacct_tsk_exit(tsk); + exit_mm(tsk); if (group_dead) diff --git a/kernel/fork.c b/kernel/fork.c index 926e5a68ea9..1b0f7b1e088 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -43,6 +43,8 @@ #include <linux/rmap.h> #include <linux/acct.h> #include <linux/cn_proc.h> +#include <linux/delayacct.h> +#include <linux/taskstats_kern.h> #include <asm/pgtable.h> #include <asm/pgalloc.h> @@ -818,6 +820,7 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts if (clone_flags & CLONE_THREAD) { atomic_inc(¤t->signal->count); atomic_inc(¤t->signal->live); + taskstats_tgid_alloc(current->signal); return 0; } sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL); @@ -862,6 +865,7 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts INIT_LIST_HEAD(&sig->cpu_timers[0]); INIT_LIST_HEAD(&sig->cpu_timers[1]); INIT_LIST_HEAD(&sig->cpu_timers[2]); + taskstats_tgid_init(sig); task_lock(current->group_leader); memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim); @@ -883,6 +887,7 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts void __cleanup_signal(struct signal_struct *sig) { exit_thread_group_keys(sig); + taskstats_tgid_free(sig); kmem_cache_free(signal_cachep, sig); } @@ -1000,6 +1005,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto bad_fork_cleanup_put_domain; p->did_exec = 0; + delayacct_tsk_init(p); /* Must remain after dup_task_struct() */ copy_flags(clone_flags, p); p->pid = pid; retval = -EFAULT; diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 39277dd6bf9..ab16a5a4cfe 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -275,8 +275,8 @@ static void upcase_if_global(struct kallsym_iter *iter) static int get_ksymbol_mod(struct kallsym_iter *iter) { iter->owner = module_get_kallsym(iter->pos - kallsyms_num_syms, - &iter->value, - &iter->type, iter->name); + &iter->value, &iter->type, + iter->name, sizeof(iter->name)); if (iter->owner == NULL) return 0; diff --git a/kernel/kthread.c b/kernel/kthread.c index 24be714b04c..4f9c60ef95e 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -216,23 +216,6 @@ EXPORT_SYMBOL(kthread_bind); */ int kthread_stop(struct task_struct *k) { - return kthread_stop_sem(k, NULL); -} -EXPORT_SYMBOL(kthread_stop); - -/** - * kthread_stop_sem - stop a thread created by kthread_create(). - * @k: thread created by kthread_create(). - * @s: semaphore that @k waits on while idle. - * - * Does essentially the same thing as kthread_stop() above, but wakes - * @k by calling up(@s). - * - * Returns the result of threadfn(), or %-EINTR if wake_up_process() - * was never called. - */ -int kthread_stop_sem(struct task_struct *k, struct semaphore *s) -{ int ret; mutex_lock(&kthread_stop_lock); @@ -246,10 +229,7 @@ int kthread_stop_sem(struct task_struct *k, struct semaphore *s) /* Now set kthread_should_stop() to true, and wake it up. */ kthread_stop_info.k = k; - if (s) - up(s); - else - wake_up_process(k); + wake_up_process(k); put_task_struct(k); /* Once it dies, reset stop ptr, gather result and we're done. */ @@ -260,7 +240,7 @@ int kthread_stop_sem(struct task_struct *k, struct semaphore *s) return ret; } -EXPORT_SYMBOL(kthread_stop_sem); +EXPORT_SYMBOL(kthread_stop); static __init int helper_init(void) { diff --git a/kernel/module.c b/kernel/module.c index 35e1b1f859d..2a19cd47c04 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2019,10 +2019,8 @@ const char *module_address_lookup(unsigned long addr, return NULL; } -struct module *module_get_kallsym(unsigned int symnum, - unsigned long *value, - char *type, - char namebuf[128]) +struct module *module_get_kallsym(unsigned int symnum, unsigned long *value, + char *type, char *name, size_t namelen) { struct module *mod; @@ -2031,9 +2029,8 @@ struct module *module_get_kallsym(unsigned int symnum, if (symnum < mod->num_symtab) { *value = mod->symtab[symnum].st_value; *type = mod->symtab[symnum].st_info; - strncpy(namebuf, - mod->strtab + mod->symtab[symnum].st_name, - 127); + strlcpy(name, mod->strtab + mod->symtab[symnum].st_name, + namelen); mutex_unlock(&module_mutex); return mod; } diff --git a/kernel/rtmutex-tester.c b/kernel/rtmutex-tester.c index 494dac872a1..948bd8f643e 100644 --- a/kernel/rtmutex-tester.c +++ b/kernel/rtmutex-tester.c @@ -275,6 +275,7 @@ static int test_func(void *data) /* Wait for the next command to be executed */ schedule(); + try_to_freeze(); if (signal_pending(current)) flush_signals(current); diff --git a/kernel/sched.c b/kernel/sched.c index d714611f169..b44b9a43b0f 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -51,6 +51,7 @@ #include <linux/times.h> #include <linux/acct.h> #include <linux/kprobes.h> +#include <linux/delayacct.h> #include <asm/tlb.h> #include <asm/unistd.h> @@ -501,9 +502,36 @@ struct file_operations proc_schedstat_operations = { .release = single_release, }; +/* + * Expects runqueue lock to be held for atomicity of update + */ +static inline void +rq_sched_info_arrive(struct rq *rq, unsigned long delta_jiffies) +{ + if (rq) { + rq->rq_sched_info.run_delay += delta_jiffies; + rq->rq_sched_info.pcnt++; + } +} + +/* + * Expects runqueue lock to be held for atomicity of update + */ +static inline void +rq_sched_info_depart(struct rq *rq, unsigned long delta_jiffies) +{ + if (rq) + rq->rq_sched_info.cpu_time += delta_jiffies; +} # define schedstat_inc(rq, field) do { (rq)->field++; } while (0) # define schedstat_add(rq, field, amt) do { (rq)->field += (amt); } while (0) #else /* !CONFIG_SCHEDSTATS */ +static inline void +rq_sched_info_arrive(struct rq *rq, unsigned long delta_jiffies) +{} +static inline void +rq_sched_info_depart(struct rq *rq, unsigned long delta_jiffies) +{} # define schedstat_inc(rq, field) do { } while (0) # define schedstat_add(rq, field, amt) do { } while (0) #endif @@ -523,7 +551,7 @@ static inline struct rq *this_rq_lock(void) return rq; } -#ifdef CONFIG_SCHEDSTATS +#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) /* * Called when a process is dequeued from the active array and given * the cpu. We should note that with the exception of interactive @@ -551,21 +579,16 @@ static inline void sched_info_dequeued(struct task_struct *t) */ static void sched_info_arrive(struct task_struct *t) { - unsigned long now = jiffies, diff = 0; - struct rq *rq = task_rq(t); + unsigned long now = jiffies, delta_jiffies = 0; if (t->sched_info.last_queued) - diff = now - t->sched_info.last_queued; + delta_jiffies = now - t->sched_info.last_queued; sched_info_dequeued(t); - t->sched_info.run_delay += diff; + t->sched_info.run_delay += delta_jiffies; t->sched_info.last_arrival = now; t->sched_info.pcnt++; - if (!rq) - return; - - rq->rq_sched_info.run_delay += diff; - rq->rq_sched_info.pcnt++; + rq_sched_info_arrive(task_rq(t), delta_jiffies); } /* @@ -585,8 +608,9 @@ static void sched_info_arrive(struct task_struct *t) */ static inline void sched_info_queued(struct task_struct *t) { - if (!t->sched_info.last_queued) - t->sched_info.last_queued = jiffies; + if (unlikely(sched_info_on())) + if (!t->sched_info.last_queued) + t->sched_info.last_queued = jiffies; } /* @@ -595,13 +619,10 @@ static inline void sched_info_queued(struct task_struct *t) */ static inline void sched_info_depart(struct task_struct *t) { - struct rq *rq = task_rq(t); - unsigned long diff = jiffies - t->sched_info.last_arrival; - - t->sched_info.cpu_time += diff; + unsigned long delta_jiffies = jiffies - t->sched_info.last_arrival; - if (rq) - rq->rq_sched_info.cpu_time += diff; + t->sched_info.cpu_time += delta_jiffies; + rq_sched_info_depart(task_rq(t), delta_jiffies); } /* @@ -610,7 +631,7 @@ static inline void sched_info_depart(struct task_struct *t) * the idle task.) We are only called when prev != next. */ static inline void -sched_info_switch(struct task_struct *prev, struct task_struct *next) +__sched_info_switch(struct task_struct *prev, struct task_struct *next) { struct rq *rq = task_rq(prev); @@ -625,10 +646,16 @@ sched_info_switch(struct task_struct *prev, struct task_struct *next) if (next != rq->idle) sched_info_arrive(next); } +static inline void +sched_info_switch(struct task_struct *prev, struct task_struct *next) +{ + if (unlikely(sched_info_on())) + __sched_info_switch(prev, next); +} #else #define sched_info_queued(t) do { } while (0) #define sched_info_switch(t, next) do { } while (0) -#endif /* CONFIG_SCHEDSTATS */ +#endif /* CONFIG_SCHEDSTATS || CONFIG_TASK_DELAY_ACCT */ /* * Adding/removing a task to/from a priority array: @@ -1530,8 +1557,9 @@ void fastcall sched_fork(struct task_struct *p, int clone_flags) INIT_LIST_HEAD(&p->run_list); p->array = NULL; -#ifdef CONFIG_SCHEDSTATS - memset(&p->sched_info, 0, sizeof(p->sched_info)); +#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) + if (unlikely(sched_info_on())) + memset(&p->sched_info, 0, sizeof(p->sched_info)); #endif #if defined(CONFIG_SMP) && defined(__ARCH_WANT_UNLOCKED_CTXSW) p->oncpu = 0; @@ -1788,7 +1816,15 @@ context_switch(struct rq *rq, struct task_struct *prev, WARN_ON(rq->prev_mm); rq->prev_mm = oldmm; } + /* + * Since the runqueue lock will be released by the next + * task (which is an invalid locking op but in the case + * of the scheduler it's an obvious special-case), so we + * do an early lockdep release here: + */ +#ifndef __ARCH_WANT_UNLOCKED_CTXSW spin_release(&rq->lock.dep_map, 1, _THIS_IP_); +#endif /* Here we just switch the register state and the stack. */ switch_to(prev, next, prev); @@ -4526,9 +4562,11 @@ void __sched io_schedule(void) { struct rq *rq = &__raw_get_cpu_var(runqueues); + delayacct_blkio_start(); atomic_inc(&rq->nr_iowait); schedule(); atomic_dec(&rq->nr_iowait); + delayacct_blkio_end(); } EXPORT_SYMBOL(io_schedule); @@ -4537,9 +4575,11 @@ long __sched io_schedule_timeout(long timeout) struct rq *rq = &__raw_get_cpu_var(runqueues); long ret; + delayacct_blkio_start(); atomic_inc(&rq->nr_iowait); ret = schedule_timeout(timeout); atomic_dec(&rq->nr_iowait); + delayacct_blkio_end(); return ret; } diff --git a/kernel/softirq.c b/kernel/softirq.c index fd12f2556f0..0f08a84ae30 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -311,8 +311,6 @@ void open_softirq(int nr, void (*action)(struct softirq_action*), void *data) softirq_vec[nr].action = action; } -EXPORT_UNUSED_SYMBOL(open_softirq); /* June 2006 */ - /* Tasklets */ struct tasklet_head { diff --git a/kernel/taskstats.c b/kernel/taskstats.c new file mode 100644 index 00000000000..f45179ce028 --- /dev/null +++ b/kernel/taskstats.c @@ -0,0 +1,568 @@ +/* + * taskstats.c - Export per-task statistics to userland + * + * Copyright (C) Shailabh Nagar, IBM Corp. 2006 + * (C) Balbir Singh, IBM Corp. 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/taskstats_kern.h> +#include <linux/delayacct.h> +#include <linux/cpumask.h> +#include <linux/percpu.h> +#include <net/genetlink.h> +#include <asm/atomic.h> + +/* + * Maximum length of a cpumask that can be specified in + * the TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK attribute + */ +#define TASKSTATS_CPUMASK_MAXLEN (100+6*NR_CPUS) + +static DEFINE_PER_CPU(__u32, taskstats_seqnum) = { 0 }; +static int family_registered; +kmem_cache_t *taskstats_cache; + +static struct genl_family family = { + .id = GENL_ID_GENERATE, + .name = TASKSTATS_GENL_NAME, + .version = TASKSTATS_GENL_VERSION, + .maxattr = TASKSTATS_CMD_ATTR_MAX, +}; + +static struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] +__read_mostly = { + [TASKSTATS_CMD_ATTR_PID] = { .type = NLA_U32 }, + [TASKSTATS_CMD_ATTR_TGID] = { .type = NLA_U32 }, + [TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING }, + [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },}; + +struct listener { + struct list_head list; + pid_t pid; + char valid; +}; + +struct listener_list { + struct rw_semaphore sem; + struct list_head list; +}; +static DEFINE_PER_CPU(struct listener_list, listener_array); + +enum actions { + REGISTER, + DEREGISTER, + CPU_DONT_CARE +}; + +static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp, + void **replyp, size_t size) +{ + struct sk_buff *skb; + void *reply; + + /* + * If new attributes are added, please revisit this allocation + */ + skb = nlmsg_new(size); + if (!skb) + return -ENOMEM; + + if (!info) { + int seq = get_cpu_var(taskstats_seqnum)++; + put_cpu_var(taskstats_seqnum); + + reply = genlmsg_put(skb, 0, seq, + family.id, 0, 0, + cmd, family.version); + } else + reply = genlmsg_put(skb, info->snd_pid, info->snd_seq, + family.id, 0, 0, + cmd, family.version); + if (reply == NULL) { + nlmsg_free(skb); + return -EINVAL; + } + + *skbp = skb; + *replyp = reply; + return 0; +} + +/* + * Send taskstats data in @skb to listener with nl_pid @pid + */ +static int send_reply(struct sk_buff *skb, pid_t pid) +{ + struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data); + void *reply = genlmsg_data(genlhdr); + int rc; + + rc = genlmsg_end(skb, reply); + if (rc < 0) { + nlmsg_free(skb); + return rc; + } + + return genlmsg_unicast(skb, pid); +} + +/* + * Send taskstats data in @skb to listeners registered for @cpu's exit data + */ +static int send_cpu_listeners(struct sk_buff *skb, unsigned int cpu) +{ + struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data); + struct listener_list *listeners; + struct listener *s, *tmp; + struct sk_buff *skb_next, *skb_cur = skb; + void *reply = genlmsg_data(genlhdr); + int rc, ret, delcount = 0; + + rc = genlmsg_end(skb, reply); + if (rc < 0) { + nlmsg_free(skb); + return rc; + } + + rc = 0; + listeners = &per_cpu(listener_array, cpu); + down_read(&listeners->sem); + list_for_each_entry_safe(s, tmp, &listeners->list, list) { + skb_next = NULL; + if (!list_is_last(&s->list, &listeners->list)) { + skb_next = skb_clone(skb_cur, GFP_KERNEL); + if (!skb_next) { + nlmsg_free(skb_cur); + rc = -ENOMEM; + break; + } + } + ret = genlmsg_unicast(skb_cur, s->pid); + if (ret == -ECONNREFUSED) { + s->valid = 0; + delcount++; + rc = ret; + } + skb_cur = skb_next; + } + up_read(&listeners->sem); + + if (!delcount) + return rc; + + /* Delete invalidated entries */ + down_write(&listeners->sem); + list_for_each_entry_safe(s, tmp, &listeners->list, list) { + if (!s->valid) { + list_del(&s->list); + kfree(s); + } + } + up_write(&listeners->sem); + return rc; +} + +static int fill_pid(pid_t pid, struct task_struct *pidtsk, + struct taskstats *stats) +{ + int rc; + struct task_struct *tsk = pidtsk; + + if (!pidtsk) { + read_lock(&tasklist_lock); + tsk = find_task_by_pid(pid); + if (!tsk) { + read_unlock(&tasklist_lock); + return -ESRCH; + } + get_task_struct(tsk); + read_unlock(&tasklist_lock); + } else + get_task_struct(tsk); + + /* + * Each accounting subsystem adds calls to its functions to + * fill in relevant parts of struct taskstsats as follows + * + * rc = per-task-foo(stats, tsk); + * if (rc) + * goto err; + */ + + rc = delayacct_add_tsk(stats, tsk); + stats->version = TASKSTATS_VERSION; + + /* Define err: label here if needed */ + put_task_struct(tsk); + return rc; + +} + +static int fill_tgid(pid_t tgid, struct task_struct *tgidtsk, + struct taskstats *stats) +{ + struct task_struct *tsk, *first; + unsigned long flags; + + /* + * Add additional stats from live tasks except zombie thread group + * leaders who are already counted with the dead tasks + */ + first = tgidtsk; + if (!first) { + read_lock(&tasklist_lock); + first = find_task_by_pid(tgid); + if (!first) { + read_unlock(&tasklist_lock); + return -ESRCH; + } + get_task_struct(first); + read_unlock(&tasklist_lock); + } else + get_task_struct(first); + + /* Start with stats from dead tasks */ + spin_lock_irqsave(&first->signal->stats_lock, flags); + if (first->signal->stats) + memcpy(stats, first->signal->stats, sizeof(*stats)); + spin_unlock_irqrestore(&first->signal->stats_lock, flags); + + tsk = first; + read_lock(&tasklist_lock); + do { + if (tsk->exit_state == EXIT_ZOMBIE && thread_group_leader(tsk)) + continue; + /* + * Accounting subsystem can call its functions here to + * fill in relevant parts of struct taskstsats as follows + * + * per-task-foo(stats, tsk); + */ + delayacct_add_tsk(stats, tsk); + + } while_each_thread(first, tsk); + read_unlock(&tasklist_lock); + stats->version = TASKSTATS_VERSION; + + /* + * Accounting subsytems can also add calls here to modify + * fields of taskstats. + */ + + return 0; +} + + +static void fill_tgid_exit(struct task_struct *tsk) +{ + unsigned long flags; + + spin_lock_irqsave(&tsk->signal->stats_lock, flags); + if (!tsk->signal->stats) + goto ret; + + /* + * Each accounting subsystem calls its functions here to + * accumalate its per-task stats for tsk, into the per-tgid structure + * + * per-task-foo(tsk->signal->stats, tsk); + */ + delayacct_add_tsk(tsk->signal->stats, tsk); +ret: + spin_unlock_irqrestore(&tsk->signal->stats_lock, flags); + return; +} + +static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd) +{ + struct listener_list *listeners; + struct listener *s, *tmp; + unsigned int cpu; + cpumask_t mask = *maskp; + + if (!cpus_subset(mask, cpu_possible_map)) + return -EINVAL; + + if (isadd == REGISTER) { + for_each_cpu_mask(cpu, mask) { + s = kmalloc_node(sizeof(struct listener), GFP_KERNEL, + cpu_to_node(cpu)); + if (!s) + goto cleanup; + s->pid = pid; + INIT_LIST_HEAD(&s->list); + s->valid = 1; + + listeners = &per_cpu(listener_array, cpu); + down_write(&listeners->sem); + list_add(&s->list, &listeners->list); + up_write(&listeners->sem); + } + return 0; + } + + /* Deregister or cleanup */ +cleanup: + for_each_cpu_mask(cpu, mask) { + listeners = &per_cpu(listener_array, cpu); + down_write(&listeners->sem); + list_for_each_entry_safe(s, tmp, &listeners->list, list) { + if (s->pid == pid) { + list_del(&s->list); + kfree(s); + break; + } + } + up_write(&listeners->sem); + } + return 0; +} + +static int parse(struct nlattr *na, cpumask_t *mask) +{ + char *data; + int len; + int ret; + + if (na == NULL) + return 1; + len = nla_len(na); + if (len > TASKSTATS_CPUMASK_MAXLEN) + return -E2BIG; + if (len < 1) + return -EINVAL; + data = kmalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + nla_strlcpy(data, na, len); + ret = cpulist_parse(data, *mask); + kfree(data); + return ret; +} + +static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) +{ + int rc = 0; + struct sk_buff *rep_skb; + struct taskstats stats; + void *reply; + size_t size; + struct nlattr *na; + cpumask_t mask; + + rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], &mask); + if (rc < 0) + return rc; + if (rc == 0) + return add_del_listener(info->snd_pid, &mask, REGISTER); + + rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], &mask); + if (rc < 0) + return rc; + if (rc == 0) + return add_del_listener(info->snd_pid, &mask, DEREGISTER); + + /* + * Size includes space for nested attributes + */ + size = nla_total_size(sizeof(u32)) + + nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); + + memset(&stats, 0, sizeof(stats)); + rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, &reply, size); + if (rc < 0) + return rc; + + if (info->attrs[TASKSTATS_CMD_ATTR_PID]) { + u32 pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); + rc = fill_pid(pid, NULL, &stats); + if (rc < 0) + goto err; + + na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_PID); + NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_PID, pid); + NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, + stats); + } else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) { + u32 tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); + rc = fill_tgid(tgid, NULL, &stats); + if (rc < 0) + goto err; + + na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID); + NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, tgid); + NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, + stats); + } else { + rc = -EINVAL; + goto err; + } + + nla_nest_end(rep_skb, na); + + return send_reply(rep_skb, info->snd_pid); + +nla_put_failure: + return genlmsg_cancel(rep_skb, reply); +err: + nlmsg_free(rep_skb); + return rc; +} + +void taskstats_exit_alloc(struct taskstats **ptidstats, unsigned int *mycpu) +{ + struct listener_list *listeners; + struct taskstats *tmp; + /* + * This is the cpu on which the task is exiting currently and will + * be the one for which the exit event is sent, even if the cpu + * on which this function is running changes later. + */ + *mycpu = raw_smp_processor_id(); + + *ptidstats = NULL; + tmp = kmem_cache_zalloc(taskstats_cache, SLAB_KERNEL); + if (!tmp) + return; + + listeners = &per_cpu(listener_array, *mycpu); + down_read(&listeners->sem); + if (!list_empty(&listeners->list)) { + *ptidstats = tmp; + tmp = NULL; + } + up_read(&listeners->sem); + kfree(tmp); +} + +/* Send pid data out on exit */ +void taskstats_exit_send(struct task_struct *tsk, struct taskstats *tidstats, + int group_dead, unsigned int mycpu) +{ + int rc; + struct sk_buff *rep_skb; + void *reply; + size_t size; + int is_thread_group; + struct nlattr *na; + unsigned long flags; + + if (!family_registered || !tidstats) + return; + + spin_lock_irqsave(&tsk->signal->stats_lock, flags); + is_thread_group = tsk->signal->stats ? 1 : 0; + spin_unlock_irqrestore(&tsk->signal->stats_lock, flags); + + rc = 0; + /* + * Size includes space for nested attributes + */ + size = nla_total_size(sizeof(u32)) + + nla_total_size(sizeof(struct taskstats)) + nla_total_size(0); + + if (is_thread_group) + size = 2 * size; /* PID + STATS + TGID + STATS */ + + rc = prepare_reply(NULL, TASKSTATS_CMD_NEW, &rep_skb, &reply, size); + if (rc < 0) + goto ret; + + rc = fill_pid(tsk->pid, tsk, tidstats); + if (rc < 0) + goto err_skb; + + na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_PID); + NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_PID, (u32)tsk->pid); + NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, + *tidstats); + nla_nest_end(rep_skb, na); + + if (!is_thread_group) + goto send; + + /* + * tsk has/had a thread group so fill the tsk->signal->stats structure + * Doesn't matter if tsk is the leader or the last group member leaving + */ + + fill_tgid_exit(tsk); + if (!group_dead) + goto send; + + na = nla_nest_start(rep_skb, TASKSTATS_TYPE_AGGR_TGID); + NLA_PUT_U32(rep_skb, TASKSTATS_TYPE_TGID, (u32)tsk->tgid); + /* No locking needed for tsk->signal->stats since group is dead */ + NLA_PUT_TYPE(rep_skb, struct taskstats, TASKSTATS_TYPE_STATS, + *tsk->signal->stats); + nla_nest_end(rep_skb, na); + +send: + send_cpu_listeners(rep_skb, mycpu); + return; + +nla_put_failure: + genlmsg_cancel(rep_skb, reply); + goto ret; +err_skb: + nlmsg_free(rep_skb); +ret: + return; +} + +static struct genl_ops taskstats_ops = { + .cmd = TASKSTATS_CMD_GET, + .doit = taskstats_user_cmd, + .policy = taskstats_cmd_get_policy, +}; + +/* Needed early in initialization */ +void __init taskstats_init_early(void) +{ + unsigned int i; + + taskstats_cache = kmem_cache_create("taskstats_cache", + sizeof(struct taskstats), + 0, SLAB_PANIC, NULL, NULL); + for_each_possible_cpu(i) { + INIT_LIST_HEAD(&(per_cpu(listener_array, i).list)); + init_rwsem(&(per_cpu(listener_array, i).sem)); + } +} + +static int __init taskstats_init(void) +{ + int rc; + + rc = genl_register_family(&family); + if (rc) + return rc; + + rc = genl_register_ops(&family, &taskstats_ops); + if (rc < 0) + goto err; + + family_registered = 1; + return 0; +err: + genl_unregister_family(&family); + return rc; +} + +/* + * late initcall ensures initialization of statistics collection + * mechanisms precedes initialization of the taskstats interface + */ +late_initcall(taskstats_init); diff --git a/kernel/timer.c b/kernel/timer.c index 2a87430a58d..05809c2e2fd 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -374,6 +374,7 @@ int del_timer_sync(struct timer_list *timer) int ret = try_to_del_timer_sync(timer); if (ret >= 0) return ret; + cpu_relax(); } } @@ -968,6 +969,7 @@ void __init timekeeping_init(void) } +static int timekeeping_suspended; /* * timekeeping_resume - Resumes the generic timekeeping subsystem. * @dev: unused @@ -983,6 +985,18 @@ static int timekeeping_resume(struct sys_device *dev) write_seqlock_irqsave(&xtime_lock, flags); /* restart the last cycle value */ clock->cycle_last = clocksource_read(clock); + clock->error = 0; + timekeeping_suspended = 0; + write_sequnlock_irqrestore(&xtime_lock, flags); + return 0; +} + +static int timekeeping_suspend(struct sys_device *dev, pm_message_t state) +{ + unsigned long flags; + + write_seqlock_irqsave(&xtime_lock, flags); + timekeeping_suspended = 1; write_sequnlock_irqrestore(&xtime_lock, flags); return 0; } @@ -990,6 +1004,7 @@ static int timekeeping_resume(struct sys_device *dev) /* sysfs resume/suspend bits for timekeeping */ static struct sysdev_class timekeeping_sysclass = { .resume = timekeeping_resume, + .suspend = timekeeping_suspend, set_kset_name("timekeeping"), }; @@ -1100,13 +1115,16 @@ static void update_wall_time(void) { cycle_t offset; - clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift; + /* Make sure we're fully resumed: */ + if (unlikely(timekeeping_suspended)) + return; #ifdef CONFIG_GENERIC_TIME offset = (clocksource_read(clock) - clock->cycle_last) & clock->mask; #else offset = clock->cycle_interval; #endif + clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift; /* normally this loop will run just once, however in the * case of lost or late ticks, it will accumulate correctly. diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e5889b1a33f..554ee688a9f 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -158,7 +158,7 @@ config DEBUG_RWSEMS config DEBUG_LOCK_ALLOC bool "Lock debugging: detect incorrect freeing of live locks" - depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT select DEBUG_SPINLOCK select DEBUG_MUTEXES select DEBUG_RWSEMS @@ -173,7 +173,7 @@ config DEBUG_LOCK_ALLOC config PROVE_LOCKING bool "Lock debugging: prove locking correctness" - depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT select LOCKDEP select DEBUG_SPINLOCK select DEBUG_MUTEXES @@ -216,7 +216,7 @@ config PROVE_LOCKING config LOCKDEP bool - depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT select STACKTRACE select FRAME_POINTER select KALLSYMS @@ -224,13 +224,14 @@ config LOCKDEP config DEBUG_LOCKDEP bool "Lock dependency engine debugging" - depends on LOCKDEP + depends on DEBUG_KERNEL && LOCKDEP help If you say Y here, the lock dependency engine will do additional runtime checks to debug itself, at the price of more runtime overhead. config TRACE_IRQFLAGS + depends on DEBUG_KERNEL bool default y depends on TRACE_IRQFLAGS_SUPPORT @@ -256,6 +257,7 @@ config DEBUG_LOCKING_API_SELFTESTS config STACKTRACE bool + depends on DEBUG_KERNEL depends on STACKTRACE_SUPPORT config DEBUG_KOBJECT diff --git a/lib/idr.c b/lib/idr.c index 4d096819511..16d2143fea4 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -38,14 +38,15 @@ static kmem_cache_t *idr_layer_cache; static struct idr_layer *alloc_layer(struct idr *idp) { struct idr_layer *p; + unsigned long flags; - spin_lock(&idp->lock); + spin_lock_irqsave(&idp->lock, flags); if ((p = idp->id_free)) { idp->id_free = p->ary[0]; idp->id_free_cnt--; p->ary[0] = NULL; } - spin_unlock(&idp->lock); + spin_unlock_irqrestore(&idp->lock, flags); return(p); } @@ -59,12 +60,14 @@ static void __free_layer(struct idr *idp, struct idr_layer *p) static void free_layer(struct idr *idp, struct idr_layer *p) { + unsigned long flags; + /* * Depends on the return element being zeroed. */ - spin_lock(&idp->lock); + spin_lock_irqsave(&idp->lock, flags); __free_layer(idp, p); - spin_unlock(&idp->lock); + spin_unlock_irqrestore(&idp->lock, flags); } /** @@ -168,6 +171,7 @@ static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id) { struct idr_layer *p, *new; int layers, v, id; + unsigned long flags; id = starting_id; build_up: @@ -191,14 +195,14 @@ build_up: * The allocation failed. If we built part of * the structure tear it down. */ - spin_lock(&idp->lock); + spin_lock_irqsave(&idp->lock, flags); for (new = p; p && p != idp->top; new = p) { p = p->ary[0]; new->ary[0] = NULL; new->bitmap = new->count = 0; __free_layer(idp, new); } - spin_unlock(&idp->lock); + spin_unlock_irqrestore(&idp->lock, flags); return -1; } new->ary[0] = p; diff --git a/mm/memory.c b/mm/memory.c index dc0d82cf2a1..109e9866237 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -47,6 +47,7 @@ #include <linux/pagemap.h> #include <linux/rmap.h> #include <linux/module.h> +#include <linux/delayacct.h> #include <linux/init.h> #include <asm/pgalloc.h> @@ -1549,9 +1550,9 @@ gotten: flush_cache_page(vma, address, pte_pfn(orig_pte)); entry = mk_pte(new_page, vma->vm_page_prot); entry = maybe_mkwrite(pte_mkdirty(entry), vma); + lazy_mmu_prot_update(entry); ptep_establish(vma, address, page_table, entry); update_mmu_cache(vma, address, entry); - lazy_mmu_prot_update(entry); lru_cache_add_active(new_page); page_add_new_anon_rmap(new_page, vma, address); @@ -1934,6 +1935,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, migration_entry_wait(mm, pmd, address); goto out; } + delayacct_set_flag(DELAYACCT_PF_SWAPIN); page = lookup_swap_cache(entry); if (!page) { swapin_readahead(entry, address, vma); @@ -1946,6 +1948,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, page_table = pte_offset_map_lock(mm, pmd, address, &ptl); if (likely(pte_same(*page_table, orig_pte))) ret = VM_FAULT_OOM; + delayacct_clear_flag(DELAYACCT_PF_SWAPIN); goto unlock; } @@ -1955,6 +1958,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, grab_swap_token(); } + delayacct_clear_flag(DELAYACCT_PF_SWAPIN); mark_page_accessed(page); lock_page(page); diff --git a/mm/nommu.c b/mm/nommu.c index 5151c44a825..c576df71e3b 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1070,6 +1070,7 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long from, vma->vm_start = vma->vm_pgoff << PAGE_SHIFT; return 0; } +EXPORT_SYMBOL(remap_pfn_range); void swap_unplug_io_fn(struct backing_dev_info *bdi, struct page *page) { @@ -1090,6 +1091,7 @@ void unmap_mapping_range(struct address_space *mapping, int even_cows) { } +EXPORT_SYMBOL(unmap_mapping_range); /* * Check that a process has enough memory to allocate a new virtual diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 7b450798b45..266162d2ba2 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -340,7 +340,7 @@ void __vunmap(void *addr, int deallocate_pages) __free_page(area->pages[i]); } - if (area->nr_pages > PAGE_SIZE/sizeof(struct page *)) + if (area->flags & VM_VPAGES) vfree(area->pages); else kfree(area->pages); @@ -427,9 +427,10 @@ void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, area->nr_pages = nr_pages; /* Please note that the recursion is strictly bounded. */ - if (array_size > PAGE_SIZE) + if (array_size > PAGE_SIZE) { pages = __vmalloc_node(array_size, gfp_mask, PAGE_KERNEL, node); - else + area->flags |= VM_VPAGES; + } else pages = kmalloc_node(array_size, (gfp_mask & ~__GFP_HIGHMEM), node); area->pages = pages; if (!area->pages) { diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 44f6a181a75..476aa397850 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -257,11 +257,11 @@ nodata: } -static void skb_drop_fraglist(struct sk_buff *skb) +static void skb_drop_list(struct sk_buff **listp) { - struct sk_buff *list = skb_shinfo(skb)->frag_list; + struct sk_buff *list = *listp; - skb_shinfo(skb)->frag_list = NULL; + *listp = NULL; do { struct sk_buff *this = list; @@ -270,6 +270,11 @@ static void skb_drop_fraglist(struct sk_buff *skb) } while (list); } +static inline void skb_drop_fraglist(struct sk_buff *skb) +{ + skb_drop_list(&skb_shinfo(skb)->frag_list); +} + static void skb_clone_fraglist(struct sk_buff *skb) { struct sk_buff *list; @@ -830,41 +835,75 @@ free_skb: int ___pskb_trim(struct sk_buff *skb, unsigned int len) { + struct sk_buff **fragp; + struct sk_buff *frag; int offset = skb_headlen(skb); int nfrags = skb_shinfo(skb)->nr_frags; int i; + int err; + + if (skb_cloned(skb) && + unlikely((err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))) + return err; for (i = 0; i < nfrags; i++) { int end = offset + skb_shinfo(skb)->frags[i].size; - if (end > len) { - if (skb_cloned(skb)) { - if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) - return -ENOMEM; - } - if (len <= offset) { - put_page(skb_shinfo(skb)->frags[i].page); - skb_shinfo(skb)->nr_frags--; - } else { - skb_shinfo(skb)->frags[i].size = len - offset; - } + + if (end < len) { + offset = end; + continue; } - offset = end; + + if (len > offset) + skb_shinfo(skb)->frags[i++].size = len - offset; + + skb_shinfo(skb)->nr_frags = i; + + for (; i < nfrags; i++) + put_page(skb_shinfo(skb)->frags[i].page); + + if (skb_shinfo(skb)->frag_list) + skb_drop_fraglist(skb); + break; } - if (offset < len) { + for (fragp = &skb_shinfo(skb)->frag_list; (frag = *fragp); + fragp = &frag->next) { + int end = offset + frag->len; + + if (skb_shared(frag)) { + struct sk_buff *nfrag; + + nfrag = skb_clone(frag, GFP_ATOMIC); + if (unlikely(!nfrag)) + return -ENOMEM; + + nfrag->next = frag->next; + frag = nfrag; + *fragp = frag; + } + + if (end < len) { + offset = end; + continue; + } + + if (end > len && + unlikely((err = pskb_trim(frag, len - offset)))) + return err; + + if (frag->next) + skb_drop_list(&frag->next); + break; + } + + if (len > skb_headlen(skb)) { skb->data_len -= skb->len - len; skb->len = len; } else { - if (len <= skb_headlen(skb)) { - skb->len = len; - skb->data_len = 0; - skb->tail = skb->data + len; - if (skb_shinfo(skb)->frag_list && !skb_cloned(skb)) - skb_drop_fraglist(skb); - } else { - skb->data_len -= skb->len - len; - skb->len = len; - } + skb->len = len; + skb->data_len = 0; + skb->tail = skb->data + len; } return 0; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index e1a7dba2fa8..184c78ca79e 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -428,6 +428,9 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, goto drop; } + /* Remove any debris in the socket control block */ + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); + return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 34afe41fa2f..cd0a973b112 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -196,7 +196,7 @@ struct htb_class struct qdisc_rate_table *rate; /* rate table of the class itself */ struct qdisc_rate_table *ceil; /* ceiling rate (limits borrows too) */ long buffer,cbuffer; /* token bucket depth/rate */ - long mbuffer; /* max wait time */ + psched_tdiff_t mbuffer; /* max wait time */ long tokens,ctokens; /* current number of tokens */ psched_time_t t_c; /* checkpoint time */ }; @@ -1601,7 +1601,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, /* set class to be in HTB_CAN_SEND state */ cl->tokens = hopt->buffer; cl->ctokens = hopt->cbuffer; - cl->mbuffer = 60000000; /* 1min */ + cl->mbuffer = PSCHED_JIFFIE2US(HZ*60) /* 1min */ PSCHED_GET_TIME(cl->t_c); cl->cmode = HTB_CAN_SEND; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2e8b4dfcbc7..a91c961ba38 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -523,12 +523,16 @@ static int try_context_mount(struct super_block *sb, void *data) goto out_free; } - rc = may_context_mount_sb_relabel(sid, sbsec, tsec); - if (rc) - goto out_free; - - if (!fscontext) + if (!fscontext) { + rc = may_context_mount_sb_relabel(sid, sbsec, tsec); + if (rc) + goto out_free; sbsec->sid = sid; + } else { + rc = may_context_mount_inode_relabel(sid, sbsec, tsec); + if (rc) + goto out_free; + } sbsec->mntpoint_sid = sid; sbsec->behavior = SECURITY_FS_USE_MNTPOINT; |