aboutsummaryrefslogtreecommitdiffstats
path: root/fs/dcookies.c
blob: 855d4b1d619aec6244b182c3669b6efe2ec1f726 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/*
 * dcookies.c
 *
 * Copyright 2002 John Levon <levon@movementarian.org>
 *
 * Persistent cookie-path mappings. These are used by
 * profilers to convert a per-task EIP value into something
 * non-transitory that can be processed at a later date.
 * This is done by locking the dentry/vfsmnt pair in the
 * kernel until released by the tasks needing the persistent
 * objects. The tag is simply an unsigned long that refers
 * to the pair and can be looked up from userspace.
 */

#include <linux/syscalls.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/mount.h>
#include <linux/capability.h>
#include <linux/dcache.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/dcookies.h>
#include <linux/mutex.h>
#include <linux/path.h>
#include <asm/uaccess.h>

/* The dcookies are allocated from a kmem_cache and
 * hashed onto a small number of lists. None of the
 * code here is particularly performance critical
 */
struct dcookie_struct {
	struct path path;
	struct list_head hash_list;
};

static LIST_HEAD(dcookie_users);
static DEFINE_MUTEX(dcookie_mutex);
static struct kmem_cache *dcookie_cache __read_mostly;
static struct list_head *dcookie_hashtable __read_mostly;
static size_t hash_size __read_mostly;

static inline int is_live(void)
{
	return !(list_empty(&dcookie_users));
}


/* The dentry is locked, its address will do for the cookie */
static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
{
	return (unsigned long)dcs->path.dentry;
}


static size_t dcookie_hash(unsigned long dcookie)
{
	return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1);
}


static struct dcookie_struct * find_dcookie(unsigned long dcookie)
{
	struct dcookie_struct *found = NULL;
	struct dcookie_struct * dcs;
	struct list_head * pos;
	struct list_head * list;

	list = dcookie_hashtable + dcookie_hash(dcookie);

	list_for_each(pos, list) {
		dcs = list_entry(pos, struct dcookie_struct, hash_list);
		if (dcookie_value(dcs) == dcookie) {
			found = dcs;
			break;
		}
	}

	return found;
}


static void hash_dcookie(struct dcookie_struct * dcs)
{
	struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs));
	list_add(&dcs->hash_list, list);
}


static struct dcookie_struct *alloc_dcookie(struct path *path)
{
	struct dcookie_struct *dcs = kmem_cache_alloc(dcookie_cache,
							GFP_KERNEL);
	if (!dcs)
		return NULL;

	path->dentry->d_cookie = dcs;
	dcs->path = *path;
	path_get(path);
	hash_dcookie(dcs);
	return dcs;
}


/* This is the main kernel-side routine that retrieves the cookie
 * value for a dentry/vfsmnt pair.
 */
int get_dcookie(struct path *path, unsigned long *cookie)
{
	int err = 0;
	struct dcookie_struct * dcs;

	mutex_lock(&dcookie_mutex);

	if (!is_live()) {
		err = -EINVAL;
		goto out;
	}

	dcs = path->dentry->d_cookie;

	if (!dcs)
		dcs = alloc_dcookie(path);

	if (!dcs) {
		err = -ENOMEM;
		goto out;
	}

	*cookie = dcookie_value(dcs);

out:
	mutex_unlock(&dcookie_mutex);
	return err;
}


/* And here is where the userspace process can look up the cookie value
 * to retrieve the path.
 */
asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len)
{
	unsigned long cookie = (unsigned long)cookie64;
	int err = -EINVAL;
	char * kbuf;
	char * path;
	size_t pathlen;
	struct dcookie_struct * dcs;

	/* we could leak path information to users
	 * without dir read permission without this
	 */
	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	mutex_lock(&dcookie_mutex);

	if (!is_live()) {
		err = -EINVAL;
		goto out;
	}

	if (!(dcs = find_dcookie(cookie)))
		goto out;

	err = -ENOMEM;
	kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
	if (!kbuf)
		goto out;

	/* FIXME: (deleted) ? */
	path = d_path(&dcs->path, kbuf, PAGE_SIZE);

	if (IS_ERR(path)) {
		err = PTR_ERR(path);
		goto out_free;
	}

	err = -ERANGE;
 
	pathlen = kbuf + PAGE_SIZE - path;
	if (pathlen <= len) {
		err = pathlen;
		if (copy_to_user(buf, path, pathlen))
			err = -EFAULT;
	}

out_free:
	kfree(kbuf);
out:
	mutex_unlock(&dcookie_mutex);
	return err;
}


