Whamcloud - gitweb
LU-8001 nodemap: fix null deref when reclassifying
[fs/lustre-release.git] / lustre / ptlrpc / nodemap_member.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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 version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (C) 2013, Trustees of Indiana University
24  * Author: Joshua Walgenbach <jjw@iu.edu>
25  */
26 #include <linux/module.h>
27 #include <lustre_net.h>
28 #include <obd_class.h>
29 #include "nodemap_internal.h"
30
31 #define HASH_NODEMAP_MEMBER_BKT_BITS 3
32 #define HASH_NODEMAP_MEMBER_CUR_BITS 3
33 #define HASH_NODEMAP_MEMBER_MAX_BITS 7
34
35 /**
36  * Delete a member from a member list
37  *
38  * \param       nodemap         nodemap containing list
39  * \param       exp             export member to delete
40  */
41 void nm_member_del(struct lu_nodemap *nodemap, struct obd_export *exp)
42 {
43         mutex_lock(&nodemap->nm_member_list_lock);
44         list_del_init(&exp->exp_target_data.ted_nodemap_member);
45         mutex_unlock(&nodemap->nm_member_list_lock);
46
47         exp->exp_target_data.ted_nodemap = NULL;
48         class_export_put(exp);
49 }
50
51 /**
52  * Delete a member list from a nodemap
53  *
54  * \param       nodemap         nodemap to remove the list from
55  */
56 void nm_member_delete_list(struct lu_nodemap *nodemap)
57 {
58         struct obd_export *exp;
59         struct obd_export *tmp;
60
61         mutex_lock(&nodemap->nm_member_list_lock);
62         list_for_each_entry_safe(exp, tmp, &nodemap->nm_member_list,
63                                  exp_target_data.ted_nodemap_member) {
64                 exp->exp_target_data.ted_nodemap = NULL;
65                 list_del_init(&exp->exp_target_data.ted_nodemap_member);
66                 class_export_put(exp);
67         }
68         mutex_unlock(&nodemap->nm_member_list_lock);
69 }
70
71 /**
72  * Add a member export to a nodemap
73  *
74  * \param       nodemap         nodemap to add to
75  * \param       exp             obd_export to add
76  * \retval      -EEXIST         export is already part of a different nodemap
77  * \retval      -EINVAL         export is NULL
78  */
79 int nm_member_add(struct lu_nodemap *nodemap, struct obd_export *exp)
80 {
81         if (exp == NULL) {
82                 CWARN("attempted to add null export to nodemap %s\n",
83                       nodemap->nm_name);
84                 return -EINVAL;
85         }
86
87         if (exp->exp_target_data.ted_nodemap != NULL &&
88             !list_empty(&exp->exp_target_data.ted_nodemap_member)) {
89                 /* export is already member of nodemap */
90                 if (exp->exp_target_data.ted_nodemap == nodemap)
91                         return 0;
92
93                 /* possibly reconnecting while about to be reclassified */
94                 CWARN("export %p %s already hashed, failed to add to "
95                       "nodemap %s already member of %s\n", exp,
96                       exp->exp_client_uuid.uuid,
97                       nodemap->nm_name,
98                       (exp->exp_target_data.ted_nodemap == NULL) ? "unknown" :
99                                 exp->exp_target_data.ted_nodemap->nm_name);
100                 return -EEXIST;
101         }
102
103         class_export_get(exp);
104         exp->exp_target_data.ted_nodemap = nodemap;
105         mutex_lock(&nodemap->nm_member_list_lock);
106         list_add(&exp->exp_target_data.ted_nodemap_member,
107                  &nodemap->nm_member_list);
108         mutex_unlock(&nodemap->nm_member_list_lock);
109
110         return 0;
111 }
112
113 /**
114  * Revokes the locks on an export if it is attached to an MDT and not in
115  * recovery. As a performance enhancement, the lock revoking process could
116  * revoke only the locks that cover files affected by the nodemap change.
117  */
118 static void nm_member_exp_revoke(struct obd_export *exp)
119 {
120         struct obd_type *type = exp->exp_obd->obd_type;
121         if (strcmp(type->typ_name, LUSTRE_MDT_NAME) != 0)
122                 return;
123         if (exp->exp_obd->obd_recovering)
124                 return;
125
126         ldlm_revoke_export_locks(exp);
127 }
128
129 /**
130  * Reclassify the members of a nodemap after range changes or activation.
131  * This function reclassifies the members of a nodemap based on the member
132  * export's NID and the nodemap's new NID ranges. Exports that are no longer
133  * classified as being part of this nodemap are moved to the nodemap whose
134  * NID ranges contain the export's NID, and their locks are revoked.
135  *
136  * Callers should hold the active_config_lock and active_config
137  * nmc_range_tree_lock.
138  *
139  * \param       nodemap         nodemap with members to reclassify
140  */
141 void nm_member_reclassify_nodemap(struct lu_nodemap *nodemap)
142 {
143         struct obd_export *exp;
144         struct obd_export *tmp;
145         struct lu_nodemap *new_nodemap;
146
147         mutex_lock(&nodemap->nm_member_list_lock);
148         list_for_each_entry_safe(exp, tmp, &nodemap->nm_member_list,
149                                  exp_target_data.ted_nodemap_member) {
150                 struct ptlrpc_connection *conn = exp->exp_connection;
151
152                 /* if no conn assigned to this exp, reconnect will reclassify */
153                 if (conn)
154                         /* nodemap_classify_nid requires nmc_range_tree_lock */
155                         new_nodemap = nodemap_classify_nid(conn->c_peer.nid);
156                 else
157                         continue;
158
159                 if (new_nodemap != nodemap) {
160                         /* don't use member_del because ted_nodemap
161                          * should never be null
162                          */
163                         list_del_init(&exp->exp_target_data.ted_nodemap_member);
164                         exp->exp_target_data.ted_nodemap = new_nodemap;
165
166                         /* could deadlock if new_nodemap also reclassifying */
167                         mutex_lock(&new_nodemap->nm_member_list_lock);
168                         list_add(&exp->exp_target_data.ted_nodemap_member,
169                                  &new_nodemap->nm_member_list);
170                         mutex_unlock(&new_nodemap->nm_member_list_lock);
171                         nm_member_exp_revoke(exp);
172                 }
173
174                 /* This put won't destroy new_nodemap because any nodemap_del
175                  * call done on new_nodemap blocks on our active_config_lock
176                  */
177                 nodemap_putref(new_nodemap);
178         }
179         mutex_unlock(&nodemap->nm_member_list_lock);
180 }
181
182 /**
183  * Revoke the locks for member exports. Changing the idmap is
184  * akin to deleting the security context. If the locks are not
185  * canceled, the client could cache permissions that are no
186  * longer correct with the map.
187  *
188  * \param       nodemap         nodemap that has been altered
189  */
190 void nm_member_revoke_locks(struct lu_nodemap *nodemap)
191 {
192         struct obd_export *exp;
193         struct obd_export *tmp;
194
195         mutex_lock(&nodemap->nm_member_list_lock);
196         list_for_each_entry_safe(exp, tmp, &nodemap->nm_member_list,
197                             exp_target_data.ted_nodemap_member)
198                 nm_member_exp_revoke(exp);
199         mutex_unlock(&nodemap->nm_member_list_lock);
200 }