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