static int dcookie_init(void)
{
	struct list_head * d;
	unsigned int i, hash_bits;
	int err = -ENOMEM;

	dcookie_cache = kmem_cache_create("dcookie_cache",
		sizeof(struct dcookie_struct),
		0, 0, NULL);

	if (!dcookie_cache)
		goto out;

	dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL);
	if (!dcookie_hashtable)
		goto out_kmem;

	err = 0;

	/*
	 * Find the power-of-two list-heads that can fit into the allocation..
	 * We don't guarantee that "sizeof(struct list_head)" is necessarily
	 * a power-of-two.
	 */
	hash_size = PAGE_SIZE / sizeof(struct list_head);
	hash_bits = 0;
	do {
		hash_bits++;
	} while ((hash_size >> hash_bits) != 0);
	hash_bits--;

	/*
	 * Re-calculate the actual number of entries and the mask
	 * from the number of bits we can fit.
	 */
	hash_size = 1UL << hash_bits;

	/* And initialize the newly allocated array */
	d = dcookie_hashtable;
	i = hash_size;
	do {
		INIT_LIST_HEAD(d);
		d++;
		i--;
	} while (i);

out:
	return err;
out_kmem:
	kmem_cache_destroy(dcookie_cache);
	goto out;
}


static void free_dcookie(struct dcookie_struct * dcs)
{
	dcs->path.dentry->d_cookie = NULL;
	path_put(&dcs->path);
	kmem_cache_free(dcookie_cache, dcs);
}


static void dcookie_exit(void)
{
	struct list_head * list;
	struct list_head * pos;
	struct list_head * pos2;
	struct dcookie_struct * dcs;
	size_t i;

	for (i = 0; i < hash_size; ++i) {
		list = dcookie_hashtable + i;
		list_for_each_safe(pos, pos2, list) {
			dcs = list_entry(pos, struct dcookie_struct, hash_list);
			list_del(&dcs->hash_list);
			free_dcookie(dcs);
		}
	}

	kfree(dcookie_hashtable);
	kmem_cache_destroy(dcookie_cache);
}


struct dcookie_user {
	struct list_head next;
};
 
struct dcookie_user * dcookie_register(void)
{
	struct dcookie_user * user;

	mutex_lock(&dcookie_mutex);

	user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL);
	if (!user)
		goto out;

	if (!is_live() && dcookie_init())
		goto out_free;

	list_add(&user->next, &dcookie_users);

out:
	mutex_unlock(&dcookie_mutex);
	return user;
out_free:
	kfree(user);
	user = NULL;
	goto out;
}


void dcookie_unregister(struct dcookie_user * user)
{
	mutex_lock(&dcookie_mutex);

	list_del(&user->next);
	kfree(user);

	if (!is_live())
		dcookie_exit();

	mutex_unlock(&dcookie_mutex);
}

EXPORT_SYMBOL_GPL(dcookie_register);
EXPORT_SYMBOL_GPL(dcookie_unregister);
EXPORT_SYMBOL_GPL(get_dcookie);
1257' href='#n1257'>1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
/*
 *  PS3 virtual uart
 *
 *  Copyright (C) 2006 Sony Computer Entertainment Inc.
 *  Copyright 2006 Sony Corp.
 *
 *  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; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
#include <asm/ps3.h>

#include <asm/firmware.h>
#include <asm/lv1call.h>

#include "vuart.h"

MODULE_AUTHOR("Sony Corporation");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("PS3 vuart");

/**
 * vuart - An inter-partition data link service.
 *  port 0: PS3 AV Settings.
 *  port 2: PS3 System Manager.
 *
 * The vuart provides a bi-directional byte stream data link between logical
 * partitions.  Its primary role is as a communications link between the guest
 * OS and the system policy module.  The current HV does not support any
 * connections other than those listed.
 */

enum {PORT_COUNT = 3,};

enum vuart_param {
	PARAM_TX_TRIGGER = 0,
	PARAM_RX_TRIGGER = 1,
	PARAM_INTERRUPT_MASK = 2,
	PARAM_RX_BUF_SIZE = 3, /* read only */
	PARAM_RX_BYTES = 4, /* read only */
	PARAM_TX_BUF_SIZE = 5, /* read only */
	PARAM_TX_BYTES = 6, /* read only */
	PARAM_INTERRUPT_STATUS = 7, /* read only */
};

enum vuart_interrupt_bit {
	INTERRUPT_BIT_TX = 0,
	INTERRUPT_BIT_RX = 1,
	INTERRUPT_BIT_DISCONNECT = 2,
};

enum vuart_interrupt_mask {
	INTERRUPT_MASK_TX = 1,
	INTERRUPT_MASK_RX = 2,
	INTERRUPT_MASK_DISCONNECT = 4,
};

/**
 * struct ps3_vuart_port_priv - private vuart device data.
 */

struct ps3_vuart_port_priv {
	u64 interrupt_mask;

