appldata_os.c 8.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6
/*
 * arch/s390/appldata/appldata_os.c
 *
 * Data gathering module for Linux-VM Monitor Stream, Stage 1.
 * Collects misc. OS related data (CPU utilization, running processes).
 *
G
Gerald Schaefer 已提交
7
 * Copyright (C) 2003,2006 IBM Corporation, IBM Deutschland Entwicklung GmbH.
L
Linus Torvalds 已提交
8
 *
G
Gerald Schaefer 已提交
9
 * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17 18
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/netdevice.h>
#include <linux/sched.h>
G
Gerald Schaefer 已提交
19
#include <asm/appldata.h>
L
Linus Torvalds 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#include <asm/smp.h>

#include "appldata.h"


#define MY_PRINT_NAME	"appldata_os"		/* for debug messages, etc. */
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)

/*
 * OS data
 *
 * This is accessed as binary data by z/VM. If changes to it can't be avoided,
 * the structure version (product ID, see appldata_base.c) needs to be changed
 * as well and all documentation and z/VM applications using it must be
 * updated.
 *
 * The record layout is documented in the Linux for zSeries Device Drivers
 * book:
 * http://oss.software.ibm.com/developerworks/opensource/linux390/index.shtml
 */
struct appldata_os_per_cpu {
	u32 per_cpu_user;	/* timer ticks spent in user mode   */
	u32 per_cpu_nice;	/* ... spent with modified priority */
	u32 per_cpu_system;	/* ... spent in kernel mode         */
	u32 per_cpu_idle;	/* ... spent in idle mode           */

G
Gerald Schaefer 已提交
47
	/* New in 2.6 */
L
Linus Torvalds 已提交
48 49 50
	u32 per_cpu_irq;	/* ... spent in interrupts          */
	u32 per_cpu_softirq;	/* ... spent in softirqs            */
	u32 per_cpu_iowait;	/* ... spent while waiting for I/O  */
G
Gerald Schaefer 已提交
51 52 53 54

	/* New in modification level 01 */
	u32 per_cpu_steal;	/* ... stolen by hypervisor	    */
	u32 cpu_id;		/* number of this CPU		    */
55
} __attribute__((packed));
L
Linus Torvalds 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

struct appldata_os_data {
	u64 timestamp;
	u32 sync_count_1;	/* after VM collected the record data, */
	u32 sync_count_2;	/* sync_count_1 and sync_count_2 should be the
				   same. If not, the record has been updated on
				   the Linux side while VM was collecting the
				   (possibly corrupt) data */

	u32 nr_cpus;		/* number of (virtual) CPUs        */
	u32 per_cpu_size;	/* size of the per-cpu data struct */
	u32 cpu_offset;		/* offset of the first per-cpu data struct */

	u32 nr_running;		/* number of runnable threads      */
	u32 nr_threads;		/* number of threads               */
	u32 avenrun[3];		/* average nr. of running processes during */
				/* the last 1, 5 and 15 minutes */

G
Gerald Schaefer 已提交
74
	/* New in 2.6 */
L
Linus Torvalds 已提交
75 76 77 78 79
	u32 nr_iowait;		/* number of blocked threads
				   (waiting for I/O)               */

	/* per cpu data */
	struct appldata_os_per_cpu os_cpu[0];
80
} __attribute__((packed));
L
Linus Torvalds 已提交
81 82 83

static struct appldata_os_data *appldata_os_data;

G
Gerald Schaefer 已提交
84 85 86 87 88 89 90 91
static struct appldata_ops ops = {
	.ctl_nr    = CTL_APPLDATA_OS,
	.name	   = "os",
	.record_nr = APPLDATA_RECORD_OS_ID,
	.owner	   = THIS_MODULE,
	.mod_lvl   = {0xF0, 0xF1},		/* EBCDIC "01" */
};

L
Linus Torvalds 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

