Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc-merge
[linux-2.6] / fs / xfs / xfs_behavior.c
1 /*
2  * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
3  * All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write the Free Software Foundation,
16  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 #include "xfs.h"
19
20 /*
21  * Source file used to associate/disassociate behaviors with virtualized
22  * objects.  See xfs_behavior.h for more information about behaviors, etc.
23  *
24  * The implementation is split between functions in this file and macros
25  * in xfs_behavior.h.
26  */
27
28 /*
29  * Insert a new behavior descriptor into a behavior chain.
30  *
31  * The behavior chain is ordered based on the 'position' number which
32  * lives in the first field of the ops vector (higher numbers first).
33  *
34  * Attemps to insert duplicate ops result in an EINVAL return code.
35  * Otherwise, return 0 to indicate success.
36  */
37 int
38 bhv_insert(bhv_head_t *bhp, bhv_desc_t *bdp)
39 {
40         bhv_desc_t      *curdesc, *prev;
41         int             position;
42
43         /*
44          * Validate the position value of the new behavior.
45          */
46         position = BHV_POSITION(bdp);
47         ASSERT(position >= BHV_POSITION_BASE && position <= BHV_POSITION_TOP);
48
49         /*
50          * Find location to insert behavior.  Check for duplicates.
51          */
52         prev = NULL;
53         for (curdesc = bhp->bh_first;
54              curdesc != NULL;
55              curdesc = curdesc->bd_next) {
56
57                 /* Check for duplication. */
58                 if (curdesc->bd_ops == bdp->bd_ops) {
59                         ASSERT(0);
60                         return EINVAL;
61                 }
62
63                 /* Find correct position */
64                 if (position >= BHV_POSITION(curdesc)) {
65                         ASSERT(position != BHV_POSITION(curdesc));
66                         break;          /* found it */
67                 }
68
69                 prev = curdesc;
70         }
71
72         if (prev == NULL) {
73                 /* insert at front of chain */
74                 bdp->bd_next = bhp->bh_first;
75                 bhp->bh_first = bdp;
76         } else {
77                 /* insert after prev */
78                 bdp->bd_next = prev->bd_next;
79                 prev->bd_next = bdp;
80         }
81
82         return 0;
83 }
84
85 /*
86  * Remove a behavior descriptor from a position in a behavior chain;
87  * the postition is guaranteed not to be the first position.
88  * Should only be called by the bhv_remove() macro.
89  */
90 void
91 bhv_remove_not_first(bhv_head_t *bhp, bhv_desc_t *bdp)
92 {
93         bhv_desc_t      *curdesc, *prev;
94
95         ASSERT(bhp->bh_first != NULL);
96         ASSERT(bhp->bh_first->bd_next != NULL);
97
98         prev = bhp->bh_first;
99         for (curdesc = bhp->bh_first->bd_next;
100              curdesc != NULL;
101              curdesc = curdesc->bd_next) {
102
103                 if (curdesc == bdp)
104                         break;          /* found it */
105                 prev = curdesc;
106         }
107
108         ASSERT(curdesc == bdp);
109         prev->bd_next = bdp->bd_next;   /* remove from after prev */
110 }
111
112 /*
113  * Look for a specific ops vector on the specified behavior chain.
114  * Return the associated behavior descriptor.  Or NULL, if not found.
115  */
116 bhv_desc_t *
117 bhv_lookup(bhv_head_t *bhp, void *ops)
118 {
119         bhv_desc_t      *curdesc;
120
121         for (curdesc = bhp->bh_first;
122              curdesc != NULL;
123              curdesc = curdesc->bd_next) {
124
125                 if (curdesc->bd_ops == ops)
126                         return curdesc;
127         }
128
129         return NULL;
130 }
131
132 /*
133  * Looks for the first behavior within a specified range of positions.
134  * Return the associated behavior descriptor.  Or NULL, if none found.
135  */
136 bhv_desc_t *
137 bhv_lookup_range(bhv_head_t *bhp, int low, int high)
138 {
139         bhv_desc_t      *curdesc;
140
141         for (curdesc = bhp->bh_first;
142              curdesc != NULL;
143              curdesc = curdesc->bd_next) {
144
145                 int     position = BHV_POSITION(curdesc);
146
147                 if (position <= high) {
148                         if (position >= low)
149                                 return curdesc;
150                         return NULL;
151                 }
152         }
153
154         return NULL;
155 }
156
157 /*
158  * Return the base behavior in the chain, or NULL if the chain
159  * is empty.
160  *
161  * The caller has not read locked the behavior chain, so acquire the
162  * lock before traversing the chain.
163  */
164 bhv_desc_t *
165 bhv_base(bhv_head_t *bhp)
166 {
167         bhv_desc_t      *curdesc;
168
169         for (curdesc = bhp->bh_first;
170              curdesc != NULL;
171              curdesc = curdesc->bd_next) {
172
173                 if (curdesc->bd_next == NULL) {
174                         return curdesc;
175                 }
176         }
177
178         return NULL;
179 }
180
181 void
182 bhv_head_init(
183         bhv_head_t *bhp,
184         char *name)
185 {
186         bhp->bh_first = NULL;
187 }
188
189 void
190 bhv_insert_initial(
191         bhv_head_t *bhp,
192         bhv_desc_t *bdp)
193 {
194         ASSERT(bhp->bh_first == NULL);
195         (bhp)->bh_first = bdp;
196 }
197
198 void
199 bhv_head_destroy(
200         bhv_head_t *bhp)
201 {
202         ASSERT(bhp->bh_first == NULL);
203 }