	struct {
		spinlock_t lock;
		struct list_head head;
	} tx_list;
	struct {
		struct ps3_vuart_work work;
		unsigned long bytes_held;
		spinlock_t lock;
		struct list_head head;
	} rx_list;
	struct ps3_vuart_stats stats;
};

static struct ps3_vuart_port_priv *to_port_priv(
	struct ps3_system_bus_device *dev)
{
	BUG_ON(!dev);
	BUG_ON(!dev->driver_priv);
	return (struct ps3_vuart_port_priv *)dev->driver_priv;
}

/**
 * struct ports_bmp - bitmap indicating ports needing service.
 *
 * A 256 bit read only bitmap indicating ports needing service.  Do not write
 * to these bits.  Must not cross a page boundary.
 */

struct ports_bmp {
	u64 status;
	u64 unused[3];
} __attribute__((aligned(32)));

#define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__)
static void __maybe_unused _dump_ports_bmp(
	const struct ports_bmp *bmp, const char *func, int line)
{
	pr_debug("%s:%d: ports_bmp: %016llxh\n", func, line, bmp->status);
}

#define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__)
static void __maybe_unused _dump_port_params(unsigned int port_number,
	const char *func, int line)
{
#if defined(DEBUG)
	static const char *strings[] = {
		"tx_trigger      ",
		"rx_trigger      ",
		"interrupt_mask  ",
		"rx_buf_size     ",
		"rx_bytes        ",
		"tx_buf_size     ",
		"tx_bytes        ",
		"interrupt_status",
	};
	int result;
	unsigned int i;
	u64 value;

	for (i = 0; i < ARRAY_SIZE(strings); i++) {
		result = lv1_get_virtual_uart_param(port_number, i, &value);

		if (result) {
			pr_debug("%s:%d: port_%u: %s failed: %s\n", func, line,
				port_number, strings[i], ps3_result(result));
			continue;
		}
		pr_debug("%s:%d: port_%u: %s = %lxh\n",
			func, line, port_number, strings[i], value);
	}
#endif
}

struct vuart_triggers {
	unsigned long rx;
	unsigned long tx;
};

int ps3_vuart_get_triggers(struct ps3_system_bus_device *dev,
	struct vuart_triggers *trig)
{
	int result;
	u64 size;
	u64 val;
	u64 tx;

	result = lv1_get_virtual_uart_param(dev->port_number,
		PARAM_TX_TRIGGER, &tx);
	trig->tx = tx;

	if (result) {
		dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n",
			__func__, __LINE__, ps3_result(result));
		return result;
	}

	result = lv1_get_virtual_uart_param(dev->port_number,
		PARAM_RX_BUF_SIZE, &size);

	if (result) {
		dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n",
			__func__, __LINE__, ps3_result(result));
		return result;
	}

	result = lv1_get_virtual_uart_param(dev->port_number,
		PARAM_RX_TRIGGER, &val);

	if (result) {
		dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n",
			__func__, __LINE__, ps3_result(result));
		return result;
	}

	trig->rx = size - val;

	dev_dbg(&dev->core, "%s:%d: tx %lxh, rx %lxh\n", __func__, __LINE__,
		trig->tx, trig->rx);

	return result;
}

int ps3_vuart_set_triggers(struct ps3_system_bus_device *dev, unsigned int tx,
	unsigned int rx)
{
	int result;
	u64 size;

	result = lv1_set_virtual_uart_param(dev->port_number,
		PARAM_TX_TRIGGER, tx);

	if (result) {
		dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n",
			__func__, __LINE__, ps3_result(result));
		return result;
	}

	result = lv1_get_virtual_uart_param(dev->port_number,
		PARAM_RX_BUF_SIZE, &size);

	if (result) {
		dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n",
			__func__, __LINE__, ps3_result(result));
		return result;
	}

	result = lv1_set_virtual_uart_param(dev->port_number,
		PARAM_RX_TRIGGER, size - rx);

	if (result) {
		dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n",
			__func__, __LINE__, ps3_result(result));
		return result;
	}

	dev_dbg(&dev->core, "%s:%d: tx %xh, rx %xh\n", __func__, __LINE__,
		tx, rx);

	return result;
}

static int ps3_vuart_get_rx_bytes_waiting(struct ps3_system_bus_device *dev,
	u64 *bytes_waiting)
{
	int result;

	result = lv1_get_virtual_uart_param(dev->port_number,
		PARAM_RX_BYTES, bytes_waiting);

	if (result)
		dev_dbg(&dev->core, "%s:%d: rx_bytes failed: %s\n",
			__func__, __LINE__, ps3_result(result));

	dev_dbg(&dev->core, "%s:%d: %llxh\n", __func__, __LINE__,
		*bytes_waiting);
	return result;
}

