Whamcloud - gitweb
LU-9312 hsm: fix error handling around mdt_hsm_get_md_hsm()
[fs/lustre-release.git] / lustre / mdt / mdt_hsm_cdt_agent.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,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License version 2 for more details.  A copy is
14  * included in the COPYING file that accompanied this code.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * GPL HEADER END
21  */
22 /*
23  * (C) Copyright 2012 Commissariat a l'energie atomique et aux energies
24  *
25  * Copyright (c) 2016, Intel Corporation.
26  *     alternatives
27  *
28  */
29 /*
30  * lustre/mdt/mdt_hsm_cdt_agent.c
31  *
32  * Lustre HSM Coordinator
33  *
34  * Author: Jacques-Charles Lafoucriere <jacques-charles.lafoucriere@cea.fr>
35  * Author: Aurelien Degremont <aurelien.degremont@cea.fr>
36  */
37
38 #define DEBUG_SUBSYSTEM S_MDS
39
40 #include <obd.h>
41 #include <obd_support.h>
42 #include <lustre_export.h>
43 #include <lustre/lustre_user.h>
44 #include <lprocfs_status.h>
45 #include <lustre_kernelcomm.h>
46 #include "mdt_internal.h"
47
48 /*
49  * Agent external API
50  */
51
52 /*
53  * find a hsm_agent by uuid
54  * lock cdt_agent_lock needs to be held by caller
55  * \param cdt [IN] coordinator
56  * \param uuid [IN] agent UUID
57  * \retval hsm_agent pointer or NULL if not found
58  */
59 static struct hsm_agent *mdt_hsm_agent_lookup(struct coordinator *cdt,
60                                               const struct obd_uuid *uuid)
61 {
62         struct hsm_agent        *ha;
63
64         list_for_each_entry(ha, &cdt->cdt_agents, ha_list) {
65                 if (obd_uuid_equals(&ha->ha_uuid, uuid))
66                         return ha;
67         }
68         return NULL;
69 }
70
71 /**
72  * register a copy tool
73  * \param mti [IN] MDT context
74  * \param uuid [IN] client UUID to be registered
75  * \param count [IN] number of archives agent serves
76  * \param archive_id [IN] vector of archive number served by the copytool
77  * \retval 0 success
78  * \retval -ve failure
79  */
80 int mdt_hsm_agent_register(struct mdt_thread_info *mti,
81                            const struct obd_uuid *uuid,
82                            int nr_archives, __u32 *archive_id)
83 {
84         struct coordinator      *cdt = &mti->mti_mdt->mdt_coordinator;
85         struct hsm_agent        *ha, *tmp;
86         int                      rc;
87         ENTRY;
88
89         /* no coordinator started, so we cannot serve requests */
90         if (cdt->cdt_state == CDT_STOPPED) {
91                 LCONSOLE_WARN("HSM coordinator thread is not running - "
92                               "denying agent registration.\n");
93                 RETURN(-ENXIO);
94         }
95
96         OBD_ALLOC_PTR(ha);
97         if (ha == NULL)
98                 GOTO(out, rc = -ENOMEM);
99
100         ha->ha_uuid = *uuid;
101         ha->ha_archive_cnt = nr_archives;
102         if (ha->ha_archive_cnt != 0) {
103                 int sz;
104
105                 sz = ha->ha_archive_cnt * sizeof(*ha->ha_archive_id);
106                 OBD_ALLOC(ha->ha_archive_id, sz);
107                 if (ha->ha_archive_id == NULL)
108                         GOTO(out_free, rc = -ENOMEM);
109                 memcpy(ha->ha_archive_id, archive_id, sz);
110         }
111         atomic_set(&ha->ha_requests, 0);
112         atomic_set(&ha->ha_success, 0);
113         atomic_set(&ha->ha_failure, 0);
114
115         down_write(&cdt->cdt_agent_lock);
116         tmp = mdt_hsm_agent_lookup(cdt, uuid);
117         if (tmp != NULL) {
118                 LCONSOLE_WARN("HSM agent %s already registered\n",
119                               obd_uuid2str(uuid));
120                 up_write(&cdt->cdt_agent_lock);
121                 GOTO(out_free, rc = -EEXIST);
122         }
123
124         list_add_tail(&ha->ha_list, &cdt->cdt_agents);
125
126         if (ha->ha_archive_cnt == 0)
127                 CDEBUG(D_HSM, "agent %s registered for all archives\n",
128                        obd_uuid2str(&ha->ha_uuid));
129         else
130                 CDEBUG(D_HSM, "agent %s registered for %d archives\n",
131                        obd_uuid2str(&ha->ha_uuid), ha->ha_archive_cnt);
132
133         up_write(&cdt->cdt_agent_lock);
134         GOTO(out, rc = 0);
135
136 out_free:
137
138         if (ha != NULL && ha->ha_archive_id != NULL)
139                 OBD_FREE(ha->ha_archive_id,
140                          ha->ha_archive_cnt * sizeof(*ha->ha_archive_id));
141         if (ha != NULL)
142                 OBD_FREE_PTR(ha);
143 out:
144         return rc;
145 }
146
147 /**
148  * register a copy tool
149  * \param mti [IN] MDT context
150  * \param uuid [IN] uuid to be registered
151  * \param archive_mask [IN] bitmask of archive number served by the copytool
152  * \retval 0 success
153  * \retval -ve failure
154  */
155 int mdt_hsm_agent_register_mask(struct mdt_thread_info *mti,
156                                 const struct obd_uuid *uuid, __u32 archive_mask)
157 {
158         int              rc, i, nr_archives = 0;
159         __u32           *archive_id = NULL;
160         ENTRY;
161
162         nr_archives = hweight32(archive_mask);
163
164         if (nr_archives != 0) {
165                 OBD_ALLOC(archive_id, nr_archives * sizeof(*archive_id));
166                 if (!archive_id)
167                         RETURN(-ENOMEM);
168
169                 nr_archives = 0;
170                 for (i = 0; i < sizeof(archive_mask) * 8; i++) {
171                         if ((1 << i) & archive_mask) {
172                                 archive_id[nr_archives] = i + 1;
173                                 nr_archives++;
174                         }
175                 }
176         }
177
178         rc = mdt_hsm_agent_register(mti, uuid, nr_archives, archive_id);
179
180         if (archive_id != NULL)
181                 OBD_FREE(archive_id, nr_archives * sizeof(*archive_id));
182
183         RETURN(rc);
184 }
185
186 /**
187  * unregister a copy tool
188  * \param mti [IN] MDT context
189  * \param uuid [IN] uuid to be unregistered
190  * \retval 0 success
191  * \retval -ve failure
192  */
193 int mdt_hsm_agent_unregister(struct mdt_thread_info *mti,
194                              const struct obd_uuid *uuid)
195 {
196         struct coordinator      *cdt = &mti->mti_mdt->mdt_coordinator;
197         struct hsm_agent        *ha;
198         int                      rc;
199         ENTRY;
200
201         /* no coordinator started, so we cannot serve requests */
202         if (cdt->cdt_state == CDT_STOPPED)
203                 RETURN(-ENXIO);
204
205         down_write(&cdt->cdt_agent_lock);
206
207         ha = mdt_hsm_agent_lookup(cdt, uuid);
208         if (ha != NULL)
209                 list_del_init(&ha->ha_list);
210
211         up_write(&cdt->cdt_agent_lock);
212
213         if (ha == NULL)
214                 GOTO(out, rc = -ENOENT);
215
216         if (ha->ha_archive_cnt != 0)
217                 OBD_FREE(ha->ha_archive_id,
218                          ha->ha_archive_cnt * sizeof(*ha->ha_archive_id));
219         OBD_FREE_PTR(ha);
220
221         GOTO(out, rc = 0);
222 out:
223         CDEBUG(D_HSM, "agent %s unregistration: %d\n", obd_uuid2str(uuid), rc);
224
225         return rc;
226 }
227
228 /**
229  * update agent statistics
230  * \param mdt [IN] MDT device
231  * \param succ_rq [IN] number of success
232  * \param fail_rq [IN] number of failure
233  * \param new_rq [IN] number of new requests
234  * \param uuid [IN] agent uuid
235  * if all counters == 0, clear counters
236  * \retval 0 success
237  * \retval -ve failure
238  */
239 int mdt_hsm_agent_update_statistics(struct coordinator *cdt,
240                                     int succ_rq, int fail_rq, int new_rq,
241                                     const struct obd_uuid *uuid)
242 {
243         struct hsm_agent        *ha;
244         int                      rc;
245         ENTRY;
246
247         down_read(&cdt->cdt_agent_lock);
248         list_for_each_entry(ha, &cdt->cdt_agents, ha_list) {
249                 if (obd_uuid_equals(&ha->ha_uuid, uuid)) {
250                         if (succ_rq == 0 && fail_rq == 0 && new_rq == 0) {
251                                 atomic_set(&ha->ha_success, 0);
252                                 atomic_set(&ha->ha_failure, 0);
253                                 atomic_set(&ha->ha_requests, 0);
254                         } else {
255                                 atomic_add(succ_rq, &ha->ha_success);
256                                 atomic_add(fail_rq, &ha->ha_failure);
257                                 atomic_add(new_rq, &ha->ha_requests);
258                                 atomic_sub(succ_rq, &ha->ha_requests);
259                                 atomic_sub(fail_rq, &ha->ha_requests);
260                         }
261                         GOTO(out, rc = 0);
262                 }
263
264         }
265         rc = -ENOENT;
266 out:
267         up_read(&cdt->cdt_agent_lock);
268         RETURN(rc);
269 }
270
271 /**
272  * find the best agent
273  * \param cdt [IN] coordinator
274  * \param archive [IN] archive number
275  * \param uuid [OUT] agent who can serve archive
276  * \retval 0 success
277  * \retval -ve failure
278  */
279 int mdt_hsm_find_best_agent(struct coordinator *cdt, __u32 archive,
280                             struct obd_uuid *uuid)
281 {
282         int                      rc = -EAGAIN, i, load = -1;
283         struct hsm_agent        *ha;
284         ENTRY;
285
286         /* Choose an export to send a copytool req to */
287         down_read(&cdt->cdt_agent_lock);
288         list_for_each_entry(ha, &cdt->cdt_agents, ha_list) {
289                 for (i = 0; (i < ha->ha_archive_cnt) &&
290                               (ha->ha_archive_id[i] != archive); i++) {
291                         /* nothing to do, just skip unmatching records */
292                 }
293
294                 /* archive count == 0 means copy tool serves any backend */
295                 if (ha->ha_archive_cnt != 0 && i == ha->ha_archive_cnt)
296                         continue;
297
298                 if (load == -1 || load > atomic_read(&ha->ha_requests)) {
299                         load = atomic_read(&ha->ha_requests);
300                         *uuid = ha->ha_uuid;
301                         rc = 0;
302                 }
303                 if (atomic_read(&ha->ha_requests) == 0)
304                         break;
305         }
306         up_read(&cdt->cdt_agent_lock);
307
308         RETURN(rc);
309 }
310
311 int mdt_hsm_send_action_to_each_archive(struct mdt_thread_info *mti,
312                                     struct hsm_action_item *hai)
313 {
314         __u64 compound_id;
315         struct hsm_agent *ha;
316         __u32 archive_mask = 0;
317         struct coordinator *cdt = &mti->mti_mdt->mdt_coordinator;
318         int i;
319         /* return error by default in case all archive_ids have unregistered */
320         int rc = -EAGAIN;
321         ENTRY;
322
323         /* send action to all registered archive_ids */
324         down_read(&cdt->cdt_agent_lock);
325         list_for_each_entry(ha, &cdt->cdt_agents, ha_list) {
326                 for (i = 0; (i < ha->ha_archive_cnt); i++) {
327                         /* only send once for each archive_id */
328                         if ((1 << ha->ha_archive_id[i]) & archive_mask)
329                                 continue;
330                         archive_mask |= (1 << ha->ha_archive_id[i]);
331
332                         /* XXX: instead of creating one request record per
333                          * new action, it could make sense to gather
334                          * all for the same archive_id as one compound
335                          * request/id, like in mdt_hsm_add_actions() ?? */
336                         compound_id = atomic_inc_return(&cdt->cdt_compound_id);
337                         rc = mdt_agent_record_add(mti->mti_env, mti->mti_mdt,
338                                                   compound_id,
339                                                   ha->ha_archive_id[i], 0,
340                                                   hai);
341                         if (rc) {
342                                 CERROR("%s: unable to add HSM remove request "
343                                        "for "DFID": rc=%d\n",
344                                        mdt_obd_name(mti->mti_mdt),
345                                        PFID(&hai->hai_fid), rc);
346                                 break;
347                         } else {
348                                 CDEBUG(D_HSM, "%s: added HSM remove request "
349                                        "for "DFID", archive_id=%d\n",
350                                        mdt_obd_name(mti->mti_mdt),
351                                        PFID(&hai->hai_fid),
352                                        ha->ha_archive_id[i]);
353                         }
354                 }
355                 /* early exit from loop due to error? */
356                 if (i != ha->ha_archive_cnt)
357                         break;
358         }
359         up_read(&cdt->cdt_agent_lock);
360
361         RETURN(rc);
362 }
363
364 /**
365  * send a compound request to the agent
366  * \param mti [IN] context
367  * \param hal [IN] request (can be a kuc payload)
368  * \param purge [IN] purge mode (no record)
369  * \retval 0 success
370  * \retval -ve failure
371  * This function supposes:
372  *  - all actions are for the same archive number
373  *  - in case of cancel, all cancel are for the same agent
374  * This implies that request split has to be done
375  *  before when building the hal
376  */
377 int mdt_hsm_agent_send(struct mdt_thread_info *mti,
378                        struct hsm_action_list *hal, bool purge)
379 {
380         struct obd_export       *exp;
381         struct mdt_device       *mdt = mti->mti_mdt;
382         struct coordinator      *cdt = &mti->mti_mdt->mdt_coordinator;
383         struct hsm_action_list  *buf = NULL;
384         struct hsm_action_item  *hai;
385         struct obd_uuid          uuid;
386         int                      len, i, rc = 0;
387         bool                     fail_request;
388         bool                     is_registered = false;
389         ENTRY;
390
391         rc = mdt_hsm_find_best_agent(cdt, hal->hal_archive_id, &uuid);
392         if (rc && hal->hal_archive_id == 0) {
393                 uint notrmcount = 0;
394                 int rc2 = 0;
395
396                 /* special case of remove requests with no archive_id specified,
397                  * and no agent registered to serve all archives, then create a
398                  * set of new requests, each to be sent to each registered
399                  * archives.
400                  * Todo so, find all HSMA_REMOVE entries, and then :
401                  *     _ set completed status as SUCCESS (or FAIL?)
402                  *     _ create a new LLOG record for each archive_id
403                  *       presently being served by any CT
404                  */
405                 hai = hai_first(hal);
406                 for (i = 0; i < hal->hal_count; i++,
407                      hai = hai_next(hai)) {
408                         /* only removes are concerned */
409                         if (hai->hai_action != HSMA_REMOVE) {
410                                 /* count if other actions than HSMA_REMOVE,
411                                  * to return original error/rc */
412                                 notrmcount++;
413                                 continue;
414                         }
415
416                         /* send remove request to all registered archive_ids */
417                         rc2 = mdt_hsm_send_action_to_each_archive(mti, hai);
418                         if (rc2)
419                                 break;
420
421                         /* only update original request as SUCCEED if it has
422                          * been successfully broadcasted to all available
423                          * archive_ids
424                          * XXX: this should only cause duplicates to be sent,
425                          * unless a method to record already successfully
426                          * reached archive_ids is implemented */
427                         rc2 = mdt_agent_record_update(mti->mti_env, mdt,
428                                                      &hai->hai_cookie,
429                                                      1, ARS_SUCCEED);
430                         if (rc2) {
431                                 CERROR("%s: mdt_agent_record_update() "
432                                       "failed, cannot update "
433                                       "status to %s for cookie "
434                                       "%#llx: rc = %d\n",
435                                       mdt_obd_name(mdt),
436                                       agent_req_status2name(ARS_SUCCEED),
437                                       hai->hai_cookie, rc2);
438                                 break;
439                         }
440                 }
441                 /* only remove requests with archive_id=0 */
442                 if (notrmcount == 0)
443                         RETURN(rc2);
444
445         }
446
447         if (rc) {
448                 CERROR("%s: Cannot find agent for archive %d: rc = %d\n",
449                        mdt_obd_name(mdt), hal->hal_archive_id, rc);
450                 RETURN(rc);
451         }
452
453         CDEBUG(D_HSM, "Agent %s selected for archive %d\n", obd_uuid2str(&uuid),
454                hal->hal_archive_id);
455
456         len = hal_size(hal);
457         buf = kuc_alloc(len, KUC_TRANSPORT_HSM, HMT_ACTION_LIST);
458         if (IS_ERR(buf))
459                 RETURN(PTR_ERR(buf));
460         memcpy(buf, hal, len);
461
462         /* Check if request is still valid (cf file hsm flags) */
463         fail_request = false;
464         hai = hai_first(hal);
465         for (i = 0; i < hal->hal_count; i++, hai = hai_next(hai)) {
466                 struct mdt_object *obj;
467                 struct md_hsm hsm;
468
469                 if (hai->hai_action == HSMA_CANCEL)
470                         continue;
471
472                 obj = mdt_hsm_get_md_hsm(mti, &hai->hai_fid, &hsm);
473                 if (!IS_ERR(obj)) {
474                         mdt_object_put(mti->mti_env, obj);
475                 } else if (PTR_ERR(obj) == -ENOENT) {
476                         if (hai->hai_action == HSMA_REMOVE)
477                                 continue;
478
479                         fail_request = true;
480                         rc = mdt_agent_record_update(mti->mti_env, mdt,
481                                                      &hai->hai_cookie,
482                                                      1, ARS_FAILED);
483                         if (rc < 0) {
484                                 CERROR("%s: mdt_agent_record_update() failed, "
485                                        "cannot update status to %s for cookie "
486                                        "%#llx: rc = %d\n",
487                                        mdt_obd_name(mdt),
488                                        agent_req_status2name(ARS_FAILED),
489                                        hai->hai_cookie, rc);
490                                 GOTO(out_buf, rc);
491                         }
492
493                         continue;
494                 } else {
495                         GOTO(out_buf, rc = PTR_ERR(obj));
496                 }
497
498                 if (!mdt_hsm_is_action_compat(hai, hal->hal_archive_id,
499                                               hal->hal_flags, &hsm)) {
500                         /* incompatible request, we abort the request */
501                         /* next time coordinator will wake up, it will
502                          * make the same compound with valid only
503                          * records */
504                         fail_request = true;
505                         rc = mdt_agent_record_update(mti->mti_env, mdt,
506                                                      &hai->hai_cookie,
507                                                      1, ARS_FAILED);
508                         if (rc) {
509                                 CERROR("%s: mdt_agent_record_update() failed, "
510                                        "cannot update status to %s for cookie "
511                                        "%#llx: rc = %d\n",
512                                        mdt_obd_name(mdt),
513                                        agent_req_status2name(ARS_FAILED),
514                                        hai->hai_cookie, rc);
515                                 GOTO(out_buf, rc);
516                         }
517
518                         /* if restore and record status updated, give
519                          * back granted layout lock */
520                         if (hai->hai_action == HSMA_RESTORE) {
521                                 struct cdt_restore_handle *crh = NULL;
522                                 struct mdt_object *obj = NULL;
523
524                                 mutex_lock(&cdt->cdt_restore_lock);
525                                 crh = mdt_hsm_restore_hdl_find(cdt,
526                                                                &hai->hai_fid);
527                                 if (crh != NULL)
528                                         list_del(&crh->crh_list);
529                                 mutex_unlock(&cdt->cdt_restore_lock);
530                                 obj = mdt_object_find(mti->mti_env,
531                                                       mti->mti_mdt,
532                                                       &hai->hai_fid);
533                                 if (!IS_ERR(obj) && crh != NULL)
534                                         mdt_object_unlock(mti, obj,
535                                                           &crh->crh_lh, 1);
536                                 if (crh != NULL)
537                                         OBD_SLAB_FREE_PTR(crh,
538                                                           mdt_hsm_cdt_kmem);
539                                 if (!IS_ERR(obj))
540                                         mdt_object_put(mti->mti_env, obj);
541                         }
542                 }
543         }
544
545         /* we found incompatible requests, so the compound cannot be send
546          * as is. Bad records have been invalidated in llog.
547          * Valid one will be reschedule next time coordinator will wake up
548          * So no need the rebuild a full valid compound request now
549          */
550         if (fail_request)
551                 GOTO(out_buf, rc = 0);
552
553         /* Cancel memory registration is useless for purge
554          * non registration avoid a deadlock :
555          * in case of failure we have to take the write lock
556          * to remove entry which conflict with the read loack needed
557          * by purge
558          */
559         if (!purge) {
560                 /* set is_registered even if failure because we may have
561                  * partial work done */
562                 is_registered = true;
563                 rc = mdt_hsm_add_hal(mti, hal, &uuid);
564                 if (rc)
565                         GOTO(out_buf, rc);
566         }
567
568         /* Uses the ldlm reverse import; this rpc will be seen by
569          *  the ldlm_callback_handler. Note this sends a request RPC
570          * from a server (MDT) to a client (MDC), backwards of normal comms.
571          */
572         exp = cfs_hash_lookup(mdt2obd_dev(mdt)->obd_uuid_hash, &uuid);
573         if (exp == NULL || exp->exp_disconnected) {
574                 if (exp != NULL)
575                         class_export_put(exp);
576                 /* This should clean up agents on evicted exports */
577                 rc = -ENOENT;
578                 CERROR("%s: agent uuid (%s) not found, unregistering:"
579                        " rc = %d\n",
580                        mdt_obd_name(mdt), obd_uuid2str(&uuid), rc);
581                 mdt_hsm_agent_unregister(mti, &uuid);
582                 GOTO(out, rc);
583         }
584
585         /* send request to agent */
586         rc = do_set_info_async(exp->exp_imp_reverse, LDLM_SET_INFO,
587                                LUSTRE_OBD_VERSION,
588                                sizeof(KEY_HSM_COPYTOOL_SEND),
589                                KEY_HSM_COPYTOOL_SEND,
590                                kuc_len(len), kuc_ptr(buf), NULL);
591
592         if (rc)
593                 CERROR("%s: cannot send request to agent '%s': rc = %d\n",
594                        mdt_obd_name(mdt), obd_uuid2str(&uuid), rc);
595
596         class_export_put(exp);
597
598         if (rc == -EPIPE) {
599                 CDEBUG(D_HSM, "Lost connection to agent '%s', unregistering\n",
600                        obd_uuid2str(&uuid));
601                 mdt_hsm_agent_unregister(mti, &uuid);
602         }
603
604 out:
605         if (rc != 0 && is_registered) {
606                 /* in case of error, we have to unregister requests */
607                 hai = hai_first(hal);
608                 for (i = 0; i < hal->hal_count; i++, hai = hai_next(hai)) {
609                         if (hai->hai_action == HSMA_CANCEL)
610                                 continue;
611                         mdt_cdt_remove_request(cdt, hai->hai_cookie);
612                 }
613         }
614
615 out_buf:
616         kuc_free(buf, len);
617
618         RETURN(rc);
619 }
620
621 /**
622  * update status of a request
623  * \param mti [IN]
624  * \param pgs [IN] progress of the copy tool
625  * \retval 0 success
626  * \retval -ve failure
627  */
628 int mdt_hsm_coordinator_update(struct mdt_thread_info *mti,
629                                struct hsm_progress_kernel *pgs)
630 {
631         int      rc;
632
633         ENTRY;
634         /* ask to coordinator to update request state and
635          * to record on disk the result */
636         rc = mdt_hsm_update_request_state(mti, pgs, 1);
637         RETURN(rc);
638 }
639
640 /**
641  * seq_file method called to start access to /proc file
642  */
643 static void *mdt_hsm_agent_proc_start(struct seq_file *s, loff_t *off)
644 {
645         struct mdt_device       *mdt = s->private;
646         struct coordinator      *cdt = &mdt->mdt_coordinator;
647         struct list_head        *pos;
648         loff_t                   i;
649         ENTRY;
650
651         down_read(&cdt->cdt_agent_lock);
652
653         if (list_empty(&cdt->cdt_agents))
654                 RETURN(NULL);
655
656         if (*off == 0)
657                 RETURN(SEQ_START_TOKEN);
658
659         i = 0;
660         list_for_each(pos, &cdt->cdt_agents) {
661                 i++;
662                 if (i >= *off)
663                         RETURN(pos);
664         }
665
666         RETURN(NULL);
667 }
668
669 /**
670  * seq_file method called to get next item
671  * just returns NULL at eof
672  */
673 static void *mdt_hsm_agent_proc_next(struct seq_file *s, void *v, loff_t *p)
674 {
675         struct mdt_device       *mdt = s->private;
676         struct coordinator      *cdt = &mdt->mdt_coordinator;
677         struct list_head        *pos = v;
678         ENTRY;
679
680         if (pos == SEQ_START_TOKEN)
681                 pos = cdt->cdt_agents.next;
682         else
683                 pos = pos->next;
684
685         (*p)++;
686         if (pos != &cdt->cdt_agents)
687                 RETURN(pos);
688
689         RETURN(NULL);
690 }
691
692 /**
693  */
694 static int mdt_hsm_agent_proc_show(struct seq_file *s, void *v)
695 {
696         struct list_head        *pos = v;
697         struct hsm_agent        *ha;
698         int                      i;
699         ENTRY;
700
701         if (pos == SEQ_START_TOKEN)
702                 RETURN(0);
703
704         ha = list_entry(pos, struct hsm_agent, ha_list);
705         seq_printf(s, "uuid=%s archive_id=", ha->ha_uuid.uuid);
706         if (ha->ha_archive_cnt == 0) {
707                 seq_printf(s, "ANY");
708         } else {
709                 seq_printf(s, "%d", ha->ha_archive_id[0]);
710                 for (i = 1; i < ha->ha_archive_cnt; i++)
711                         seq_printf(s, ",%d", ha->ha_archive_id[i]);
712         }
713
714         seq_printf(s, " requests=[current:%d ok:%d errors:%d]\n",
715                    atomic_read(&ha->ha_requests),
716                    atomic_read(&ha->ha_success),
717                    atomic_read(&ha->ha_failure));
718         RETURN(0);
719 }
720
721 /**
722  * seq_file method called to stop access to /proc file
723  */
724 static void mdt_hsm_agent_proc_stop(struct seq_file *s, void *v)
725 {
726         struct mdt_device       *mdt = s->private;
727         struct coordinator      *cdt = &mdt->mdt_coordinator;
728
729         up_read(&cdt->cdt_agent_lock);
730 }
731
732 /* hsm agent list proc functions */
733 static const struct seq_operations mdt_hsm_agent_proc_ops = {
734         .start  = mdt_hsm_agent_proc_start,
735         .next   = mdt_hsm_agent_proc_next,
736         .show   = mdt_hsm_agent_proc_show,
737         .stop   = mdt_hsm_agent_proc_stop,
738 };
739
740 /**
741  * public function called at open of /proc file to get
742  * list of agents
743  */
744 static int lprocfs_open_hsm_agent(struct inode *inode, struct file *file)
745 {
746         struct seq_file *s;
747         int              rc;
748         ENTRY;
749
750         rc = seq_open(file, &mdt_hsm_agent_proc_ops);
751         if (rc)
752                 RETURN(rc);
753
754         s = file->private_data;
755         s->private = PDE_DATA(inode);
756
757         RETURN(rc);
758 }
759
760 /* methods to access hsm agent list */
761 const struct file_operations mdt_hsm_agent_fops = {
762         .owner          = THIS_MODULE,
763         .open           = lprocfs_open_hsm_agent,
764         .read           = seq_read,
765         .llseek         = seq_lseek,
766         .release        = lprocfs_seq_release,
767 };
768