visorchannel_funcs.c revision 22ad57ba5eacf2611a086379aaa18e456bf2ffbc
1/* visorchannel_funcs.c
2 *
3 * Copyright � 2010 - 2013 UNISYS CORPORATION
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14 * NON INFRINGEMENT.  See the GNU General Public License for more
15 * details.
16 */
17
18/*
19 *  This provides Supervisor channel communication primitives, which are
20 *  independent of the mechanism used to access the channel data.  All channel
21 *  data is accessed using the memregion abstraction.  (memregion has both
22 *  a CM2 implementation and a direct memory implementation.)
23 */
24
25#include "globals.h"
26#include "visorchannel.h"
27#include "guidutils.h"
28
29#define MYDRVNAME "visorchannel"
30
31struct VISORCHANNEL_Tag {
32	MEMREGION *memregion;	/* from visor_memregion_create() */
33	CHANNEL_HEADER chan_hdr;
34	GUID guid;
35	ulong size;
36	BOOL needs_lock;
37	spinlock_t insert_lock;
38	spinlock_t remove_lock;
39
40	struct {
41		SIGNAL_QUEUE_HEADER req_queue;
42		SIGNAL_QUEUE_HEADER rsp_queue;
43		SIGNAL_QUEUE_HEADER event_queue;
44		SIGNAL_QUEUE_HEADER ack_queue;
45	} safe_uis_queue;
46};
47
48/* Creates the VISORCHANNEL abstraction for a data area in memory, but does
49 * NOT modify this data area.
50 */
51static VISORCHANNEL *
52visorchannel_create_guts(HOSTADDRESS physaddr, ulong channelBytes,
53			 VISORCHANNEL *parent, ulong off, GUID guid,
54			 BOOL needs_lock)
55{
56	VISORCHANNEL *p = NULL;
57	void *rc = NULL;
58
59	p = kmalloc(sizeof(VISORCHANNEL), GFP_KERNEL|__GFP_NORETRY);
60	if (p == NULL) {
61		ERRDRV("allocation failed: (status=0)\n");
62		rc = NULL;
63		goto Away;
64	}
65	p->memregion = NULL;
66	p->needs_lock = needs_lock;
67	spin_lock_init(&p->insert_lock);
68	spin_lock_init(&p->remove_lock);
69
70	/* prepare chan_hdr (abstraction to read/write channel memory) */
71	if (parent == NULL)
72		p->memregion =
73		    visor_memregion_create(physaddr, sizeof(CHANNEL_HEADER));
74	else
75		p->memregion =
76		    visor_memregion_create_overlapped(parent->memregion,
77						      off,
78						      sizeof(CHANNEL_HEADER));
79	if (p->memregion == NULL) {
80		ERRDRV("visor_memregion_create failed failed: (status=0)\n");
81		rc = NULL;
82		goto Away;
83	}
84	if (visor_memregion_read(p->memregion, 0, &p->chan_hdr,
85				 sizeof(CHANNEL_HEADER)) < 0) {
86		ERRDRV("visor_memregion_read failed: (status=0)\n");
87		rc = NULL;
88		goto Away;
89	}
90	if (channelBytes == 0)
91		/* we had better be a CLIENT of this channel */
92		channelBytes = (ulong) p->chan_hdr.Size;
93	if (STRUCTSEQUAL(guid, Guid0))
94		/* we had better be a CLIENT of this channel */
95		guid = p->chan_hdr.Type;
96	if (visor_memregion_resize(p->memregion, channelBytes) < 0) {
97		ERRDRV("visor_memregion_resize failed: (status=0)\n");
98		rc = NULL;
99		goto Away;
100	}
101	p->size = channelBytes;
102	p->guid = guid;
103
104	rc = p;
105Away:
106
107	if (rc == NULL) {
108		if (p != NULL) {
109			visorchannel_destroy(p);
110			p = NULL;
111		}
112	}
113	return rc;
114}
115
116VISORCHANNEL *
117visorchannel_create(HOSTADDRESS physaddr, ulong channelBytes, GUID guid)
118{
119	return visorchannel_create_guts(physaddr, channelBytes, NULL, 0, guid,
120					FALSE);
121}
122EXPORT_SYMBOL_GPL(visorchannel_create);
123
124VISORCHANNEL *
125visorchannel_create_with_lock(HOSTADDRESS physaddr, ulong channelBytes,
126			      GUID guid)
127{
128	return visorchannel_create_guts(physaddr, channelBytes, NULL, 0, guid,
129					TRUE);
130}
131EXPORT_SYMBOL_GPL(visorchannel_create_with_lock);
132
133VISORCHANNEL *
134visorchannel_create_overlapped(ulong channelBytes,
135			       VISORCHANNEL *parent, ulong off, GUID guid)
136{
137	return visorchannel_create_guts(0, channelBytes, parent, off, guid,
138					FALSE);
139}
140EXPORT_SYMBOL_GPL(visorchannel_create_overlapped);
141
142VISORCHANNEL *
143visorchannel_create_overlapped_with_lock(ulong channelBytes,
144					 VISORCHANNEL *parent, ulong off,
145					 GUID guid)
146{
147	return visorchannel_create_guts(0, channelBytes, parent, off, guid,
148					TRUE);
149}
150EXPORT_SYMBOL_GPL(visorchannel_create_overlapped_with_lock);
151
152void
153visorchannel_destroy(VISORCHANNEL *channel)
154{
155	if (channel == NULL)
156		return;
157	if (channel->memregion != NULL) {
158		visor_memregion_destroy(channel->memregion);
159		channel->memregion = NULL;
160	}
161	kfree(channel);
162}
163EXPORT_SYMBOL_GPL(visorchannel_destroy);
164
165HOSTADDRESS
166visorchannel_get_physaddr(VISORCHANNEL *channel)
167{
168	return visor_memregion_get_physaddr(channel->memregion);
169}
170EXPORT_SYMBOL_GPL(visorchannel_get_physaddr);
171
172ulong
173visorchannel_get_nbytes(VISORCHANNEL *channel)
174{
175	return channel->size;
176}
177EXPORT_SYMBOL_GPL(visorchannel_get_nbytes);
178
179char *
180visorchannel_GUID_id(GUID *guid, char *s)
181{
182	return GUID_format1(guid, s);
183}
184EXPORT_SYMBOL_GPL(visorchannel_GUID_id);
185
186char *
187visorchannel_id(VISORCHANNEL *channel, char *s)
188{
189	return visorchannel_GUID_id(&channel->guid, s);
190}
191EXPORT_SYMBOL_GPL(visorchannel_id);
192
193char *
194visorchannel_zoneid(VISORCHANNEL *channel, char *s)
195{
196	return visorchannel_GUID_id(&channel->chan_hdr.ZoneGuid, s);
197}
198EXPORT_SYMBOL_GPL(visorchannel_zoneid);
199
200HOSTADDRESS
201visorchannel_get_clientpartition(VISORCHANNEL *channel)
202{
203	return channel->chan_hdr.PartitionHandle;
204}
205EXPORT_SYMBOL_GPL(visorchannel_get_clientpartition);
206
207GUID
208visorchannel_get_GUID(VISORCHANNEL *channel)
209{
210	return channel->guid;
211}
212EXPORT_SYMBOL_GPL(visorchannel_get_GUID);
213
214MEMREGION *
215visorchannel_get_memregion(VISORCHANNEL *channel)
216{
217	return channel->memregion;
218}
219EXPORT_SYMBOL_GPL(visorchannel_get_memregion);
220
221int
222visorchannel_read(VISORCHANNEL *channel, ulong offset,
223		  void *local, ulong nbytes)
224{
225	int rc = visor_memregion_read(channel->memregion, offset,
226				      local, nbytes);
227	if ((rc >= 0) && (offset == 0) && (nbytes >= sizeof(CHANNEL_HEADER)))
228		memcpy(&channel->chan_hdr, local, sizeof(CHANNEL_HEADER));
229	return rc;
230}
231EXPORT_SYMBOL_GPL(visorchannel_read);
232
233int
234visorchannel_write(VISORCHANNEL *channel, ulong offset,
235		   void *local, ulong nbytes)
236{
237	if (offset == 0 && nbytes >= sizeof(CHANNEL_HEADER))
238		memcpy(&channel->chan_hdr, local, sizeof(CHANNEL_HEADER));
239	return visor_memregion_write(channel->memregion, offset, local, nbytes);
240}
241EXPORT_SYMBOL_GPL(visorchannel_write);
242
243int
244visorchannel_clear(VISORCHANNEL *channel, ulong offset, U8 ch, ulong nbytes)
245{
246	int rc = -1;
247	int bufsize = 65536;
248	int written = 0;
249	U8 *buf = vmalloc(bufsize);
250
251	if (buf == NULL) {
252		ERRDRV("%s failed memory allocation", __func__);
253		goto Away;
254	}
255	memset(buf, ch, bufsize);
256	while (nbytes > 0) {
257		ulong thisbytes = bufsize;
258		int x = -1;
259		if (nbytes < thisbytes)
260			thisbytes = nbytes;
261		x = visor_memregion_write(channel->memregion, offset + written,
262					  buf, thisbytes);
263		if (x < 0) {
264			rc = x;
265			goto Away;
266		}
267		written += thisbytes;
268		nbytes -= thisbytes;
269	}
270	rc = 0;
271
272Away:
273	if (buf != NULL) {
274		vfree(buf);
275		buf = NULL;
276	}
277	return rc;
278}
279EXPORT_SYMBOL_GPL(visorchannel_clear);
280
281void *
282visorchannel_get_header(VISORCHANNEL *channel)
283{
284	return (void *) &(channel->chan_hdr);
285}
286EXPORT_SYMBOL_GPL(visorchannel_get_header);
287
288/** Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a
289 *  channel header
290 */
291#define SIG_QUEUE_OFFSET(chan_hdr, q) \
292	((chan_hdr)->oChannelSpace + ((q) * sizeof(SIGNAL_QUEUE_HEADER)))
293
294/** Return offset of a specific queue entry (data) from the beginning of a
295 *  channel header
296 */
297#define SIG_DATA_OFFSET(chan_hdr, q, sig_hdr, slot) \
298	(SIG_QUEUE_OFFSET(chan_hdr, q) + (sig_hdr)->oSignalBase + \
299	    ((slot) * (sig_hdr)->SignalSize))
300
301/** Write the contents of a specific field within a SIGNAL_QUEUE_HEADER back
302 *  into host memory
303 */
304#define SIG_WRITE_FIELD(channel, queue, sig_hdr, FIELD)			\
305	(visor_memregion_write(channel->memregion,			\
306			       SIG_QUEUE_OFFSET(&channel->chan_hdr, queue)+ \
307			       offsetof(SIGNAL_QUEUE_HEADER, FIELD),	\
308			       &((sig_hdr)->FIELD),			\
309			       sizeof((sig_hdr)->FIELD)) >= 0)
310
311static BOOL
312sig_read_header(VISORCHANNEL *channel, U32 queue,
313		SIGNAL_QUEUE_HEADER *sig_hdr)
314{
315	BOOL rc = FALSE;
316
317	if (channel->chan_hdr.oChannelSpace < sizeof(CHANNEL_HEADER)) {
318		ERRDRV("oChannelSpace too small: (status=%d)\n", rc);
319		goto Away;
320	}
321
322	/* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */
323
324	if (visor_memregion_read(channel->memregion,
325				 SIG_QUEUE_OFFSET(&channel->chan_hdr, queue),
326				 sig_hdr, sizeof(SIGNAL_QUEUE_HEADER)) < 0) {
327		ERRDRV("queue=%d SIG_QUEUE_OFFSET=%d",
328		       queue, (int)SIG_QUEUE_OFFSET(&channel->chan_hdr, queue));
329		ERRDRV("visor_memregion_read of signal queue failed: (status=%d)\n", rc);
330		goto Away;
331	}
332	rc = TRUE;
333Away:
334	return rc;
335}
336
337static BOOL
338sig_do_data(VISORCHANNEL *channel, U32 queue,
339	    SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data, BOOL is_write)
340{
341	BOOL rc = FALSE;
342	int signal_data_offset = SIG_DATA_OFFSET(&channel->chan_hdr, queue,
343						 sig_hdr, slot);
344	if (is_write) {
345		if (visor_memregion_write(channel->memregion,
346					  signal_data_offset,
347					  data, sig_hdr->SignalSize) < 0) {
348			ERRDRV("visor_memregion_write of signal data failed: (status=%d)\n", rc);
349			goto Away;
350		}
351	} else {
352		if (visor_memregion_read(channel->memregion, signal_data_offset,
353					 data, sig_hdr->SignalSize) < 0) {
354			ERRDRV("visor_memregion_read of signal data failed: (status=%d)\n", rc);
355			goto Away;
356		}
357	}
358	rc = TRUE;
359Away:
360	return rc;
361}
362
363static inline BOOL
364sig_read_data(VISORCHANNEL *channel, U32 queue,
365	      SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data)
366{
367	return sig_do_data(channel, queue, sig_hdr, slot, data, FALSE);
368}
369
370static inline BOOL
371sig_write_data(VISORCHANNEL *channel, U32 queue,
372	       SIGNAL_QUEUE_HEADER *sig_hdr, U32 slot, void *data)
373{
374	return sig_do_data(channel, queue, sig_hdr, slot, data, TRUE);
375}
376
377static inline unsigned char
378safe_sig_queue_validate(pSIGNAL_QUEUE_HEADER psafe_sqh,
379			pSIGNAL_QUEUE_HEADER punsafe_sqh,
380			U32 *phead, U32 *ptail)
381{
382	if ((*phead >= psafe_sqh->MaxSignalSlots)
383	    || (*ptail >= psafe_sqh->MaxSignalSlots)) {
384		/* Choose 0 or max, maybe based on current tail value */
385		*phead = 0;
386		*ptail = 0;
387
388		/* Sync with client as necessary */
389		punsafe_sqh->Head = *phead;
390		punsafe_sqh->Tail = *ptail;
391
392		ERRDRV("safe_sig_queue_validate: head = 0x%x, tail = 0x%x, MaxSlots = 0x%x",
393		     *phead, *ptail, psafe_sqh->MaxSignalSlots);
394		return 0;
395	}
396	return 1;
397}				/* end safe_sig_queue_validate */
398
399BOOL
400visorchannel_signalremove(VISORCHANNEL *channel, U32 queue, void *msg)
401{
402	BOOL rc = FALSE;
403	SIGNAL_QUEUE_HEADER sig_hdr;
404
405	if (channel->needs_lock)
406		spin_lock(&channel->remove_lock);
407
408	if (!sig_read_header(channel, queue, &sig_hdr)) {
409		rc = FALSE;
410		goto Away;
411	}
412	if (sig_hdr.Head == sig_hdr.Tail) {
413		rc = FALSE;	/* no signals to remove */
414		goto Away;
415	}
416	sig_hdr.Tail = (sig_hdr.Tail + 1) % sig_hdr.MaxSignalSlots;
417	if (!sig_read_data(channel, queue, &sig_hdr, sig_hdr.Tail, msg)) {
418		ERRDRV("sig_read_data failed: (status=%d)\n", rc);
419		goto Away;
420	}
421	sig_hdr.NumSignalsReceived++;
422
423	/* For each data field in SIGNAL_QUEUE_HEADER that was modified,
424	 * update host memory.
425	 */
426	MEMORYBARRIER;
427	if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, Tail)) {
428		ERRDRV("visor_memregion_write of Tail failed: (status=%d)\n",
429		       rc);
430		goto Away;
431	}
432	if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumSignalsReceived)) {
433		ERRDRV("visor_memregion_write of NumSignalsReceived failed: (status=%d)\n", rc);
434		goto Away;
435	}
436	rc = TRUE;
437Away:
438	if (channel->needs_lock)
439		spin_unlock(&channel->remove_lock);
440
441	return rc;
442}
443EXPORT_SYMBOL_GPL(visorchannel_signalremove);
444
445BOOL
446visorchannel_signalinsert(VISORCHANNEL *channel, U32 queue, void *msg)
447{
448	BOOL rc = FALSE;
449	SIGNAL_QUEUE_HEADER sig_hdr;
450
451	if (channel->needs_lock)
452		spin_lock(&channel->insert_lock);
453
454	if (!sig_read_header(channel, queue, &sig_hdr)) {
455		rc = FALSE;
456		goto Away;
457	}
458
459	sig_hdr.Head = ((sig_hdr.Head + 1) % sig_hdr.MaxSignalSlots);
460	if (sig_hdr.Head == sig_hdr.Tail) {
461		sig_hdr.NumOverflows++;
462		if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumOverflows)) {
463			ERRDRV("visor_memregion_write of NumOverflows failed: (status=%d)\n", rc);
464			goto Away;
465		}
466		rc = FALSE;
467		goto Away;
468	}
469
470	if (!sig_write_data(channel, queue, &sig_hdr, sig_hdr.Head, msg)) {
471		ERRDRV("sig_write_data failed: (status=%d)\n", rc);
472		goto Away;
473	}
474	sig_hdr.NumSignalsSent++;
475
476	/* For each data field in SIGNAL_QUEUE_HEADER that was modified,
477	 * update host memory.
478	 */
479	MEMORYBARRIER;
480	if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, Head)) {
481		ERRDRV("visor_memregion_write of Head failed: (status=%d)\n",
482		       rc);
483		goto Away;
484	}
485	if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, NumSignalsSent)) {
486		ERRDRV("visor_memregion_write of NumSignalsSent failed: (status=%d)\n", rc);
487		goto Away;
488	}
489	rc = TRUE;
490Away:
491	if (channel->needs_lock)
492		spin_unlock(&channel->insert_lock);
493
494	return rc;
495}
496EXPORT_SYMBOL_GPL(visorchannel_signalinsert);
497
498
499int
500visorchannel_signalqueue_slots_avail(VISORCHANNEL *channel, U32 queue)
501{
502	SIGNAL_QUEUE_HEADER sig_hdr;
503	U32 slots_avail, slots_used;
504	U32 head, tail;
505
506	if (!sig_read_header(channel, queue, &sig_hdr))
507		return 0;
508	head = sig_hdr.Head;
509	tail = sig_hdr.Tail;
510	if (head < tail)
511		head = head + sig_hdr.MaxSignalSlots;
512	slots_used = (head - tail);
513	slots_avail = sig_hdr.MaxSignals - slots_used;
514	return (int) slots_avail;
515}
516EXPORT_SYMBOL_GPL(visorchannel_signalqueue_slots_avail);
517
518int
519visorchannel_signalqueue_max_slots(VISORCHANNEL *channel, U32 queue)
520{
521	SIGNAL_QUEUE_HEADER sig_hdr;
522	if (!sig_read_header(channel, queue, &sig_hdr))
523		return 0;
524	return (int) sig_hdr.MaxSignals;
525}
526EXPORT_SYMBOL_GPL(visorchannel_signalqueue_max_slots);
527
528static void
529sigqueue_debug(SIGNAL_QUEUE_HEADER *q, int which, struct seq_file *seq)
530{
531	seq_printf(seq, "Signal Queue #%d\n", which);
532	seq_printf(seq, "   VersionId          = %lu\n", (ulong) q->VersionId);
533	seq_printf(seq, "   Type               = %lu\n", (ulong) q->Type);
534	seq_printf(seq, "   oSignalBase        = %llu\n",
535		   (long long) q->oSignalBase);
536	seq_printf(seq, "   SignalSize         = %lu\n", (ulong) q->SignalSize);
537	seq_printf(seq, "   MaxSignalSlots     = %lu\n",
538		   (ulong) q->MaxSignalSlots);
539	seq_printf(seq, "   MaxSignals         = %lu\n", (ulong) q->MaxSignals);
540	seq_printf(seq, "   FeatureFlags       = %-16.16Lx\n",
541		   (long long) q->FeatureFlags);
542	seq_printf(seq, "   NumSignalsSent     = %llu\n",
543		   (long long) q->NumSignalsSent);
544	seq_printf(seq, "   NumSignalsReceived = %llu\n",
545		   (long long) q->NumSignalsReceived);
546	seq_printf(seq, "   NumOverflows       = %llu\n",
547		   (long long) q->NumOverflows);
548	seq_printf(seq, "   Head               = %lu\n", (ulong) q->Head);
549	seq_printf(seq, "   Tail               = %lu\n", (ulong) q->Tail);
550}
551
552void
553visorchannel_debug(VISORCHANNEL *channel, int nQueues,
554		   struct seq_file *seq, U32 off)
555{
556	HOSTADDRESS addr = 0;
557	ulong nbytes = 0, nbytes_region = 0;
558	MEMREGION *memregion = NULL;
559	CHANNEL_HEADER hdr;
560	CHANNEL_HEADER *phdr = &hdr;
561	char s[99];
562	int i = 0;
563	int errcode = 0;
564
565	if (channel == NULL) {
566		ERRDRV("%s no channel", __func__);
567		return;
568	}
569	memregion = channel->memregion;
570	if (memregion == NULL) {
571		ERRDRV("%s no memregion", __func__);
572		return;
573	}
574	addr = visor_memregion_get_physaddr(memregion);
575	nbytes_region = visor_memregion_get_nbytes(memregion);
576	errcode = visorchannel_read(channel, off,
577				    phdr, sizeof(CHANNEL_HEADER));
578	if (errcode < 0) {
579		seq_printf(seq,
580			   "Read of channel header failed with errcode=%d)\n",
581			   errcode);
582		if (off == 0) {
583			phdr = &channel->chan_hdr;
584			seq_puts(seq, "(following data may be stale)\n");
585		} else
586			return;
587	}
588	nbytes = (ulong) (phdr->Size);
589	seq_printf(seq, "--- Begin channel @0x%-16.16Lx for 0x%lx bytes (region=0x%lx bytes) ---\n",
590		   addr + off, nbytes, nbytes_region);
591	seq_printf(seq, "Type            = %s\n", GUID_format2(&phdr->Type, s));
592	seq_printf(seq, "ZoneGuid        = %s\n",
593		   GUID_format2(&phdr->ZoneGuid, s));
594	seq_printf(seq, "Signature       = 0x%-16.16Lx\n",
595		   (long long) phdr->Signature);
596	seq_printf(seq, "LegacyState     = %lu\n", (ulong) phdr->LegacyState);
597	seq_printf(seq, "SrvState        = %lu\n", (ulong) phdr->SrvState);
598	seq_printf(seq, "CliStateBoot    = %lu\n", (ulong) phdr->CliStateBoot);
599	seq_printf(seq, "CliStateOS      = %lu\n", (ulong) phdr->CliStateOS);
600	seq_printf(seq, "HeaderSize      = %lu\n", (ulong) phdr->HeaderSize);
601	seq_printf(seq, "Size            = %llu\n", (long long) phdr->Size);
602	seq_printf(seq, "Features        = 0x%-16.16llx\n",
603		   (long long) phdr->Features);
604	seq_printf(seq, "PartitionHandle = 0x%-16.16llx\n",
605		   (long long) phdr->PartitionHandle);
606	seq_printf(seq, "Handle          = 0x%-16.16llx\n",
607		   (long long) phdr->Handle);
608	seq_printf(seq, "VersionId       = %lu\n", (ulong) phdr->VersionId);
609	seq_printf(seq, "oChannelSpace   = %llu\n",
610		   (long long) phdr->oChannelSpace);
611	if ((phdr->oChannelSpace == 0) || (errcode < 0))
612		;
613	else
614		for (i = 0; i < nQueues; i++) {
615			SIGNAL_QUEUE_HEADER q;
616			errcode = visorchannel_read(channel,
617						    off + phdr->oChannelSpace +
618						    (i * sizeof(q)),
619						    &q, sizeof(q));
620			if (errcode < 0) {
621				seq_printf(seq,
622					   "failed to read signal queue #%d from channel @0x%-16.16Lx errcode=%d\n",
623					   i, addr, errcode);
624				continue;
625			}
626			sigqueue_debug(&q, i, seq);
627		}
628	seq_printf(seq, "--- End   channel @0x%-16.16Lx for 0x%lx bytes ---\n",
629		   addr + off, nbytes);
630}
631EXPORT_SYMBOL_GPL(visorchannel_debug);
632
633void
634visorchannel_dump_section(VISORCHANNEL *chan, char *s,
635			  int off, int len, struct seq_file *seq)
636{
637	char *buf, *tbuf, *fmtbuf;
638	int fmtbufsize = 0;
639	int i;
640	int errcode = 0;
641
642	fmtbufsize = 100 * COVQ(len, 16);
643	buf = kmalloc(len, GFP_KERNEL|__GFP_NORETRY);
644	fmtbuf = kmalloc(fmtbufsize, GFP_KERNEL|__GFP_NORETRY);
645	if (buf == NULL || fmtbuf == NULL)
646		goto Away;
647
648	errcode = visorchannel_read(chan, off, buf, len);
649	if (errcode < 0) {
650		ERRDRV("%s failed to read %s from channel errcode=%d",
651		       s, __func__, errcode);
652		goto Away;
653	}
654	seq_printf(seq, "channel %s:\n", s);
655	tbuf = buf;
656	while (len > 0) {
657		i = (len < 16) ? len : 16;
658		hex_dump_to_buffer(tbuf, i, 16, 1, fmtbuf, fmtbufsize, TRUE);
659		seq_printf(seq, "%s\n", fmtbuf);
660		tbuf += 16;
661		len -= 16;
662	}
663
664Away:
665	if (buf != NULL) {
666		kfree(buf);
667		buf = NULL;
668	}
669	if (fmtbuf != NULL) {
670		kfree(fmtbuf);
671		fmtbuf = NULL;
672	}
673}
674EXPORT_SYMBOL_GPL(visorchannel_dump_section);
675