/**
 * ps3_vuart_set_interrupt_mask - Enable/disable the port interrupt sources.
 * @dev: The struct ps3_system_bus_device instance.
 * @bmp: Logical OR of enum vuart_interrupt_mask values. A zero bit disables.
 */

static int ps3_vuart_set_interrupt_mask(struct ps3_system_bus_device *dev,
	unsigned long mask)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask);

	priv->interrupt_mask = mask;

	result = lv1_set_virtual_uart_param(dev->port_number,
		PARAM_INTERRUPT_MASK, priv->interrupt_mask);

	if (result)
		dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n",
			__func__, __LINE__, ps3_result(result));

	return result;
}

static int ps3_vuart_get_interrupt_status(struct ps3_system_bus_device *dev,
	unsigned long *status)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	u64 tmp;

	result = lv1_get_virtual_uart_param(dev->port_number,
		PARAM_INTERRUPT_STATUS, &tmp);

	if (result)
		dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n",
			__func__, __LINE__, ps3_result(result));

	*status = tmp & priv->interrupt_mask;

	dev_dbg(&dev->core, "%s:%d: m %llxh, s %llxh, m&s %lxh\n",
		__func__, __LINE__, priv->interrupt_mask, tmp, *status);

	return result;
}

int ps3_vuart_enable_interrupt_tx(struct ps3_system_bus_device *dev)
{
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	return (priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0
		: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
		| INTERRUPT_MASK_TX);
}

int ps3_vuart_enable_interrupt_rx(struct ps3_system_bus_device *dev)
{
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	return (priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0
		: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
		| INTERRUPT_MASK_RX);
}

int ps3_vuart_enable_interrupt_disconnect(struct ps3_system_bus_device *dev)
{
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0
		: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
		| INTERRUPT_MASK_DISCONNECT);
}

int ps3_vuart_disable_interrupt_tx(struct ps3_system_bus_device *dev)
{
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	return (priv->interrupt_mask & INTERRUPT_MASK_TX)
		? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
		& ~INTERRUPT_MASK_TX) : 0;
}

int ps3_vuart_disable_interrupt_rx(struct ps3_system_bus_device *dev)
{
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	return (priv->interrupt_mask & INTERRUPT_MASK_RX)
		? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
		& ~INTERRUPT_MASK_RX) : 0;
}

int ps3_vuart_disable_interrupt_disconnect(struct ps3_system_bus_device *dev)
{
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT)
		? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
		& ~INTERRUPT_MASK_DISCONNECT) : 0;
}

/**
 * ps3_vuart_raw_write - Low level write helper.
 * @dev: The struct ps3_system_bus_device instance.
 *
 * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write.
 */

static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev,
	const void *buf, unsigned int bytes, u64 *bytes_written)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	result = lv1_write_virtual_uart(dev->port_number,
		ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written);

	if (result) {
		dev_dbg(&dev->core, "%s:%d: lv1_write_virtual_uart failed: "
			"%s\n", __func__, __LINE__, ps3_result(result));
		return result;
	}

	priv->stats.bytes_written += *bytes_written;

	dev_dbg(&dev->core, "%s:%d: wrote %llxh/%xh=>%lxh\n", __func__, __LINE__,
		*bytes_written, bytes, priv->stats.bytes_written);

	return result;
}

/**
 * ps3_vuart_raw_read - Low level read helper.
 * @dev: The struct ps3_system_bus_device instance.
 *
 * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read.
 */

static int ps3_vuart_raw_read(struct ps3_system_bus_device *dev, void *buf,
	unsigned int bytes, u64 *bytes_read)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);

	dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes);

	result = lv1_read_virtual_uart(dev->port_number,
		ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read);

	if (result) {
		dev_dbg(&dev->core, "%s:%d: lv1_read_virtual_uart failed: %s\n",
			__func__, __LINE__, ps3_result(result));
		return result;
	}

	priv->stats.bytes_read += *bytes_read;

	dev_dbg(&dev->core, "%s:%d: read %llxh/%xh=>%lxh\n", __func__, __LINE__,
		*bytes_read, bytes, priv->stats.bytes_read);

	return result;
}

/**
 * ps3_vuart_clear_rx_bytes - Discard bytes received.
 * @dev: The struct ps3_system_bus_device instance.
 * @bytes: Max byte count to discard, zero = all pending.
 *
 * Used to clear pending rx interrupt source.  Will not block.
 */

