File: /usr/src/linux/arch/ia64/sn/io/labelcl.c

1     /*  labelcl - SGI's Hwgraph Compatibility Layer.
2     
3         This library is free software; you can redistribute it and/or
4         modify it under the terms of the GNU Library General Public
5         License as published by the Free Software Foundation; either
6         version 2 of the License, or (at your option) any later version.
7     
8         This library is distributed in the hope that it will be useful,
9         but WITHOUT ANY WARRANTY; without even the implied warranty of
10         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11         Library General Public License for more details.
12     
13         You should have received a copy of the GNU Library General Public
14         License along with this library; if not, write to the Free
15         Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16     
17         Colin Ngam may be reached by email at cngam@sgi.com
18     
19     */
20     
21     #include <linux/types.h>
22     #include <linux/slab.h>
23     #include <asm/sn/sgi.h>
24     #include <linux/devfs_fs.h>
25     #include <linux/devfs_fs_kernel.h>
26     #include <asm/sn/invent.h>
27     #include <asm/sn/hcl.h>
28     #include <asm/sn/labelcl.h>
29     
30     /*
31     ** Very simple and dumb string table that supports only find/insert.
32     ** In practice, if this table gets too large, we may need a more
33     ** efficient data structure.   Also note that currently there is no 
34     ** way to delete an item once it's added.  Therefore, name collision 
35     ** will return an error.
36     */
37     
38     struct string_table label_string_table;
39     
40     
41     
42     /*
43      * string_table_init - Initialize the given string table.
44      */
45     void
46     string_table_init(struct string_table *string_table)
47     {
48     	string_table->string_table_head = NULL;
49     	string_table->string_table_generation = 0;
50     
51     	/*
52     	 * We nedd to initialize locks here!
53     	 */
54     
55     	return;
56     }
57     
58     
59     /*
60      * string_table_destroy - Destroy the given string table.
61      */
62     void
63     string_table_destroy(struct string_table *string_table)
64     {
65     	struct string_table_item *item, *next_item;
66     
67     	item = string_table->string_table_head;
68     	while (item) {
69     		next_item = item->next;
70     
71     		STRTBL_FREE(item);
72     		item = next_item;
73     	}
74     
75     	/*
76     	 * We need to destroy whatever lock we have here
77     	 */
78     
79     	return;
80     }
81     
82     
83     
84     /*
85      * string_table_insert - Insert an entry in the string table .. duplicate 
86      *	names are not allowed.
87      */
88     char *
89     string_table_insert(struct string_table *string_table, char *name)
90     {
91     	struct string_table_item *item, *new_item = NULL, *last_item = NULL;
92     
93     again:
94     	/*
95     	 * Need to lock the table ..
96     	 */
97     	item = string_table->string_table_head;
98     	last_item = NULL;
99     
100     	while (item) {
101     		if (!strcmp(item->string, name)) {
102     			/*
103     			 * If we allocated space for the string and the found that
104     			 * someone else already entered it into the string table,
105     			 * free the space we just allocated.
106     			 */
107     			if (new_item)
108     				STRTBL_FREE(new_item);
109     
110     
111     			/*
112     			 * Search optimization: move the found item to the head
113     			 * of the list.
114     			 */
115     			if (last_item != NULL) {
116     				last_item->next = item->next;
117     				item->next = string_table->string_table_head;
118     				string_table->string_table_head = item;
119     			}
120     			goto out;
121     		}
122     		last_item = item;
123     		item=item->next;
124     	}
125     
126     	/*
127     	 * name was not found, so add it to the string table.
128     	 */
129     	if (new_item == NULL) {
130     		long old_generation = string_table->string_table_generation;
131     
132     		new_item = STRTBL_ALLOC(strlen(name));
133     
134     		strcpy(new_item->string, name);
135     
136     		/*
137     		 * While we allocated memory for the new string, someone else 
138     		 * changed the string table.
139     		 */
140     		if (old_generation != string_table->string_table_generation) {
141     			goto again;
142     		}
143     	} else {
144     		/* At this we only have the string table lock in access mode.
145     		 * Promote the access lock to an update lock for the string
146     		 * table insertion below.
147     		 */
148     			long old_generation = 
149     				string_table->string_table_generation;
150     
151     			/*
152     			 * After we did the unlock and wer waiting for update
153     			 * lock someone could have potentially updated
154     			 * the string table. Check the generation number
155     			 * for this case. If it is the case we have to
156     			 * try all over again.
157     			 */
158     			if (old_generation != 
159     			    string_table->string_table_generation) {
160     				goto again;
161     			}
162     		}
163     
164     	/*
165     	 * At this point, we're committed to adding new_item to the string table.
166     	 */
167     	new_item->next = string_table->string_table_head;
168     	item = string_table->string_table_head = new_item;
169     	string_table->string_table_generation++;
170     
171     out:
172     	/*
173     	 * Need to unlock here.
174     	 */
175     	return(item->string);
176     }
177     
178     /*
179      * labelcl_info_create - Creates the data structure that will hold the
180      *	device private information asscoiated with a devfs entry.
181      *	The pointer to this structure is what gets stored in the devfs 
182      *	(void * info).
183      */
184     labelcl_info_t *
185     labelcl_info_create()
186     {
187     
188     	labelcl_info_t *new = NULL;
189     
190     	/* Initial allocation does not include any area for labels */
191     	if ( ( new = (labelcl_info_t *)kmalloc (sizeof(labelcl_info_t), GFP_KERNEL) ) == NULL )
192     		return NULL;
193     
194     	memset (new, 0, sizeof(labelcl_info_t));
195     	new->hwcl_magic = LABELCL_MAGIC;
196     	return( new);
197     
198     }
199     
200     /*
201      * labelcl_info_destroy - Frees the data structure that holds the
202      *      device private information asscoiated with a devfs entry.  This 
203      *	data structure was created by device_info_create().
204      *
205      *	The caller is responsible for nulling the (void *info) in the 
206      *	corresponding devfs entry.
207      */
208     int
209     labelcl_info_destroy(labelcl_info_t *labelcl_info)
210     {
211     
212     	if (labelcl_info == NULL)
213     		return(0);
214     
215     	/* Free the label list */
216     	if (labelcl_info->label_list)
217     		kfree(labelcl_info->label_list);
218     
219     	/* Now free the label info area */
220     	labelcl_info->hwcl_magic = 0;
221     	kfree(labelcl_info);
222     
223     	return(0);
224     }
225     
226     /*
227      * labelcl_info_add_LBL - Adds a new label entry in the labelcl info 
228      *	structure.
229      *
230      *	Error is returned if we find another label with the same name.
231      */
232     int
233     labelcl_info_add_LBL(devfs_handle_t de,
234     			char *info_name,
235     			arb_info_desc_t info_desc,
236     			arbitrary_info_t info)
237     {
238     	labelcl_info_t	*labelcl_info = NULL;
239     	int num_labels;
240     	int new_label_list_size;
241     	label_info_t *old_label_list, *new_label_list = NULL;
242     	char *name;
243     	int i;
244     
245     	if (de == NULL)
246     		return(-1);
247     
248             labelcl_info = devfs_get_info(de);
249     	if (labelcl_info == NULL)
250     		return(-1);
251     
252     	if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
253     		return(-1);
254     
255     	if (info_name == NULL)
256     		return(-1);
257     
258     	if (strlen(info_name) >= LABEL_LENGTH_MAX)
259     		return(-1);
260     
261     	name = string_table_insert(&label_string_table, info_name);
262     
263     	num_labels = labelcl_info->num_labels;
264     	new_label_list_size = sizeof(label_info_t) * (num_labels+1);
265     
266     	/*
267     	 * Create a new label info area.
268     	 */
269     	if (new_label_list_size != 0) {
270     		new_label_list = (label_info_t *) kmalloc(new_label_list_size, GFP_KERNEL);
271     
272     		if (new_label_list == NULL)
273     			return(-1);
274     	}
275     
276     	/*
277     	 * At this point, we are committed to adding the labelled info, 
278     	 * if there isn't already information there with the same name.
279     	 */
280     	old_label_list = labelcl_info->label_list;
281     
282     	/* 
283     	 * Look for matching info name.
284     	 */
285     	for (i=0; i<num_labels; i++) {
286     		if (!strcmp(info_name, old_label_list[i].name)) {
287     			/* Not allowed to add duplicate labelled info names. */
288     			kfree(new_label_list);
289     			printk(KERN_WARNING "labelcl_info_add_LBL: Duplicate label name %s for vertex 0x%p\n", info_name, de);
290     			return(-1);
291     		}
292     		new_label_list[i] = old_label_list[i]; /* structure copy */
293     	}
294     
295     	new_label_list[num_labels].name = name;
296     	new_label_list[num_labels].desc = info_desc;
297     	new_label_list[num_labels].info = info;
298     
299     	labelcl_info->num_labels = num_labels+1;
300     	labelcl_info->label_list = new_label_list;
301     
302     	if (old_label_list != NULL)
303     		kfree(old_label_list);
304     
305     	return(0);
306     }
307     
308     /*
309      * labelcl_info_remove_LBL - Remove a label entry.
310      */
311     int
312     labelcl_info_remove_LBL(devfs_handle_t de,
313     			 char *info_name,
314     			 arb_info_desc_t *info_desc,
315     			 arbitrary_info_t *info)
316     {
317     	labelcl_info_t	*labelcl_info = NULL;
318     	int num_labels;
319     	int new_label_list_size;
320     	label_info_t *old_label_list, *new_label_list = NULL;
321     	arb_info_desc_t label_desc_found;
322     	arbitrary_info_t label_info_found;
323     	int i;
324     
325     	if (de == NULL)
326     		return(-1);
327     
328     	labelcl_info = devfs_get_info(de);
329     	if (labelcl_info == NULL)
330     		return(-1);
331     
332     	if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
333     		return(-1);
334     
335     	num_labels = labelcl_info->num_labels;
336     	if (num_labels == 0) {
337     		return(-1);
338     	}
339     
340     	/*
341     	 * Create a new info area.
342     	 */
343     	new_label_list_size = sizeof(label_info_t) * (num_labels-1);
344     	if (new_label_list_size) {
345     		new_label_list = (label_info_t *) kmalloc(new_label_list_size, GFP_KERNEL);
346     		if (new_label_list == NULL)
347     			return(-1);
348     	}
349     
350     	/*
351     	 * At this point, we are committed to removing the labelled info, 
352     	 * if it still exists.
353     	 */
354     	old_label_list = labelcl_info->label_list;
355     
356     	/* 
357     	 * Find matching info name.
358     	 */
359     	for (i=0; i<num_labels; i++) {
360     		if (!strcmp(info_name, old_label_list[i].name)) {
361     			label_desc_found = old_label_list[i].desc;
362     			label_info_found = old_label_list[i].info;
363     			goto found;
364     		}
365     		if (i < num_labels-1) /* avoid walking off the end of the new vertex */
366     			new_label_list[i] = old_label_list[i]; /* structure copy */
367     	}
368     
369     	/* The named info doesn't exist. */
370     	if (new_label_list)
371     		kfree(new_label_list);
372     
373     	return(-1);
374     
375     found:
376     	/* Finish up rest of labelled info */
377     	for (i=i+1; i<num_labels; i++)
378     		new_label_list[i-1] = old_label_list[i]; /* structure copy */
379     
380     	labelcl_info->num_labels = num_labels+1;
381     	labelcl_info->label_list = new_label_list;
382     
383     	kfree(old_label_list);
384     
385     	if (info != NULL)
386     		*info = label_info_found;
387     
388     	if (info_desc != NULL)
389     		*info_desc = label_desc_found;
390     
391     	return(0);
392     }
393     
394     
395     /*
396      * labelcl_info_replace_LBL - Replace an existing label entry with the 
397      *	given new information.
398      *
399      *	Label entry must exist.
400      */
401     int
402     labelcl_info_replace_LBL(devfs_handle_t de,
403     			char *info_name,
404     			arb_info_desc_t info_desc,
405     			arbitrary_info_t info,
406     			arb_info_desc_t *old_info_desc,
407     			arbitrary_info_t *old_info)
408     {
409     	labelcl_info_t	*labelcl_info = NULL;
410     	int num_labels;
411     	label_info_t *label_list;
412     	int i;
413     
414     	if (de == NULL)
415     		return(-1);
416     
417     	labelcl_info = devfs_get_info(de);
418     	if (labelcl_info == NULL)
419     		return(-1);
420     
421     	if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
422     		return(-1);
423     
424     	num_labels = labelcl_info->num_labels;
425     	if (num_labels == 0) {
426     		return(-1);
427     	}
428     
429     	if (info_name == NULL)
430     		return(-1);
431     
432     	label_list = labelcl_info->label_list;
433     
434     	/* 
435     	 * Verify that information under info_name already exists.
436     	 */
437     	for (i=0; i<num_labels; i++)
438     		if (!strcmp(info_name, label_list[i].name)) {
439     			if (old_info != NULL)
440     				*old_info = label_list[i].info;
441     
442     			if (old_info_desc != NULL)
443     				*old_info_desc = label_list[i].desc;
444     
445     			label_list[i].info = info;
446     			label_list[i].desc = info_desc;
447     
448     			return(0);
449     		}
450     
451     
452     	return(-1);
453     }
454     
455     /*
456      * labelcl_info_get_LBL - Retrieve and return the information for the 
457      *	given label entry.
458      */
459     int
460     labelcl_info_get_LBL(devfs_handle_t de,
461     		      char *info_name,
462     		      arb_info_desc_t *info_desc,
463     		      arbitrary_info_t *info)
464     {
465     	labelcl_info_t	*labelcl_info = NULL;
466     	int num_labels;
467     	label_info_t *label_list;
468     	int i;
469     
470     	if (de == NULL)
471     		return(-1);
472     
473     	labelcl_info = devfs_get_info(de);
474     	if (labelcl_info == NULL)
475     		return(-1);
476     
477     	if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
478     		return(-1);
479     
480     	num_labels = labelcl_info->num_labels;
481     	if (num_labels == 0) {
482     		return(-1);
483     	}
484     
485     	label_list = labelcl_info->label_list;
486     
487     	/* 
488     	 * Find information under info_name.
489     	 */
490     	for (i=0; i<num_labels; i++)
491     		if (!strcmp(info_name, label_list[i].name)) {
492     			if (info != NULL)
493     				*info = label_list[i].info;
494     			if (info_desc != NULL)
495     				*info_desc = label_list[i].desc;
496     
497     			return(0);
498     		}
499     
500     	return(-1);
501     }
502     
503     /*
504      * labelcl_info_get_next_LBL - returns the next label entry on the list.
505      */
506     int
507     labelcl_info_get_next_LBL(devfs_handle_t de,
508     			   char *buffer,
509     			   arb_info_desc_t *info_descp,
510     			   arbitrary_info_t *infop,
511     			   labelcl_info_place_t *placeptr)
512     {
513     	labelcl_info_t	*labelcl_info = NULL;
514     	uint which_info;
515     	label_info_t *label_list;
516     
517     	if ((buffer == NULL) && (infop == NULL))
518     		return(-1);
519     
520     	if (placeptr == NULL)
521     		return(-1);
522     
523     	if (de == NULL)
524     		return(-1);
525     
526     	labelcl_info = devfs_get_info(de);
527     	if (labelcl_info == NULL)
528     		return(-1);
529     
530     	if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
531     		return(-1);
532     
533     	which_info = *placeptr;
534     
535     	if (which_info >= labelcl_info->num_labels) {
536     		return(-1);
537     	}
538     
539     	label_list = (label_info_t *) labelcl_info->label_list;
540     
541     	if (buffer != NULL)
542     		strcpy(buffer, label_list[which_info].name);
543     
544     	if (infop)
545     		*infop = label_list[which_info].info;
546     
547     	if (info_descp)
548     		*info_descp = label_list[which_info].desc;
549     
550     	*placeptr = which_info + 1;
551     
552     	return(0);
553     }
554     
555     
556     int
557     labelcl_info_replace_IDX(devfs_handle_t de,
558     			int index,
559     			arbitrary_info_t info,
560     			arbitrary_info_t *old_info)
561     {
562     	arbitrary_info_t *info_list_IDX;
563     	labelcl_info_t	*labelcl_info = NULL;
564     
565     	if (de == NULL) {
566     		printk(KERN_ALERT "labelcl: NULL devfs handle given.\n");
567     		return(-1);
568     	}
569     
570     	labelcl_info = devfs_get_info(de);
571     	if (labelcl_info == NULL) {
572     		printk(KERN_ALERT "labelcl: Entry does not have info pointer.\n");
573     		return(-1);
574     	}
575     
576     	if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
577     		return(-1);
578     
579     	if ( (index < 0) || (index >= HWGRAPH_NUM_INDEX_INFO) )
580     		return(-1);
581     
582     	/*
583     	 * Replace information at the appropriate index in this vertex with 
584     	 * the new info.
585     	 */
586     	info_list_IDX = labelcl_info->IDX_list;
587     	if (old_info != NULL)
588     		*old_info = info_list_IDX[index];
589     	info_list_IDX[index] = info;
590     
591     	return(0);
592     
593     }
594     
595     /*
596      * labelcl_info_connectpt_set - Sets the connectpt.
597      */
598     int
599     labelcl_info_connectpt_set(struct devfs_entry *de,
600     			  struct devfs_entry *connect_de)
601     {
602     	arbitrary_info_t old_info;
603     	int	rv;
604     
605     	rv = labelcl_info_replace_IDX(de, HWGRAPH_CONNECTPT, 
606     		(arbitrary_info_t) connect_de, &old_info);
607     
608     	if (rv) {
609     		return(rv);
610     	}
611     
612     	return(0);
613     }
614     
615     
616     /*
617      * labelcl_info_get_IDX - Returns the information pointed at by index.
618      *
619      */
620     int
621     labelcl_info_get_IDX(devfs_handle_t de,
622     			int index,
623     			arbitrary_info_t *info)
624     {
625     	arbitrary_info_t *info_list_IDX;
626     	labelcl_info_t	*labelcl_info = NULL;
627     
628     	if (de == NULL)
629     		return(-1);
630     
631     	labelcl_info = devfs_get_info(de);
632     	if (labelcl_info == NULL)
633     		return(-1);
634     
635     	if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
636     		return(-1);
637     
638     	if ( (index < 0) || (index >= HWGRAPH_NUM_INDEX_INFO) )
639     		return(-1);
640     
641     	/*
642     	 * Return information at the appropriate index in this vertex.
643     	 */
644     	info_list_IDX = labelcl_info->IDX_list;
645     	if (info != NULL)
646     		*info = info_list_IDX[index];
647     
648     	return(0);
649     }
650     
651     /*
652      * labelcl_info_connectpt_get - Retrieve the connect point for a device entry.
653      */
654     struct devfs_entry *
655     labelcl_info_connectpt_get(struct devfs_entry *de)
656     {
657     	int rv;
658     	arbitrary_info_t info;
659     
660     	rv = labelcl_info_get_IDX(de, HWGRAPH_CONNECTPT, &info);
661     	if (rv)
662     		return(NULL);
663     
664     	return((struct devfs_entry *)info);
665     }
666