static inline void appldata_print_debug(struct appldata_os_data *os_data)
{
	int a0, a1, a2, i;

	P_DEBUG("--- OS - RECORD ---\n");
	P_DEBUG("nr_threads   = %u\n", os_data->nr_threads);
	P_DEBUG("nr_running   = %u\n", os_data->nr_running);
	P_DEBUG("nr_iowait    = %u\n", os_data->nr_iowait);
	P_DEBUG("avenrun(int) = %8x / %8x / %8x\n", os_data->avenrun[0],
		os_data->avenrun[1], os_data->avenrun[2]);
	a0 = os_data->avenrun[0];
	a1 = os_data->avenrun[1];
	a2 = os_data->avenrun[2];
	P_DEBUG("avenrun(float) = %d.%02d / %d.%02d / %d.%02d\n",
		LOAD_INT(a0), LOAD_FRAC(a0), LOAD_INT(a1), LOAD_FRAC(a1),
		LOAD_INT(a2), LOAD_FRAC(a2));

	P_DEBUG("nr_cpus = %u\n", os_data->nr_cpus);
	for (i = 0; i < os_data->nr_cpus; i++) {
		P_DEBUG("cpu%u : user = %u, nice = %u, system = %u, "
G
Gerald Schaefer 已提交
113 114 115
			"idle = %u, irq = %u, softirq = %u, iowait = %u, "
			"steal = %u\n",
				os_data->os_cpu[i].cpu_id,
L
Linus Torvalds 已提交
116 117 118 119 120 121
				os_data->os_cpu[i].per_cpu_user,
				os_data->os_cpu[i].per_cpu_nice,
				os_data->os_cpu[i].per_cpu_system,
				os_data->os_cpu[i].per_cpu_idle,
				os_data->os_cpu[i].per_cpu_irq,
				os_data->os_cpu[i].per_cpu_softirq,
G
Gerald Schaefer 已提交
122 123
				os_data->os_cpu[i].per_cpu_iowait,
				os_data->os_cpu[i].per_cpu_steal);
L
Linus Torvalds 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137
	}

	P_DEBUG("sync_count_1 = %u\n", os_data->sync_count_1);
	P_DEBUG("sync_count_2 = %u\n", os_data->sync_count_2);
	P_DEBUG("timestamp    = %lX\n", os_data->timestamp);
}

/*
 * appldata_get_os_data()
 *
 * gather OS data
 */
static void appldata_get_os_data(void *data)
{
G
Gerald Schaefer 已提交
138
	int i, j, rc;
L
Linus Torvalds 已提交
139
	struct appldata_os_data *os_data;
G
Gerald Schaefer 已提交
140
	unsigned int new_size;
L
Linus Torvalds 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154

	os_data = data;
	os_data->sync_count_1++;

	os_data->nr_threads = nr_threads;
	os_data->nr_running = nr_running();
	os_data->nr_iowait  = nr_iowait();
	os_data->avenrun[0] = avenrun[0] + (FIXED_1/200);
	os_data->avenrun[1] = avenrun[1] + (FIXED_1/200);
	os_data->avenrun[2] = avenrun[2] + (FIXED_1/200);

	j = 0;
	for_each_online_cpu(i) {
		os_data->os_cpu[j].per_cpu_user =
155
			cputime_to_jiffies(kstat_cpu(i).cpustat.user);
L
Linus Torvalds 已提交
156
		os_data->os_cpu[j].per_cpu_nice =
157
			cputime_to_jiffies(kstat_cpu(i).cpustat.nice);
L
Linus Torvalds 已提交
158
		os_data->os_cpu[j].per_cpu_system =
159
			cputime_to_jiffies(kstat_cpu(i).cpustat.system);
L
Linus Torvalds 已提交
160
		os_data->os_cpu[j].per_cpu_idle =
161
			cputime_to_jiffies(kstat_cpu(i).cpustat.idle);
L
Linus Torvalds 已提交
162
		os_data->os_cpu[j].per_cpu_irq =
163
			cputime_to_jiffies(kstat_cpu(i).cpustat.irq);
L
Linus Torvalds 已提交
164
		os_data->os_cpu[j].per_cpu_softirq =
165
			cputime_to_jiffies(kstat_cpu(i).cpustat.softirq);
L
Linus Torvalds 已提交
166
		os_data->os_cpu[j].per_cpu_iowait =
167
			cputime_to_jiffies(kstat_cpu(i).cpustat.iowait);
G
Gerald Schaefer 已提交
168 169 170
		os_data->os_cpu[j].per_cpu_steal =
			cputime_to_jiffies(kstat_cpu(i).cpustat.steal);
		os_data->os_cpu[j].cpu_id = i;
L
Linus Torvalds 已提交
171 172 173
		j++;
	}

G
Gerald Schaefer 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
	os_data->nr_cpus = j;

	new_size = sizeof(struct appldata_os_data) +
		   (os_data->nr_cpus * sizeof(struct appldata_os_per_cpu));
	if (ops.size != new_size) {
		if (ops.active) {
			rc = appldata_diag(APPLDATA_RECORD_OS_ID,
					   APPLDATA_START_INTERVAL_REC,
					   (unsigned long) ops.data, new_size,
					   ops.mod_lvl);
			if (rc != 0) {
				P_ERROR("os: START NEW DIAG 0xDC failed, "
					"return code: %d, new size = %i\n", rc,
					new_size);
				P_INFO("os: stopping old record now\n");
			} else
				P_INFO("os: new record size = %i\n", new_size);

			rc = appldata_diag(APPLDATA_RECORD_OS_ID,
					   APPLDATA_STOP_REC,
					   (unsigned long) ops.data, ops.size,
					   ops.mod_lvl);
			if (rc != 0)
				P_ERROR("os: STOP OLD DIAG 0xDC failed, "
					"return code: %d, old size = %i\n", rc,
					ops.size);
			else
				P_INFO("os: old record size = %i stopped\n",
					ops.size);
		}
		ops.size = new_size;
	}
L
Linus Torvalds 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
	os_data->timestamp = get_clock();
	os_data->sync_count_2++;
#ifdef APPLDATA_DEBUG
	appldata_print_debug(os_data);
#endif
}