void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev,
	unsigned int bytes)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	u64 bytes_waiting;
	void *tmp;

	result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes_waiting);

	BUG_ON(result);

	bytes = bytes ? min(bytes, (unsigned int)bytes_waiting) : bytes_waiting;

	dev_dbg(&dev->core, "%s:%d: %u\n", __func__, __LINE__, bytes);

	if (!bytes)
		return;

	/* Add some extra space for recently arrived data. */

	bytes += 128;

	tmp = kmalloc(bytes, GFP_KERNEL);

	if (!tmp)
		return;

	ps3_vuart_raw_read(dev, tmp, bytes, &bytes_waiting);

	kfree(tmp);

	/* Don't include these bytes in the stats. */

	priv->stats.bytes_read -= bytes_waiting;
}
EXPORT_SYMBOL_GPL(ps3_vuart_clear_rx_bytes);

/**
 * struct list_buffer - An element for a port device fifo buffer list.
 */

struct list_buffer {
	struct list_head link;
	const unsigned char *head;
	const unsigned char *tail;
	unsigned long dbg_number;
	unsigned char data[];
};

/**
 * ps3_vuart_write - the entry point for writing data to a port
 * @dev: The struct ps3_system_bus_device instance.
 *
 * If the port is idle on entry as much of the incoming data is written to
 * the port as the port will accept.  Otherwise a list buffer is created
 * and any remaning incoming data is copied to that buffer.  The buffer is
 * then enqueued for transmision via the transmit interrupt.
 */

int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf,
	unsigned int bytes)
{
	static unsigned long dbg_number;
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	unsigned long flags;
	struct list_buffer *lb;

	dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
		bytes, bytes);

	spin_lock_irqsave(&priv->tx_list.lock, flags);

	if (list_empty(&priv->tx_list.head)) {
		u64 bytes_written;

		result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written);

		spin_unlock_irqrestore(&priv->tx_list.lock, flags);

		if (result) {
			dev_dbg(&dev->core,
				"%s:%d: ps3_vuart_raw_write failed\n",
				__func__, __LINE__);
			return result;
		}

		if (bytes_written == bytes) {
			dev_dbg(&dev->core, "%s:%d: wrote %xh bytes\n",
				__func__, __LINE__, bytes);
			return 0;
		}

		bytes -= bytes_written;
		buf += bytes_written;
	} else
		spin_unlock_irqrestore(&priv->tx_list.lock, flags);

	lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL);

	if (!lb)
		return -ENOMEM;

	memcpy(lb->data, buf, bytes);
	lb->head = lb->data;
	lb->tail = lb->data + bytes;
	lb->dbg_number = ++dbg_number;

	spin_lock_irqsave(&priv->tx_list.lock, flags);
	list_add_tail(&lb->link, &priv->tx_list.head);
	ps3_vuart_enable_interrupt_tx(dev);
	spin_unlock_irqrestore(&priv->tx_list.lock, flags);

	dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n",
		__func__, __LINE__, lb->dbg_number, bytes);

	return 0;
}
EXPORT_SYMBOL_GPL(ps3_vuart_write);

/**
 * ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list.
 * @dev: The struct ps3_system_bus_device instance.
 * @bytes_queued: Number of bytes queued to the buffer list.
 *
 * Must be called with priv->rx_list.lock held.
 */

static int ps3_vuart_queue_rx_bytes(struct ps3_system_bus_device *dev,
	u64 *bytes_queued)
{
	static unsigned long dbg_number;
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	struct list_buffer *lb;
	u64 bytes;

	*bytes_queued = 0;

	result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes);
	BUG_ON(result);

	if (result)
		return -EIO;

	if (!bytes)
		return 0;

	/* Add some extra space for recently arrived data. */

	bytes += 128;

	lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC);

	if (!lb)
		return -ENOMEM;

	ps3_vuart_raw_read(dev, lb->data, bytes, &bytes);

	lb->head = lb->data;
	lb->tail = lb->data + bytes;
	lb->dbg_number = ++dbg_number;

	list_add_tail(&lb->link, &priv->rx_list.head);
	priv->rx_list.bytes_held += bytes;

	dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %llxh bytes\n",
		__func__, __LINE__, lb->dbg_number, bytes);

	*bytes_queued = bytes;

	return 0;
}

/**
 * ps3_vuart_read - The entry point for reading data from a port.
 *
 * Queue data waiting at the port, and if enough bytes to satisfy the request
 * are held in the buffer list those bytes are dequeued and copied to the
 * caller's buffer.  Emptied list buffers are retiered.  If the request cannot
 * be statified by bytes held in the list buffers -EAGAIN is returned.
 */

int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf,
	unsigned int bytes)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	unsigned long flags;
	struct list_buffer *lb, *n;
	unsigned long bytes_read;

	dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
		bytes, bytes);

	spin_lock_irqsave(&priv->rx_list.lock, flags);

	/* Queue rx bytes here for polled reads. */

	while (priv->rx_list.bytes_held < bytes) {
		u64 tmp;

		result = ps3_vuart_queue_rx_bytes(dev, &tmp);
		if (result || !tmp) {
			dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n",
				__func__, __LINE__,
				bytes - priv->rx_list.bytes_held);
			spin_unlock_irqrestore(&priv->rx_list.lock, flags);
			return -EAGAIN;
		}
	}

	list_for_each_entry_safe(lb, n, &priv->rx_list.head, link) {
		bytes_read = min((unsigned int)(lb->tail - lb->head), bytes);

		memcpy(buf, lb->head, bytes_read);
		buf += bytes_read;
		bytes -= bytes_read;
		priv->rx_list.bytes_held -= bytes_read;

		if (bytes_read < lb->tail - lb->head) {
			lb->head += bytes_read;
			dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh "
				"bytes\n", __func__, __LINE__, lb->dbg_number,
				bytes_read);
			spin_unlock_irqrestore(&priv->rx_list.lock, flags);
			return 0;
		}

		dev_dbg(&dev->core, "%s:%d: buf_%lu: free, dequeued %lxh "
			"bytes\n", __func__, __LINE__, lb->dbg_number,
			bytes_read);

		list_del(&lb->link);
		kfree(lb);
	}

	spin_unlock_irqrestore(&priv->rx_list.lock, flags);
	return 0;
}
EXPORT_SYMBOL_GPL(ps3_vuart_read);

/**
 * ps3_vuart_work - Asynchronous read handler.
 */

static void ps3_vuart_work(struct work_struct *work)
{
	struct ps3_system_bus_device *dev =
		ps3_vuart_work_to_system_bus_dev(work);
	struct ps3_vuart_port_driver *drv =
		ps3_system_bus_dev_to_vuart_drv(dev);

	BUG_ON(!drv);
	drv->work(dev);
}

int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes)
{
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	unsigned long flags;

	if (priv->rx_list.work.trigger) {
		dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n",
			__func__, __LINE__);
		return -EAGAIN;
	}

	BUG_ON(!bytes);

	PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work);

	spin_lock_irqsave(&priv->rx_list.lock, flags);
	if (priv->rx_list.bytes_held >= bytes) {
		dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n",
			__func__, __LINE__, bytes);
		schedule_work(&priv->rx_list.work.work);
		spin_unlock_irqrestore(&priv->rx_list.lock, flags);
		return 0;
	}

	priv->rx_list.work.trigger = bytes;
	spin_unlock_irqrestore(&priv->rx_list.lock, flags);

	dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__,
		__LINE__, bytes, bytes);

	return 0;
}
EXPORT_SYMBOL_GPL(ps3_vuart_read_async);

void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev)
{
	to_port_priv(dev)->rx_list.work.trigger = 0;
}
EXPORT_SYMBOL_GPL(ps3_vuart_cancel_async);

/**
 * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler
 *
 * Services the transmit interrupt for the port.  Writes as much data from the
 * buffer list as the port will accept.  Retires any emptied list buffers and
 * adjusts the final list buffer state for a partial write.
 */

static int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev)
{
	int result = 0;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	unsigned long flags;
	struct list_buffer *lb, *n;
	unsigned long bytes_total = 0;

	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);

	spin_lock_irqsave(&priv->tx_list.lock, flags);

	list_for_each_entry_safe(lb, n, &priv->tx_list.head, link) {

		u64 bytes_written;

		result = ps3_vuart_raw_write(dev, lb->head, lb->tail - lb->head,
			&bytes_written);

		if (result) {
			dev_dbg(&dev->core,
				"%s:%d: ps3_vuart_raw_write failed\n",
				__func__, __LINE__);
			break;
		}

		bytes_total += bytes_written;

		if (bytes_written < lb->tail - lb->head) {
			lb->head += bytes_written;
			dev_dbg(&dev->core,
				"%s:%d cleared buf_%lu, %llxh bytes\n",
				__func__, __LINE__, lb->dbg_number,
				bytes_written);
			goto port_full;
		}

		dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__,
			lb->dbg_number);

		list_del(&lb->link);
		kfree(lb);
	}

	ps3_vuart_disable_interrupt_tx(dev);
port_full:
	spin_unlock_irqrestore(&priv->tx_list.lock, flags);
	dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n",
		__func__, __LINE__, bytes_total);
	return result;
}

/**
 * ps3_vuart_handle_interrupt_rx - third stage receive interrupt handler
 *
 * Services the receive interrupt for the port.  Creates a list buffer and
 * copies all waiting port data to that buffer and enqueues the buffer in the
 * buffer list.  Buffer list data is dequeued via ps3_vuart_read.
 */