/*
 * appldata_os_init()
 *
 * init data, register ops
 */
static int __init appldata_os_init(void)
{
G
Gerald Schaefer 已提交
221
	int rc, max_size;
L
Linus Torvalds 已提交
222

G
Gerald Schaefer 已提交
223 224 225 226 227
	max_size = sizeof(struct appldata_os_data) +
		   (NR_CPUS * sizeof(struct appldata_os_per_cpu));
	if (max_size > APPLDATA_MAX_REC_SIZE) {
		P_ERROR("Max. size of OS record = %i, bigger than maximum "
			"record size (%i)\n", max_size, APPLDATA_MAX_REC_SIZE);
L
Linus Torvalds 已提交
228 229 230
		rc = -ENOMEM;
		goto out;
	}
G
Gerald Schaefer 已提交
231
	P_DEBUG("max. sizeof(os) = %i, sizeof(os_cpu) = %lu\n", max_size,
L
Linus Torvalds 已提交
232 233
		sizeof(struct appldata_os_per_cpu));

G
Gerald Schaefer 已提交
234
	appldata_os_data = kzalloc(max_size, GFP_DMA);
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243 244 245 246
	if (appldata_os_data == NULL) {
		P_ERROR("No memory for %s!\n", ops.name);
		rc = -ENOMEM;
		goto out;
	}

	appldata_os_data->per_cpu_size = sizeof(struct appldata_os_per_cpu);
	appldata_os_data->cpu_offset   = offsetof(struct appldata_os_data,
							os_cpu);
	P_DEBUG("cpu offset = %u\n", appldata_os_data->cpu_offset);

	ops.data = appldata_os_data;
G
Gerald Schaefer 已提交
247
	ops.callback  = &appldata_get_os_data;
L
Linus Torvalds 已提交
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
	rc = appldata_register_ops(&ops);
	if (rc != 0) {
		P_ERROR("Error registering ops, rc = %i\n", rc);
		kfree(appldata_os_data);
	} else {
		P_DEBUG("%s-ops registered!\n", ops.name);
	}
out:
	return rc;
}

/*
 * appldata_os_exit()
 *
 * unregister ops
 */
static void __exit appldata_os_exit(void)
{
	appldata_unregister_ops(&ops);
	kfree(appldata_os_data);
	P_DEBUG("%s-ops unregistered!\n", ops.name);
}


module_init(appldata_os_init);
module_exit(appldata_os_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gerald Schaefer");
MODULE_DESCRIPTION("Linux-VM Monitor Stream, OS statistics");