static int ps3_vuart_handle_interrupt_rx(struct ps3_system_bus_device *dev)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	unsigned long flags;
	u64 bytes;

	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);

	spin_lock_irqsave(&priv->rx_list.lock, flags);
	result = ps3_vuart_queue_rx_bytes(dev, &bytes);

	if (result) {
		spin_unlock_irqrestore(&priv->rx_list.lock, flags);
		return result;
	}

	if (priv->rx_list.work.trigger && priv->rx_list.bytes_held
		>= priv->rx_list.work.trigger) {
		dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n",
			__func__, __LINE__, priv->rx_list.work.trigger);
		priv->rx_list.work.trigger = 0;
		schedule_work(&priv->rx_list.work.work);
	}

	spin_unlock_irqrestore(&priv->rx_list.lock, flags);
	return result;
}

static int ps3_vuart_handle_interrupt_disconnect(
	struct ps3_system_bus_device *dev)
{
	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
	BUG_ON("no support");
	return -1;
}

/**
 * ps3_vuart_handle_port_interrupt - second stage interrupt handler
 *
 * Services any pending interrupt types for the port.  Passes control to the
 * third stage type specific interrupt handler.  Returns control to the first
 * stage handler after one iteration.
 */

static int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	unsigned long status;

	result = ps3_vuart_get_interrupt_status(dev, &status);

	if (result)
		return result;

	dev_dbg(&dev->core, "%s:%d: status: %lxh\n", __func__, __LINE__,
		status);

	if (status & INTERRUPT_MASK_DISCONNECT) {
		priv->stats.disconnect_interrupts++;
		result = ps3_vuart_handle_interrupt_disconnect(dev);
		if (result)
			ps3_vuart_disable_interrupt_disconnect(dev);
	}

	if (status & INTERRUPT_MASK_TX) {
		priv->stats.tx_interrupts++;
		result = ps3_vuart_handle_interrupt_tx(dev);
		if (result)
			ps3_vuart_disable_interrupt_tx(dev);
	}

	if (status & INTERRUPT_MASK_RX) {
		priv->stats.rx_interrupts++;
		result = ps3_vuart_handle_interrupt_rx(dev);
		if (result)
			ps3_vuart_disable_interrupt_rx(dev);
	}

	return 0;
}

struct vuart_bus_priv {
	struct ports_bmp *bmp;
	unsigned int virq;
	struct mutex probe_mutex;
	int use_count;
	struct ps3_system_bus_device *devices[PORT_COUNT];
} static vuart_bus_priv;

/**
 * ps3_vuart_irq_handler - first stage interrupt handler
 *
 * Loops finding any interrupting port and its associated instance data.
 * Passes control to the second stage port specific interrupt handler.  Loops
 * until all outstanding interrupts are serviced.
 */

static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private)
{
	struct vuart_bus_priv *bus_priv = _private;

	BUG_ON(!bus_priv);

	while (1) {
		unsigned int port;

		dump_ports_bmp(bus_priv->bmp);

		port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp->status);

		if (port == BITS_PER_LONG)
			break;

		BUG_ON(port >= PORT_COUNT);
		BUG_ON(!bus_priv->devices[port]);

		ps3_vuart_handle_port_interrupt(bus_priv->devices[port]);
	}

	return IRQ_HANDLED;
}

static int ps3_vuart_bus_interrupt_get(void)
{
	int result;

	pr_debug(" -> %s:%d\n", __func__, __LINE__);

	vuart_bus_priv.use_count++;

	BUG_ON(vuart_bus_priv.use_count > 2);

	if (vuart_bus_priv.use_count != 1)
		return 0;

	BUG_ON(vuart_bus_priv.bmp);

	vuart_bus_priv.bmp = kzalloc(sizeof(struct ports_bmp), GFP_KERNEL);

	if (!vuart_bus_priv.bmp) {
		pr_debug("%s:%d: kzalloc failed.\n", __func__, __LINE__);
		result = -ENOMEM;
		goto fail_bmp_malloc;
	}

	result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, vuart_bus_priv.bmp,
		&vuart_bus_priv.virq);

	if (result) {
		pr_debug("%s:%d: ps3_vuart_irq_setup failed (%d)\n",
			__func__, __LINE__, result);
		result = -EPERM;
		goto fail_alloc_irq;
	}

	result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler,
		0, "vuart", &vuart_bus_priv);

	if (result) {
		pr_debug("%s:%d: request_irq failed (%d)\n",
			__func__, __LINE__, result);
		goto fail_request_irq;
	}

	pr_debug(" <- %s:%d: ok\n", __func__, __LINE__);
	return result;

fail_request_irq:
	ps3_vuart_irq_destroy(vuart_bus_priv.virq);
	vuart_bus_priv.virq = NO_IRQ;
fail_alloc_irq:
	kfree(vuart_bus_priv.bmp);
	vuart_bus_priv.bmp = NULL;
fail_bmp_malloc:
	vuart_bus_priv.use_count--;
	pr_debug(" <- %s:%d: failed\n", __func__, __LINE__);
	return result;
}

static int ps3_vuart_bus_interrupt_put(void)
{
	pr_debug(" -> %s:%d\n", __func__, __LINE__);

	vuart_bus_priv.use_count--;

	BUG_ON(vuart_bus_priv.use_count < 0);

	if (vuart_bus_priv.use_count != 0)
		return 0;

	free_irq(vuart_bus_priv.virq, &vuart_bus_priv);

	ps3_vuart_irq_destroy(vuart_bus_priv.virq);
	vuart_bus_priv.virq = NO_IRQ;

	kfree(vuart_bus_priv.bmp);
	vuart_bus_priv.bmp = NULL;

	pr_debug(" <- %s:%d\n", __func__, __LINE__);
	return 0;
}

static int ps3_vuart_probe(struct ps3_system_bus_device *dev)
{
	int result;
	struct ps3_vuart_port_driver *drv;
	struct ps3_vuart_port_priv *priv = NULL;

	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);

	drv = ps3_system_bus_dev_to_vuart_drv(dev);

	dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
		drv->core.core.name);

	BUG_ON(!drv);

	if (dev->port_number >= PORT_COUNT) {
		BUG();
		return -EINVAL;
	}

	mutex_lock(&vuart_bus_priv.probe_mutex);

	result = ps3_vuart_bus_interrupt_get();

	if (result)
		goto fail_setup_interrupt;

	if (vuart_bus_priv.devices[dev->port_number]) {
		dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__,
			__LINE__, dev->port_number);
		result = -EBUSY;
		goto fail_busy;
	}

	vuart_bus_priv.devices[dev->port_number] = dev;

	/* Setup dev->driver_priv. */

	dev->driver_priv = kzalloc(sizeof(struct ps3_vuart_port_priv),
		GFP_KERNEL);

	if (!dev->driver_priv) {
		result = -ENOMEM;
		goto fail_dev_malloc;
	}

	priv = to_port_priv(dev);

	INIT_LIST_HEAD(&priv->tx_list.head);
	spin_lock_init(&priv->tx_list.lock);

	INIT_LIST_HEAD(&priv->rx_list.head);
	spin_lock_init(&priv->rx_list.lock);

	INIT_WORK(&priv->rx_list.work.work, NULL);
	priv->rx_list.work.trigger = 0;
	priv->rx_list.work.dev = dev;

	/* clear stale pending interrupts */

	ps3_vuart_clear_rx_bytes(dev, 0);

	ps3_vuart_set_interrupt_mask(dev, INTERRUPT_MASK_RX);

	ps3_vuart_set_triggers(dev, 1, 1);

	if (drv->probe)
		result = drv->probe(dev);
	else {
		result = 0;
		dev_info(&dev->core, "%s:%d: no probe method\n", __func__,
			__LINE__);
	}

	if (result) {
		dev_dbg(&dev->core, "%s:%d: drv->probe failed\n",
			__func__, __LINE__);
		goto fail_probe;
	}

	mutex_unlock(&vuart_bus_priv.probe_mutex);

	return result;

fail_probe:
	ps3_vuart_set_interrupt_mask(dev, 0);
	kfree(dev->driver_priv);
	dev->driver_priv = NULL;
fail_dev_malloc:
	vuart_bus_priv.devices[dev->port_number] = NULL;
fail_busy:
	ps3_vuart_bus_interrupt_put();
fail_setup_interrupt:
	mutex_unlock(&vuart_bus_priv.probe_mutex);
	dev_dbg(&dev->core, "%s:%d: failed\n", __func__, __LINE__);
	return result;
}

/**
 * ps3_vuart_cleanup - common cleanup helper.
 * @dev: The struct ps3_system_bus_device instance.
 *
 * Cleans interrupts and HV resources.  Must be called with
 * vuart_bus_priv.probe_mutex held.  Used by ps3_vuart_remove and
 * ps3_vuart_shutdown.  After this call, polled reading will still work.
 */

static int ps3_vuart_cleanup(struct ps3_system_bus_device *dev)
{
	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);

	ps3_vuart_cancel_async(dev);
	ps3_vuart_set_interrupt_mask(dev, 0);
	ps3_vuart_bus_interrupt_put();
	return 0;
}

/**
 * ps3_vuart_remove - Completely clean the device instance.
 * @dev: The struct ps3_system_bus_device instance.
 *
 * Cleans all memory, interrupts and HV resources.  After this call the
 * device can no longer be used.
 */

static int ps3_vuart_remove(struct ps3_system_bus_device *dev)
{
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	struct ps3_vuart_port_driver *drv;

	BUG_ON(!dev);

	mutex_lock(&vuart_bus_priv.probe_mutex);