1 /*
2  * sel.c
3  *
4  * MontaVista IPMI code for handling the system event log
5  *
6  * Author: MontaVista Software, Inc.
7  *         Corey Minyard <minyard@mvista.com>
8  *         source@mvista.com
9  *
10  * Copyright 2002,2003,2004,2005 MontaVista Software Inc.
11  *
12  *  This program is free software; you can redistribute it and/or
13  *  modify it under the terms of the GNU Lesser General Public License
14  *  as published by the Free Software Foundation; either version 2 of
15  *  the License, or (at your option) any later version.
16  *
17  *
18  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  You should have received a copy of the GNU Lesser General Public
30  *  License along with this program; if not, write to the Free
31  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33 
34 #include <string.h>
35 #include <stdio.h>
36 
37 #include <OpenIPMI/ipmiif.h>
38 #include <OpenIPMI/ipmi_msgbits.h>
39 #include <OpenIPMI/ipmi_err.h>
40 
41 #include <OpenIPMI/internal/opq.h>
42 #include <OpenIPMI/internal/ilist.h>
43 #include <OpenIPMI/internal/ipmi_int.h>
44 #include <OpenIPMI/internal/ipmi_event.h>
45 #include <OpenIPMI/internal/ipmi_sel.h>
46 #include <OpenIPMI/internal/ipmi_domain.h>
47 #include <OpenIPMI/internal/ipmi_mc.h>
48 
49 #define MAX_SEL_FETCH_RETRIES 10
50 
51 typedef struct sel_fetch_handler_s
52 {
53     ipmi_sel_info_t     *sel;
54     ipmi_sels_fetched_t handler;
55     void                *cb_data;
56     int                 rv;
57 
58     struct sel_fetch_handler_s *next;
59 } sel_fetch_handler_t;
60 
61 /* Holds an event in the list of events. */
62 typedef struct sel_event_holder_s
63 {
64     unsigned int deleted : 1;
65     unsigned int cancelled : 1;
66     unsigned int refcount;
67     ipmi_event_t *event;
68 } sel_event_holder_t;
69 
70 static sel_event_holder_t *
sel_event_holder_alloc(void)71 sel_event_holder_alloc(void)
72 {
73     sel_event_holder_t *holder = ipmi_mem_alloc(sizeof(*holder));
74 
75     if (!holder)
76 	return NULL;
77     holder->deleted = 0;
78     holder->cancelled = 0;
79     holder->refcount = 1;
80     holder->event = NULL;
81     return holder;
82 }
83 
84 static void
sel_event_holder_get(sel_event_holder_t * holder)85 sel_event_holder_get(sel_event_holder_t *holder)
86 {
87     holder->refcount++;
88 }
89 
90 static void
sel_event_holder_put(sel_event_holder_t * holder)91 sel_event_holder_put(sel_event_holder_t *holder)
92 {
93     holder->refcount--;
94     if (holder->refcount == 0) {
95 	ipmi_event_free(holder->event);
96 	ipmi_mem_free(holder);
97     }
98 }
99 
100 typedef struct sel_clear_req_s
101 {
102     ipmi_event_t *last_event;
103     struct sel_clear_req_s *next;
104 } sel_clear_req_t;
105 
106 #define SEL_NAME_LEN (IPMI_MC_NAME_LEN + 32)
107 
108 struct ipmi_sel_info_s
109 {
110     ipmi_mcid_t mc;
111 
112     /* LUN we are attached with. */
113     int         lun;
114 
115     uint8_t  major_version;
116     uint8_t  minor_version;
117     uint16_t entries;
118     uint32_t last_addition_timestamp;
119     uint32_t last_erase_timestamp;
120 
121     /* These are here to store the timestamps until the operation
122        completes.  Successfully.  Otherwise, if we restart the fetch,
123        it will have the timestamps set wrong and won't do the
124        fetch. */
125     uint32_t curr_addition_timestamp;
126     uint32_t curr_erase_timestamp;
127 
128     uint16_t free_bytes;
129     unsigned int overflow : 1;
130     unsigned int supports_delete_sel : 1;
131     unsigned int supports_partial_add_sel : 1;
132     unsigned int supports_reserve_sel : 1;
133     unsigned int supports_get_sel_allocation : 1;
134 
135     unsigned int fetched : 1;
136 
137     /* Has the SEL been destroyed?  This is here because of race
138        conditions in shutdown.  If we are currently in the process of
139        fetching SELs, we will allow a destroy operation to complete,
140        but we don't actually destroy the data until the SEL fetch
141        reaches a point were it can be stopped safely. */
142     unsigned int destroyed : 1;
143     unsigned int in_destroy : 1;
144 
145     /* Is a fetch in the queue or currently running? */
146     unsigned int in_fetch : 1;
147 
148     /* Something to call when the destroy is complete. */
149     ipmi_sel_destroyed_t destroy_handler;
150     void                 *destroy_cb_data;
151 
152 
153     /* When fetching the data in event-driven mode, these are the
154        variables that track what is going on. */
155     unsigned int           curr_rec_id;
156     unsigned int           next_rec_id;
157     unsigned int           reservation;
158     int                    sels_changed;
159     unsigned int           fetch_retry_count;
160     sel_fetch_handler_t    *fetch_handlers;
161 
162     /* When we start a fetch, we start with this id.  This is the last
163        one we successfully fetches (or 0 if it is not valid) so we can
164        find the next valid id to fetch. */
165     unsigned int           start_rec_id;
166     unsigned char          start_rec_id_data[14];
167 
168     /* A lock, primarily for handling race conditions fetching the data. */
169     os_hnd_lock_t *sel_lock;
170 
171     os_handler_t *os_hnd;
172 
173     /* This is the actual list of events and the number of non-deleted
174        events and the number of deleted events.  Note that events may
175        contain more items than num_sels, num_sels only counts the
176        number of non-deleted events in the list.  del_sels+num_sels
177        should be the number of events. */
178     ilist_t      *events;
179     unsigned int num_sels;
180     unsigned int del_sels;
181 
182     /* We serialize operations through here, since we are dealing with
183        a locked resource. */
184     opq_t *opq;
185 
186     ipmi_sel_new_event_handler_cb new_event_handler;
187     void                          *new_event_cb_data;
188 
189     char name[SEL_NAME_LEN];
190 
191     ipmi_domain_stat_t *sel_good_scans;
192     ipmi_domain_stat_t *sel_scan_lost_reservation;
193     ipmi_domain_stat_t *sel_fail_scan_lost_reservation;
194     ipmi_domain_stat_t *sel_received_events;
195     ipmi_domain_stat_t *sel_fetch_errors;
196 
197     ipmi_domain_stat_t *sel_good_clears;
198     ipmi_domain_stat_t *sel_clear_lost_reservation;
199     ipmi_domain_stat_t *sel_clear_errors;
200 
201     ipmi_domain_stat_t *sel_good_deletes;
202     ipmi_domain_stat_t *sel_delete_lost_reservation;
203     ipmi_domain_stat_t *sel_fail_delete_lost_reservation;
204     ipmi_domain_stat_t *sel_delete_errors;
205 };
206 
sel_lock(ipmi_sel_info_t * sel)207 static inline void sel_lock(ipmi_sel_info_t *sel)
208 {
209     if (sel->os_hnd->lock)
210 	sel->os_hnd->lock(sel->os_hnd, sel->sel_lock);
211 }
212 
sel_unlock(ipmi_sel_info_t * sel)213 static inline void sel_unlock(ipmi_sel_info_t *sel)
214 {
215     if (sel->os_hnd->lock)
216 	sel->os_hnd->unlock(sel->os_hnd, sel->sel_lock);
217 }
218 
219 static void
free_event(ilist_iter_t * iter,void * item,void * cb_data)220 free_event(ilist_iter_t *iter, void *item, void *cb_data)
221 {
222     sel_event_holder_t *holder = item;
223     sel_event_holder_put(holder);
224 }
225 
226 static void
free_events(ilist_t * events)227 free_events(ilist_t *events)
228 {
229     ilist_iter(events, free_event, NULL);
230 }
231 
232 static int
recid_search_cmp(void * item,void * cb_data)233 recid_search_cmp(void *item, void *cb_data)
234 {
235     sel_event_holder_t *holder = item;
236     unsigned int       recid = *((unsigned int *) cb_data);
237 
238     return ipmi_event_get_record_id(holder->event) == recid;
239 }
240 static sel_event_holder_t *
find_event(ilist_t * list,unsigned int recid)241 find_event(ilist_t *list, unsigned int recid)
242 {
243     return ilist_search(list, recid_search_cmp, &recid);
244 }
245 
246 static int
event_cmp(ipmi_event_t * event1,ipmi_event_t * event2)247 event_cmp(ipmi_event_t *event1, ipmi_event_t *event2)
248 {
249     int rv;
250     unsigned int        record_id1, record_id2;
251     unsigned int        type1, type2;
252     unsigned int        data_len1, data_len2;
253     const unsigned char *ptr1, *ptr2;
254 
255     rv = ipmi_cmp_mc_id(ipmi_event_get_mcid(event1),
256 			ipmi_event_get_mcid(event2));
257     if (rv)
258 	return rv;
259     record_id1 = ipmi_event_get_record_id(event1);
260     record_id2 = ipmi_event_get_record_id(event2);
261     if (record_id1 > record_id2)
262 	return 1;
263     if (record_id1 < record_id2)
264 	return -1;
265     type1 = ipmi_event_get_type(event1);
266     type2 = ipmi_event_get_type(event2);
267     if (type1 > type2)
268 	return 1;
269     if (type1 < type2)
270 	return -1;
271     data_len1 = ipmi_event_get_data_len(event1);
272     data_len2 = ipmi_event_get_data_len(event2);
273     if (data_len1 > data_len2)
274 	return 1;
275     if (data_len1 < data_len2)
276 	return -1;
277     ptr1 = ipmi_event_get_data_ptr(event1);
278     ptr2 = ipmi_event_get_data_ptr(event2);
279     return memcmp(ptr1, ptr2, data_len1);
280 }
281 
282 int
ipmi_sel_alloc(ipmi_mc_t * mc,unsigned int lun,ipmi_sel_info_t ** new_sel)283 ipmi_sel_alloc(ipmi_mc_t       *mc,
284 	       unsigned int    lun,
285 	       ipmi_sel_info_t **new_sel)
286 {
287     ipmi_sel_info_t *sel = NULL;
288     int             rv = 0;
289     ipmi_domain_t   *domain;
290     int             i;
291 
292     CHECK_MC_LOCK(mc);
293 
294     domain = ipmi_mc_get_domain(mc);
295 
296     if (lun >= 4)
297 	return EINVAL;
298 
299     sel = ipmi_mem_alloc(sizeof(*sel));
300     if (!sel) {
301 	rv = ENOMEM;
302 	goto out;
303     }
304     memset(sel, 0, sizeof(*sel));
305 
306     i = ipmi_mc_get_name(mc, sel->name, sizeof(sel->name));
307     snprintf(sel->name+i, sizeof(sel->name)-i, "(sel)");
308 
309     sel->events = alloc_ilist();
310     if (!sel->events) {
311 	rv = ENOMEM;
312 	goto out;
313     }
314 
315     sel->mc = ipmi_mc_convert_to_id(mc);
316     sel->destroyed = 0;
317     sel->in_destroy = 0;
318     sel->os_hnd = ipmi_domain_get_os_hnd(domain);
319     sel->sel_lock = NULL;
320     sel->fetched = 0;
321     sel->in_fetch = 0;
322     sel->num_sels = 0;
323     sel->del_sels = 0;
324     sel->destroy_handler = NULL;
325     sel->lun = lun;
326     sel->fetch_handlers = NULL;
327     sel->new_event_handler = NULL;
328 
329     sel->opq = opq_alloc(sel->os_hnd);
330     if (!sel->opq) {
331 	rv = ENOMEM;
332 	goto out;
333     }
334 
335     if (sel->os_hnd->create_lock) {
336 	rv = sel->os_hnd->create_lock(sel->os_hnd, &sel->sel_lock);
337 	if (rv)
338 	    goto out;
339     }
340 
341  out:
342     if (rv) {
343 	if (sel) {
344 	    if (sel->events)
345 		free_ilist(sel->events);
346 	    if (sel->opq)
347 		opq_destroy(sel->opq);
348 	    if (sel->sel_lock)
349 		sel->os_hnd->destroy_lock(sel->os_hnd, sel->sel_lock);
350 	    ipmi_mem_free(sel);
351 	}
352     } else {
353 	ipmi_domain_stat_register(domain, "sel_good_scans",
354 				  i_ipmi_mc_name(mc),
355 				  &sel->sel_good_scans);
356 	ipmi_domain_stat_register(domain, "sel_scan_lost_reservation",
357 				  i_ipmi_mc_name(mc),
358 				  &sel->sel_scan_lost_reservation);
359 	ipmi_domain_stat_register(domain, "sel_fail_scan_lost_reservation",
360 				  i_ipmi_mc_name(mc),
361 				  &sel->sel_fail_scan_lost_reservation);
362 	ipmi_domain_stat_register(domain, "sel_received_events",
363 				  i_ipmi_mc_name(mc),
364 				  &sel->sel_received_events);
365 	ipmi_domain_stat_register(domain, "sel_fetch_errors",
366 				  i_ipmi_mc_name(mc),
367 				  &sel->sel_fetch_errors);
368 	ipmi_domain_stat_register(domain, "sel_good_clears",
369 				  i_ipmi_mc_name(mc),
370 				  &sel->sel_good_clears);
371 	ipmi_domain_stat_register(domain, "sel_clear_lost_reservation",
372 				  i_ipmi_mc_name(mc),
373 				  &sel->sel_clear_lost_reservation);
374 	ipmi_domain_stat_register(domain, "sel_clear_errors",
375 				  i_ipmi_mc_name(mc),
376 				  &sel->sel_clear_errors);
377 	ipmi_domain_stat_register(domain, "sel_good_deletes",
378 				  i_ipmi_mc_name(mc),
379 				  &sel->sel_good_deletes);
380 	ipmi_domain_stat_register(domain, "sel_delete_lost_reservation",
381 				  i_ipmi_mc_name(mc),
382 				  &sel->sel_delete_lost_reservation);
383 	ipmi_domain_stat_register(domain, "sel_fail_delete_lost_reservation",
384 				  i_ipmi_mc_name(mc),
385 				  &sel->sel_fail_delete_lost_reservation);
386 	ipmi_domain_stat_register(domain, "sel_delete_errors",
387 				  i_ipmi_mc_name(mc),
388 				  &sel->sel_delete_errors);
389 	*new_sel = sel;
390     }
391     return rv;
392 }
393 
394 static void
internal_destroy_sel(ipmi_sel_info_t * sel)395 internal_destroy_sel(ipmi_sel_info_t *sel)
396 {
397     sel->in_destroy = 1;
398 
399     /* We don't have to have a valid ipmi to destroy an SEL, the are
400        designed to live after the ipmi has been destroyed. */
401 
402     if (sel->events) {
403 	free_events(sel->events);
404 	free_ilist(sel->events);
405     }
406     sel_unlock(sel);
407 
408     if (sel->opq)
409 	opq_destroy(sel->opq);
410 
411     if (sel->sel_lock)
412 	sel->os_hnd->destroy_lock(sel->os_hnd, sel->sel_lock);
413 
414     if (sel->sel_good_scans)
415 	ipmi_domain_stat_put(sel->sel_good_scans);
416     if (sel->sel_scan_lost_reservation)
417 	ipmi_domain_stat_put(sel->sel_scan_lost_reservation);
418     if (sel->sel_fail_scan_lost_reservation)
419 	ipmi_domain_stat_put(sel->sel_fail_scan_lost_reservation);
420     if (sel->sel_received_events)
421 	ipmi_domain_stat_put(sel->sel_received_events);
422     if (sel->sel_fetch_errors)
423 	ipmi_domain_stat_put(sel->sel_fetch_errors);
424     if (sel->sel_good_clears)
425 	ipmi_domain_stat_put(sel->sel_good_clears);
426     if (sel->sel_clear_lost_reservation)
427 	ipmi_domain_stat_put(sel->sel_clear_lost_reservation);
428     if (sel->sel_clear_errors)
429 	ipmi_domain_stat_put(sel->sel_clear_errors);
430     if (sel->sel_good_deletes)
431 	ipmi_domain_stat_put(sel->sel_good_deletes);
432     if (sel->sel_delete_lost_reservation)
433 	ipmi_domain_stat_put(sel->sel_delete_lost_reservation);
434     if (sel->sel_fail_delete_lost_reservation)
435 	ipmi_domain_stat_put(sel->sel_fail_delete_lost_reservation);
436     if (sel->sel_delete_errors)
437 	ipmi_domain_stat_put(sel->sel_delete_errors);
438 
439     /* Do this after we have gotten rid of all external dependencies,
440        but before it is free. */
441     if (sel->destroy_handler)
442 	sel->destroy_handler(sel, sel->destroy_cb_data);
443 
444     ipmi_mem_free(sel);
445 }
446 
447 int
ipmi_sel_destroy(ipmi_sel_info_t * sel,ipmi_sel_destroyed_t handler,void * cb_data)448 ipmi_sel_destroy(ipmi_sel_info_t      *sel,
449 		 ipmi_sel_destroyed_t handler,
450 		 void                 *cb_data)
451 {
452     /* We don't need the read lock, because the sels are stand-alone
453        after they are created (except for fetching SELs, of course). */
454     sel_lock(sel);
455     if (sel->destroyed) {
456 	sel_unlock(sel);
457 	return EINVAL;
458     }
459     sel->destroyed = 1;
460     sel->destroy_handler = handler;
461     sel->destroy_cb_data = cb_data;
462     if (opq_stuff_in_progress(sel->opq)) {
463 	/* It's currently doing something with callbacks, so let it be
464            destroyed in the handler, since we can't cancel the handler
465            or operation. */
466 	sel_unlock(sel);
467 	return 0;
468     }
469 
470     /* This unlocks the lock. */
471     internal_destroy_sel(sel);
472     return 0;
473 }
474 
475 /* This should be called with the sel locked.  It will unlock the sel
476    before returning. */
477 static void
fetch_complete(ipmi_sel_info_t * sel,int err,int do_opq_done)478 fetch_complete(ipmi_sel_info_t *sel, int err, int do_opq_done)
479 {
480     sel_fetch_handler_t *elem, *next;
481     int                 sels_changed;
482     unsigned int        num_sels;
483 
484     if (sel->in_destroy)
485 	goto out;
486 
487     sels_changed = sel->sels_changed;
488     num_sels = sel->num_sels;
489 
490     elem = sel->fetch_handlers;
491     sel->fetch_handlers = NULL;
492     sel->fetched = 1;
493     sel->in_fetch = 0;
494     sel_unlock(sel);
495 
496     while (elem) {
497 	next = elem->next;
498 	elem->next = NULL;
499 	if (elem->handler)
500 	    elem->handler(sel,
501 		          err,
502 		          sels_changed,
503 		          num_sels,
504 		          elem->cb_data);
505 	ipmi_mem_free(elem);
506 	elem = next;
507     }
508 
509     if (sel->destroyed) {
510 	sel_lock(sel);
511 	internal_destroy_sel(sel);
512 	/* Previous call releases lock. */
513 	return;
514     }
515 
516     if (do_opq_done)
517 	opq_op_done(sel->opq);
518     return;
519 
520  out:
521     sel_unlock(sel);
522 }
523 
524 static void
free_deleted_event(ilist_iter_t * iter,void * item,void * cb_data)525 free_deleted_event(ilist_iter_t *iter, void *item, void *cb_data)
526 {
527     sel_event_holder_t *holder = item;
528     ipmi_sel_info_t    *sel = cb_data;
529 
530     if (holder->deleted) {
531 	ilist_delete(iter);
532 	holder->cancelled = 1;
533 	sel->del_sels--;
534 	sel_event_holder_put(holder);
535     }
536 }
537 
538 static void
free_deleted_events(ipmi_sel_info_t * sel)539 free_deleted_events(ipmi_sel_info_t *sel)
540 {
541     ilist_iter(sel->events, free_deleted_event, sel);
542 }
543 
544 static void
handle_sel_clear(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)545 handle_sel_clear(ipmi_mc_t  *mc,
546 		 ipmi_msg_t *rsp,
547 		 void       *rsp_data)
548 {
549     sel_fetch_handler_t *elem = rsp_data;
550     ipmi_sel_info_t     *sel = elem->sel;
551 
552     sel_lock(sel);
553     if (sel->destroyed) {
554 	ipmi_log(IPMI_LOG_ERR_INFO,
555 		 "%ssel.c(handle_sel_clear): "
556 		 "SEL info was destroyed while an operation was in"
557 		 " progress(1)", sel->name);
558 	fetch_complete(sel, ECANCELED, 1);
559 	goto out;
560     }
561 
562     if (!mc) {
563 	ipmi_log(IPMI_LOG_ERR_INFO,
564 		 "%ssel.c(handle_sel_clear): "
565 		 "MC went away while SEL op was in progress",
566 		 sel->name);
567         fetch_complete(sel, ECANCELED, 1);
568 	goto out;
569     }
570 
571     if (rsp->data[0] == 0) {
572 	if (sel->sel_good_clears)
573 	    ipmi_domain_stat_add(sel->sel_good_clears, 1);
574 
575 	/* Success!  We can free the data. */
576 	free_deleted_events(sel);
577 	sel->del_sels = 0;
578     } else if (rsp->data[0] == IPMI_INVALID_RESERVATION_CC) {
579 	if (sel->sel_clear_lost_reservation)
580 	    ipmi_domain_stat_add(sel->sel_clear_lost_reservation, 1);
581     } else {
582 	if (sel->sel_clear_errors)
583 	    ipmi_domain_stat_add(sel->sel_clear_errors, 1);
584     }
585 
586     fetch_complete(sel, 0, 1);
587  out:
588     return;
589 }
590 
591 static int
send_sel_clear(sel_fetch_handler_t * elem,ipmi_mc_t * mc)592 send_sel_clear(sel_fetch_handler_t *elem, ipmi_mc_t *mc)
593 {
594     ipmi_sel_info_t *sel = elem->sel;
595     unsigned char   cmd_data[MAX_IPMI_DATA_SIZE];
596     ipmi_msg_t      cmd_msg;
597 
598     cmd_msg.data = cmd_data;
599     cmd_msg.netfn = IPMI_STORAGE_NETFN;
600     cmd_msg.cmd = IPMI_CLEAR_SEL_CMD;
601     cmd_msg.data_len = 6;
602     ipmi_set_uint16(cmd_msg.data, sel->reservation);
603     cmd_msg.data[2] = 'C';
604     cmd_msg.data[3] = 'L';
605     cmd_msg.data[4] = 'R';
606     cmd_msg.data[5] = 0xaa;
607 
608     return ipmi_mc_send_command(mc, sel->lun, &cmd_msg,
609 				handle_sel_clear, elem);
610 }
611 
612 static int start_fetch(void *cb_data, int shutdown);
613 
614 static void
handle_sel_data(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)615 handle_sel_data(ipmi_mc_t  *mc,
616 		ipmi_msg_t *rsp,
617 		void       *rsp_data)
618 {
619     sel_fetch_handler_t *elem = rsp_data;
620     ipmi_sel_info_t     *sel = elem->sel;
621     unsigned char       cmd_data[MAX_IPMI_DATA_SIZE];
622     ipmi_msg_t          cmd_msg;
623     int                 rv;
624     int                 event_is_new = 0;
625     ipmi_event_t        *del_event;
626     unsigned int        record_id;
627     ipmi_time_t         timestamp;
628     sel_event_holder_t  *holder;
629 
630 
631     sel_lock(sel);
632     if (sel->destroyed) {
633 	ipmi_log(IPMI_LOG_ERR_INFO,
634 		 "%ssel.c(handle_sel_data): "
635 		 "SEL info was destroyed while an operation was in"
636 		 " progress(2)", sel->name);
637 	fetch_complete(sel, ECANCELED, 1);
638 	goto out;
639     }
640 
641     if (!mc) {
642 	ipmi_log(IPMI_LOG_ERR_INFO,
643 		 "%ssel.c(handle_sel_data): "
644 		 "handle_sel_data: MC went away while SEL op was in progress",
645 		 sel->name);
646         fetch_complete(sel, ECANCELED, 1);
647 	goto out;
648     }
649 
650     if (rsp->data[0] == IPMI_INVALID_RESERVATION_CC) {
651 	/* We lost our reservation, restart the operation.  Only do
652            this so many times, in order to guarantee that this
653            completes.  Note that we do this so many times *per fetch*,
654            we do not reset the counter if we get a successful
655            operation.  This is because if this happens a lot during a
656            fetch, there is heavy contention for the SEL and someone
657            needs to drop out to allow everyone else to continue. */
658 	sel->fetch_retry_count++;
659 	if (sel->sel_scan_lost_reservation)
660 	    ipmi_domain_stat_add(sel->sel_scan_lost_reservation, 1);
661 	if (sel->fetch_retry_count > MAX_SEL_FETCH_RETRIES) {
662 	    if (sel->sel_fail_scan_lost_reservation)
663 		ipmi_domain_stat_add(sel->sel_fail_scan_lost_reservation, 1);
664 	    ipmi_log(IPMI_LOG_ERR_INFO,
665 		     "%ssel.c(handle_sel_data): "
666 		     "Too many lost reservations in SEL fetch",
667 		     sel->name);
668 	    fetch_complete(sel, EAGAIN, 1);
669 	    goto out;
670 	} else {
671 	    sel_unlock(sel);
672 	    start_fetch(elem, 0);
673 	    goto out;
674 	}
675     }
676     if ((rsp->data[0] == 0) && (rsp->data_len < 19)) {
677 	ipmi_log(IPMI_LOG_ERR_INFO,
678 		 "%ssel.c(handle_sel_data): "
679 		 "Received a short SEL data message",
680 		 sel->name);
681 	fetch_complete(sel, EINVAL, 1);
682 	goto out;
683     }
684 
685     if ((rsp->data[0] != 0)
686 	|| ((sel->start_rec_id != 0) && (sel->start_rec_id == sel->curr_rec_id)
687 	    && (memcmp(sel->start_rec_id_data, rsp->data+5, 14) != 0)))
688     {
689 	/* We got an error fetching the current id, or the current
690 	   id's data was for our "start" record and it doesn't match
691 	   the one we fetched, so it has changed.  We have to start
692 	   over or handle the error. */
693 	if (sel->start_rec_id != 0) {
694 	    /* If we get a fetch error and it is not a lost
695 	       reservation, it may be that another system deleted our
696 	       "current" record.  Start over from the beginning of the
697 	       SEL. */
698 	    sel->start_rec_id = 0;
699 	    sel->curr_rec_id = 0;
700 	    del_event = NULL;
701 	    goto start_request_sel_data;
702 	}
703 	if (sel->sel_fetch_errors)
704 	    ipmi_domain_stat_add(sel->sel_fetch_errors, 1);
705 	ipmi_log(IPMI_LOG_ERR_INFO,
706 		 "%ssel.c(handle_sel_data): "
707 		 "IPMI error from SEL fetch: %x",
708 		 sel->name, rsp->data[0]);
709 	fetch_complete(sel, IPMI_IPMI_ERR_VAL(rsp->data[0]), 1);
710 	goto out;
711     }
712 
713     sel->next_rec_id = ipmi_get_uint16(rsp->data+1);
714 
715     record_id = ipmi_get_uint16(rsp->data+3);
716 
717     if (rsp->data[5] < 0xe0)
718 	timestamp = ipmi_seconds_to_time(ipmi_get_uint32(rsp->data+6));
719     else
720 	timestamp = -1;
721     del_event = ipmi_event_alloc(ipmi_mc_convert_to_id(mc),
722 				 record_id,
723 				 rsp->data[5],
724 				 timestamp,
725 				 rsp->data+6,
726 				 13);
727     if (!del_event) {
728 	ipmi_log(IPMI_LOG_ERR_INFO,
729 		 "%ssel.c(handle_sel_data): "
730 		 "Could not allocate event for SEL",
731 		 sel->name);
732 	fetch_complete(sel, ENOMEM, 1);
733 	goto out;
734     }
735 
736     if ((timestamp > 0) && (timestamp < ipmi_mc_get_startup_SEL_time(mc)))
737 	ipmi_event_set_is_old(del_event, 1);
738 
739     holder = find_event(sel->events, record_id);
740     if (!holder) {
741 	holder = sel_event_holder_alloc();
742 	if (!holder) {
743 	    ipmi_log(IPMI_LOG_ERR_INFO,
744 		     "%ssel.c(handle_sel_data): "
745 		     "Could not allocate log information for SEL",
746 		     sel->name);
747 	    fetch_complete(sel, ENOMEM, 1);
748 	    goto out;
749 	}
750 	if (!ilist_add_tail(sel->events, holder, NULL)) {
751 	    ipmi_mem_free(holder);
752 	    ipmi_log(IPMI_LOG_ERR_INFO,
753 		     "%ssel.c(handle_sel_data): "
754 		     "Could not link log onto the log linked list",
755 		     sel->name);
756 	    fetch_complete(sel, ENOMEM, 1);
757 	    goto out;
758 	}
759 	holder->event = del_event;
760 	holder->deleted = 0;
761 	event_is_new = 1;
762 	sel->num_sels++;
763 	if (sel->sel_received_events)
764 	    ipmi_domain_stat_add(sel->sel_received_events, 1);
765     } else if (event_cmp(del_event, holder->event) != 0) {
766 	/* It's a new event in an old slot, so overwrite the old
767            event. */
768 
769 	ipmi_event_free(holder->event);
770 	holder->event = del_event;
771 	if (holder->deleted) {
772 	    holder->deleted = 0;
773 	    sel->num_sels++;
774 	    sel->del_sels--;
775 	}
776 	event_is_new = 1;
777 	if (sel->sel_received_events)
778 	    ipmi_domain_stat_add(sel->sel_received_events, 1);
779     } else {
780 	ipmi_event_free(del_event);
781     }
782 
783     if (sel->next_rec_id == 0xFFFF) {
784 	/* Only set the timestamps if the SEL fetch completed
785 	   successfully.  If we were unsuccessful, we want to redo the
786 	   operation so don't set the timestamps. */
787 	sel->last_addition_timestamp = sel->curr_addition_timestamp;
788 	sel->last_erase_timestamp = sel->curr_erase_timestamp;
789 
790 	/* To avoid confusion, deliver the event before we deliver fetch
791            complete. */
792 	if (event_is_new && sel->new_event_handler) {
793 	    ipmi_sel_new_event_handler_cb handler = sel->new_event_handler;
794 	    void                          *cb_data = sel->new_event_cb_data;
795 	    sel_unlock(sel);
796 	    handler(sel, mc, del_event, cb_data);
797 	    sel_lock(sel);
798 	}
799 
800 	if (sel->sel_good_scans)
801 	    ipmi_domain_stat_add(sel->sel_good_scans, 1);
802 
803 	/* If the operation completed successfully and everything in
804 	   our SEL is deleted, then clear it with our old reservation.
805 	   We also do the clear if the overflow flag is set; on some
806 	   systems this operation clears the overflow flag. */
807 	if ((sel->num_sels == 0)
808 	    && ((!ilist_empty(sel->events)) || sel->overflow))
809 	{
810 	    /* We don't care if this fails, because it will just
811 	       happen again later if it does. */
812 	    rv = send_sel_clear(elem, mc);
813 	    if (rv) {
814 		fetch_complete(sel, 0, 1);
815 		goto out;
816 	    }
817 	    rv = 0;
818 	    goto out_unlock;
819 	} else {
820 	    fetch_complete(sel, 0, 1);
821 	    goto out;
822 	}
823     }
824     sel->start_rec_id = sel->curr_rec_id;
825     memcpy(sel->start_rec_id_data, rsp->data+5, 14);
826     sel->curr_rec_id = sel->next_rec_id;
827 
828  start_request_sel_data:
829     /* Request some more data. */
830     cmd_msg.data = cmd_data;
831     cmd_msg.netfn = IPMI_STORAGE_NETFN;
832     cmd_msg.cmd = IPMI_GET_SEL_ENTRY_CMD;
833     cmd_msg.data_len = 6;
834     ipmi_set_uint16(cmd_msg.data, sel->reservation);
835     ipmi_set_uint16(cmd_msg.data+2, sel->curr_rec_id);
836     cmd_msg.data[4] = 0;
837     cmd_msg.data[5] = 0xff;
838     rv = ipmi_mc_send_command(mc, sel->lun, &cmd_msg, handle_sel_data, elem);
839     if (rv) {
840 	ipmi_log(IPMI_LOG_ERR_INFO,
841 		 "%ssel.c(handle_sel_clear): "
842 		 "Could not send SEL fetch command: %x", sel->name, rv);
843 	fetch_complete(sel, rv, 1);
844 	goto out;
845     }
846 
847     if (event_is_new && sel->new_event_handler) {
848 	ipmi_sel_new_event_handler_cb handler = sel->new_event_handler;
849 	void                          *cb_data = sel->new_event_cb_data;
850 	sel_unlock(sel);
851 	handler(sel, mc, del_event, cb_data);
852 	sel_lock(sel);
853     }
854  out_unlock:
855     sel_unlock(sel);
856  out:
857     return;
858 }
859 
860 /* Cheap hacks for broken hardware. */
861 static void
sel_fixups(ipmi_mc_t * mc,ipmi_sel_info_t * sel)862 sel_fixups(ipmi_mc_t *mc, ipmi_sel_info_t *sel)
863 {
864     unsigned int mfg_id, product_id;
865 
866     /* Fixups */
867     mfg_id = ipmi_mc_manufacturer_id(mc);
868     product_id = ipmi_mc_product_id(mc);
869     if ((mfg_id == 0x157) && (product_id == 0x841))
870 	/* Intel ATCA CMM mistakenly reports that it supports delete SEL */
871 	sel->supports_delete_sel = 0;
872 }
873 
874 static void
handle_sel_info(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)875 handle_sel_info(ipmi_mc_t  *mc,
876 		ipmi_msg_t *rsp,
877 		void       *rsp_data)
878 {
879     sel_fetch_handler_t *elem = rsp_data;
880     ipmi_sel_info_t     *sel = elem->sel;
881     unsigned char       cmd_data[MAX_IPMI_DATA_SIZE];
882     ipmi_msg_t          cmd_msg;
883     int                 rv;
884     uint32_t            add_timestamp;
885     uint32_t            erase_timestamp;
886     int                 fetched_num_sels;
887 
888     sel_lock(sel);
889     if (sel->destroyed) {
890 	ipmi_log(IPMI_LOG_ERR_INFO,
891 		 "%ssel.c(handle_sel_info): "
892 		 "SEL info was destroyed while an operation was in progress",
893 		 sel->name);
894 	fetch_complete(sel, ECANCELED, 1);
895 	goto out;
896     }
897 
898     if (!mc) {
899 	ipmi_log(IPMI_LOG_ERR_INFO,
900 		 "%ssel.c(handle_sel_info): "
901 		 "MC went away while SEL op was in progress",
902 		 sel->name);
903         fetch_complete(sel, ECANCELED, 1);
904 	goto out;
905     }
906 
907     if (rsp->data[0] != 0) {
908 	if (sel->sel_fetch_errors)
909 	    ipmi_domain_stat_add(sel->sel_fetch_errors, 1);
910 	ipmi_log(IPMI_LOG_ERR_INFO,
911 		 "%ssel.c(handle_sel_info): "
912 		 "IPMI error from SEL info fetch: %x",
913 		 sel->name, rsp->data[0]);
914 	fetch_complete(sel, IPMI_IPMI_ERR_VAL(rsp->data[0]), 1);
915 	goto out;
916     }
917 
918     if (rsp->data_len < 15) {
919 	if (sel->sel_fetch_errors)
920 	    ipmi_domain_stat_add(sel->sel_fetch_errors, 1);
921 	ipmi_log(IPMI_LOG_ERR_INFO,
922 		 "%ssel.c(handle_sel_info): SEL info too short", sel->name);
923 	fetch_complete(sel, EINVAL, 1);
924 	goto out;
925     }
926 
927     /* Pull pertinant info from the response. */
928     sel->major_version = rsp->data[1] & 0xf;
929     sel->minor_version = (rsp->data[1] >> 4) & 0xf;
930     fetched_num_sels = ipmi_get_uint16(rsp->data+2);
931     sel->entries = fetched_num_sels;
932     sel->free_bytes = ipmi_get_uint16(rsp->data+4);
933     sel->overflow = (rsp->data[14] & 0x80) == 0x80;
934     sel->supports_delete_sel = (rsp->data[14] & 0x08) == 0x08;
935     sel->supports_partial_add_sel = (rsp->data[14] & 0x04) == 0x04;
936     sel->supports_reserve_sel = (rsp->data[14] & 0x02) == 0x02;
937     sel->supports_get_sel_allocation = (rsp->data[14] & 0x01) == 0x01;
938 
939     add_timestamp = ipmi_get_uint32(rsp->data + 6);
940     erase_timestamp = ipmi_get_uint32(rsp->data + 10);
941 
942     sel_fixups(mc, sel);
943 
944     /* If the timestamps still match, no need to re-fetch the
945        repository.  Note that we only check the add timestamp.  We
946        don't care if things were deleted. */
947     if (sel->fetched && (add_timestamp == sel->last_addition_timestamp)) {
948 	/* If the operation completed successfully and everything in
949 	   our SEL is deleted, then clear it with our old reservation.
950 	   We also do the clear if the overflow flag is set; on some
951 	   systems this operation clears the overflow flag. */
952 	if ((sel->num_sels == 0)
953 	    && ((!ilist_empty(sel->events)) || sel->overflow))
954 	{
955 	    /* We don't care if this fails, because it will just
956 	       happen again later if it does. */
957 	    rv = send_sel_clear(elem, mc);
958 	    if (rv) {
959 		fetch_complete(sel, 0, 1);
960 		goto out;
961 	    }
962 	    goto out_unlock;
963 	} else {
964 	    fetch_complete(sel, 0, 1);
965 	    goto out;
966 	}
967     }
968 
969     sel->curr_addition_timestamp = add_timestamp;
970     sel->curr_erase_timestamp = erase_timestamp;
971 
972     sel->sels_changed = 1;
973     sel->next_rec_id = 0;
974 
975     if (fetched_num_sels == 0) {
976 	/* No sels, so there's nothing to do. */
977 
978 	/* Set the timestamps here, because they are not the same, but
979 	   there was nothing to do. */
980 	sel->last_addition_timestamp = sel->curr_addition_timestamp;
981 	sel->last_erase_timestamp = sel->curr_erase_timestamp;
982 	sel->start_rec_id = 0;
983 	sel->curr_rec_id = 0;
984 
985 	fetch_complete(sel, 0, 1);
986 	goto out;
987     }
988 
989     /* Fetch the first SEL entry. */
990     sel->curr_rec_id = sel->start_rec_id;
991     cmd_msg.data = cmd_data;
992     cmd_msg.netfn = IPMI_STORAGE_NETFN;
993     cmd_msg.cmd = IPMI_GET_SEL_ENTRY_CMD;
994     cmd_msg.data_len = 6;
995     ipmi_set_uint16(cmd_msg.data, sel->reservation);
996     ipmi_set_uint16(cmd_msg.data+2, sel->curr_rec_id);
997     cmd_msg.data[4] = 0;
998     cmd_msg.data[5] = 0xff;
999     rv = ipmi_mc_send_command(mc, sel->lun, &cmd_msg, handle_sel_data, elem);
1000     if (rv) {
1001 	ipmi_log(IPMI_LOG_ERR_INFO,
1002 		 "%ssel.c(handle_sel_info): "
1003 		 "Could not send first SEL fetch command: %x",
1004 		 sel->name, rv);
1005 	fetch_complete(sel, rv, 1);
1006 	goto out;
1007     }
1008  out_unlock:
1009     sel_unlock(sel);
1010  out:
1011     return;
1012 }
1013 
1014 static int
send_get_sel_info(sel_fetch_handler_t * elem,ipmi_mc_t * mc)1015 send_get_sel_info(sel_fetch_handler_t *elem, ipmi_mc_t *mc)
1016 {
1017     ipmi_sel_info_t *sel = elem->sel;
1018     ipmi_msg_t      cmd_msg;
1019 
1020     /* Fetch the repository info. */
1021     cmd_msg.netfn = IPMI_STORAGE_NETFN;
1022     cmd_msg.cmd = IPMI_GET_SEL_INFO_CMD;
1023     cmd_msg.data = NULL;
1024     cmd_msg.data_len = 0;
1025     return ipmi_mc_send_command(mc, sel->lun, &cmd_msg, handle_sel_info, elem);
1026 }
1027 
1028 static void
sel_handle_reservation(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)1029 sel_handle_reservation(ipmi_mc_t  *mc,
1030 		       ipmi_msg_t *rsp,
1031 		       void       *rsp_data)
1032 {
1033     sel_fetch_handler_t *elem = rsp_data;
1034     ipmi_sel_info_t     *sel = elem->sel;
1035     int                 rv;
1036 
1037     sel_lock(sel);
1038     if (sel->destroyed) {
1039 	ipmi_log(IPMI_LOG_ERR_INFO,
1040 		 "%ssel.c(sel_handle_reservation): "
1041 		 "SEL info was destroyed while an operation was in progress",
1042 		 sel->name);
1043 	fetch_complete(sel, ECANCELED, 1);
1044 	goto out;
1045     }
1046 
1047     if (!mc) {
1048 	ipmi_log(IPMI_LOG_ERR_INFO,
1049 		 "%ssel.c(sel_handle_reservation): "
1050 		 "MC went away while SEL op was in progress",
1051 		 sel->name);
1052         fetch_complete(sel, ECANCELED, 1);
1053 	goto out;
1054     }
1055 
1056     if (rsp->data[0] != 0) {
1057 	if (sel->sel_fetch_errors)
1058 	    ipmi_domain_stat_add(sel->sel_fetch_errors, 1);
1059 	ipmi_log(IPMI_LOG_ERR_INFO,
1060 		 "%ssel.c(sel_handle_reservation): "
1061 		 "Failed getting reservation", sel->name);
1062 	fetch_complete(sel, ENOSYS, 1);
1063 	goto out;
1064     } else if (rsp->data_len < 3) {
1065 	if (sel->sel_fetch_errors)
1066 	    ipmi_domain_stat_add(sel->sel_fetch_errors, 1);
1067 	ipmi_log(IPMI_LOG_ERR_INFO,
1068 		 "%ssel.c(sel_handle_reservation): "
1069 		 "got invalid reservation length", sel->name);
1070 	fetch_complete(sel, EINVAL, 1);
1071 	goto out;
1072     }
1073 
1074     sel->reservation = ipmi_get_uint16(rsp->data+1);
1075 
1076     rv = send_get_sel_info(elem, mc);
1077     if (rv) {
1078 	ipmi_log(IPMI_LOG_ERR_INFO,
1079 		 "%ssel.c(sel_handle_reservation): "
1080 		 "Could not send SEL info command: %x", sel->name, rv);
1081 	fetch_complete(sel, rv, 1);
1082 	goto out;
1083     }
1084     sel_unlock(sel);
1085  out:
1086     return;
1087 }
1088 
1089 static void
start_fetch_cb(ipmi_mc_t * mc,void * cb_data)1090 start_fetch_cb(ipmi_mc_t *mc, void *cb_data)
1091 {
1092     sel_fetch_handler_t *elem = cb_data;
1093     ipmi_sel_info_t     *sel = elem->sel;
1094     unsigned char       cmd_data[MAX_IPMI_DATA_SIZE];
1095     ipmi_msg_t          cmd_msg;
1096     int                 rv;
1097 
1098     if (sel->destroyed) {
1099 	ipmi_log(IPMI_LOG_ERR_INFO,
1100 		 "%ssel.c(start_fetch_cb): "
1101 		 "SEL info was destroyed while an operation was in progress",
1102 		 sel->name);
1103 	elem->rv = ECANCELED;
1104 	goto out;
1105     }
1106 
1107     if (sel->supports_reserve_sel) {
1108 	/* Get a reservation first. */
1109 	cmd_msg.data = cmd_data;
1110 	cmd_msg.netfn = IPMI_STORAGE_NETFN;
1111 	cmd_msg.cmd = IPMI_RESERVE_SEL_CMD;
1112 	cmd_msg.data_len = 0;
1113 	rv = ipmi_mc_send_command_sideeff(mc, sel->lun, &cmd_msg,
1114 					  sel_handle_reservation, elem);
1115     } else {
1116 	/* Bypass the reservation, it's not supported. */
1117 	sel->reservation = 0;
1118 
1119 	/* Fetch the repository info. */
1120 	cmd_msg.data = cmd_data;
1121 	cmd_msg.netfn = IPMI_STORAGE_NETFN;
1122 	cmd_msg.cmd = IPMI_GET_SEL_INFO_CMD;
1123 	cmd_msg.data_len = 0;
1124 	rv = ipmi_mc_send_command(mc, sel->lun,
1125 				  &cmd_msg, handle_sel_info, elem);
1126     }
1127 
1128     if (rv) {
1129 	ipmi_log(IPMI_LOG_ERR_INFO,
1130 		 "%ssel.c(start_fetch_cb): could not send cmd: %x",
1131 		 sel->name, rv);
1132 	elem->rv = rv;
1133 	goto out;
1134     }
1135 
1136  out:
1137     return;
1138 }
1139 
1140 static int
start_fetch(void * cb_data,int shutdown)1141 start_fetch(void *cb_data, int shutdown)
1142 {
1143     sel_fetch_handler_t *elem = cb_data;
1144     int                 rv;
1145 
1146     sel_lock(elem->sel);
1147     if (shutdown) {
1148 	ipmi_log(IPMI_LOG_ERR_INFO,
1149 		 "%ssel.c(start_fetch): "
1150 		 "SEL info was destroyed while an operation was in progress",
1151 		 elem->sel->name);
1152 	fetch_complete(elem->sel, ECANCELED, 0);
1153 	return OPQ_HANDLER_ABORTED;
1154     }
1155 
1156     /* The read lock must be claimed before the sel lock to avoid
1157        deadlock. */
1158     rv = ipmi_mc_pointer_cb(elem->sel->mc, start_fetch_cb, elem);
1159     if (rv)
1160 	ipmi_log(IPMI_LOG_ERR_INFO,
1161 		 "%ssel.c(start_fetch): MC is not valid", elem->sel->name);
1162     else
1163 	rv = elem->rv;
1164 
1165     if (rv) {
1166 	fetch_complete(elem->sel, rv, 0);
1167 	return OPQ_HANDLER_ABORTED;
1168     }
1169 
1170     sel_unlock(elem->sel);
1171 
1172     return OPQ_HANDLER_STARTED;
1173 }
1174 
1175 /* We have to have this because the allocate element can go away (the
1176    operation can complete) before returning to the user. */
1177 typedef struct sel_get_cb_s
1178 {
1179     sel_fetch_handler_t *elem;
1180     int                 rv;
1181 } sel_get_cb_t;
1182 
1183 static void
ipmi_sel_get_cb(ipmi_mc_t * mc,void * cb_data)1184 ipmi_sel_get_cb(ipmi_mc_t *mc, void *cb_data)
1185 {
1186     sel_get_cb_t        *info = cb_data;
1187     sel_fetch_handler_t *elem = info->elem;
1188     ipmi_sel_info_t     *sel = elem->sel;
1189 
1190     if (!ipmi_mc_sel_device_support(mc)) {
1191 	ipmi_log(IPMI_LOG_ERR_INFO,
1192 		 "%ssel.c(ipmi_sel_get_cb): "
1193 		 "No support for the system event log", sel->name);
1194 	info->rv = ENOSYS;
1195 	return;
1196     }
1197 
1198     sel_lock(sel);
1199     if (! sel->in_fetch) {
1200 	/* If we are not currently fetching sels, then start the
1201 	   process.  If we are already fetching sels, then the current
1202 	   fetch process will handle it. */
1203 	sel->fetch_retry_count = 0;
1204 	sel->in_fetch = 1;
1205 	sel->sels_changed = 0;
1206 
1207 	elem->next = NULL;
1208 	sel->fetch_handlers = elem;
1209 	sel_unlock(sel);
1210 	/* Always put a fetch ahead of everything else.  If there are
1211 	   deletes in progress and a clear gets done, we can complete
1212 	   all the deletes. */
1213 	if (!opq_new_op_prio(sel->opq, start_fetch, elem, 0, OPQ_ADD_HEAD,
1214 			    NULL))
1215 	{
1216 	    sel->fetch_handlers = NULL;
1217 	    info->rv = ENOMEM;
1218 	}
1219 	goto out;
1220     } else if (elem->handler) {
1221 	/* Add it to the list of waiting fetch handlers, if it has a
1222 	   handler. */
1223 	elem->next = sel->fetch_handlers;
1224 	sel->fetch_handlers = elem;
1225     } else {
1226 	/* No handler and fetch was already in progress.  Return an
1227 	   error so the caller knows what happened. */
1228 	info->rv = EEXIST;
1229     }
1230 
1231     sel_unlock(sel);
1232  out:
1233     return;
1234 }
1235 
1236 int
ipmi_sel_get(ipmi_sel_info_t * sel,ipmi_sels_fetched_t handler,void * cb_data)1237 ipmi_sel_get(ipmi_sel_info_t     *sel,
1238 	     ipmi_sels_fetched_t handler,
1239 	     void                *cb_data)
1240 {
1241     sel_get_cb_t        info;
1242     sel_fetch_handler_t *elem;
1243     int                 rv;
1244 
1245     elem = ipmi_mem_alloc(sizeof(*elem));
1246     if (!elem) {
1247 	ipmi_log(IPMI_LOG_ERR_INFO,
1248 		 "%ssel.c(ipmi_sel_get): "
1249 		 "could not allocate the sel element", sel->name);
1250 	return ENOMEM;
1251     }
1252 
1253     elem->handler = handler;
1254     elem->cb_data = cb_data;
1255     elem->sel = sel;
1256     elem->rv = 0;
1257     info.elem = elem;
1258     info.rv = 0;
1259 
1260     rv = ipmi_mc_pointer_cb(sel->mc, ipmi_sel_get_cb, &info);
1261     if (!rv)
1262 	rv = info.rv;
1263     if (rv)
1264 	ipmi_mem_free(elem);
1265     if (rv == EEXIST)
1266 	/* EEXIST means that a operation was already running, and no
1267 	   handler was given.  We want to free the element, but we
1268 	   still want to return success. */
1269 	rv = 0;
1270 
1271     return rv;
1272 }
1273 
1274 /* Don't do this forever. */
1275 #define MAX_DEL_RESERVE_RETRIES		10
1276 
1277 typedef struct sel_del_handler_data_s
1278 {
1279     ipmi_sel_info_t       *sel;
1280     ipmi_sel_op_done_cb_t handler;
1281     void                  *cb_data;
1282     unsigned int          reservation;
1283     unsigned int          record_id;
1284     unsigned int          lun;
1285     unsigned int          count;
1286     ipmi_event_t          *event;
1287     sel_event_holder_t    *holder;
1288 
1289     /* If true, we do a clear operation if the given record is the
1290        last record in the SEL. */
1291     int                   do_clear;
1292 } sel_del_handler_data_t;
1293 
1294 static int send_reserve_sel_for_delete(sel_del_handler_data_t *data,
1295 				       ipmi_mc_t *mc);
1296 
1297 static void
sel_op_done(sel_del_handler_data_t * data,int rv,int do_op_done)1298 sel_op_done(sel_del_handler_data_t *data,
1299 	    int                    rv,
1300 	    int                    do_op_done)
1301 {
1302     ipmi_sel_info_t *sel = data->sel;
1303 
1304     if (data->holder)
1305 	sel_event_holder_put(data->holder);
1306 
1307     sel_unlock(sel);
1308 
1309     if (data->handler)
1310 	data->handler(sel, data->cb_data, rv);
1311 
1312     sel_lock(sel);
1313 
1314     if (sel->in_destroy) {
1315 	/* Nothing to do */
1316 	sel_unlock(sel);
1317     } else if (sel->destroyed) {
1318 	/* This will unlock the lock. */
1319 	internal_destroy_sel(sel);
1320     } else {
1321 	sel_unlock(sel);
1322 	if (do_op_done)
1323 	    opq_op_done(sel->opq);
1324     }
1325     if (data->event)
1326 	ipmi_event_free(data->event);
1327     ipmi_mem_free(data);
1328 }
1329 
1330 static void
free_all_event(ilist_iter_t * iter,void * item,void * cb_data)1331 free_all_event(ilist_iter_t *iter, void *item, void *cb_data)
1332 {
1333     sel_event_holder_t *holder = item;
1334     ipmi_sel_info_t    *sel = cb_data;
1335 
1336     if (holder->deleted) {
1337 	sel->del_sels--;
1338 	holder->cancelled = 1;
1339     }
1340     ilist_delete(iter);
1341     sel_event_holder_put(holder);
1342 }
1343 
1344 static void
free_all_events(ipmi_sel_info_t * sel)1345 free_all_events(ipmi_sel_info_t *sel)
1346 {
1347     ilist_iter(sel->events, free_all_event, sel);
1348 }
1349 
1350 static void
handle_del_sel_clear(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)1351 handle_del_sel_clear(ipmi_mc_t  *mc,
1352 		     ipmi_msg_t *rsp,
1353 		     void       *rsp_data)
1354 {
1355     sel_del_handler_data_t *data = rsp_data;
1356     ipmi_sel_info_t        *sel = data->sel;
1357 
1358     sel_lock(sel);
1359     if (sel->destroyed) {
1360 	ipmi_log(IPMI_LOG_ERR_INFO,
1361 		 "%ssel.c(handle_del_sel_clear): "
1362 		 "SEL info was destroyed while an operation was in progress",
1363 		 sel->name);
1364 	sel_op_done(data, ECANCELED, 1);
1365 	goto out;
1366     }
1367 
1368     if (!mc) {
1369 	ipmi_log(IPMI_LOG_ERR_INFO,
1370 		 "%ssel.c(handle_del_sel_clear): "
1371 		 "MC went away while SEL fetch was in progress",
1372 		 sel->name);
1373 	sel_op_done(data, ECANCELED, 1);
1374 	goto out;
1375     }
1376 
1377     if (rsp->data[0]) {
1378 	ipmi_log(IPMI_LOG_ERR_INFO,
1379 		 "%ssel.c(handle_del_sel_clear): "
1380 		 "IPMI error clearing SEL: 0x%x",
1381 		 sel->name, rsp->data[0]);
1382 	sel_op_done(data, IPMI_IPMI_ERR_VAL(rsp->data[0]), 1);
1383 	goto out;
1384     }
1385 
1386     free_all_events(sel);
1387     sel->num_sels = 0;
1388 
1389     sel_op_done(data, 0, 1);
1390 
1391  out:
1392     return;
1393 }
1394 
1395 static int
send_del_clear(sel_del_handler_data_t * data,ipmi_mc_t * mc)1396 send_del_clear(sel_del_handler_data_t *data, ipmi_mc_t *mc)
1397 {
1398     ipmi_sel_info_t *sel = data->sel;
1399     unsigned char   cmd_data[MAX_IPMI_DATA_SIZE];
1400     ipmi_msg_t      cmd_msg;
1401 
1402     cmd_msg.data = cmd_data;
1403     cmd_msg.netfn = IPMI_STORAGE_NETFN;
1404     cmd_msg.cmd = IPMI_CLEAR_SEL_CMD;
1405     cmd_msg.data_len = 6;
1406     ipmi_set_uint16(cmd_msg.data, data->reservation);
1407     cmd_msg.data[2] = 'C';
1408     cmd_msg.data[3] = 'L';
1409     cmd_msg.data[4] = 'R';
1410     cmd_msg.data[5] = 0xaa;
1411 
1412     return ipmi_mc_send_command(mc, sel->lun, &cmd_msg,
1413 				handle_del_sel_clear, data);
1414 }
1415 
1416 static void
handle_sel_delete(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)1417 handle_sel_delete(ipmi_mc_t  *mc,
1418 		  ipmi_msg_t *rsp,
1419 		  void       *rsp_data)
1420 {
1421     sel_del_handler_data_t *data = rsp_data;
1422     ipmi_sel_info_t        *sel = data->sel;
1423     int                    rv = 0;
1424 
1425     sel_lock(sel);
1426     if (sel->destroyed) {
1427 	ipmi_log(IPMI_LOG_ERR_INFO,
1428 		 "%ssel.c(handle_sel_delete): "
1429 		 "SEL info was destroyed while an operation was in progress",
1430 		 sel->name);
1431 	sel_op_done(data, ECANCELED, 1);
1432 	goto out;
1433     }
1434 
1435     if (!mc) {
1436 	ipmi_log(IPMI_LOG_ERR_INFO,
1437 		 "%ssel.c(handle_sel_delete): "
1438 		 "MC went away while SEL fetch was in progress",
1439 		 sel->name);
1440 	sel_op_done(data, ECANCELED, 1);
1441 	goto out;
1442     }
1443 
1444     /* Special return codes. */
1445     if (rsp->data[0] == 0x80) {
1446 	ipmi_log(IPMI_LOG_ERR_INFO,
1447 		 "%ssel.c(handle_sel_delete): "
1448 		 "Operation not supported on SEL delete",
1449 		 sel->name);
1450 	rv = ENOSYS;
1451     } else if (rsp->data[0] == 0x81) {
1452 	/* The SEL is being erased, so by definition the log will be
1453            gone. */
1454 	rv = 0;
1455     } else if (rsp->data[0] == IPMI_NOT_PRESENT_CC) {
1456 	/* The entry is already gone, so just return no error. */
1457 	rv = 0;
1458     } else if ((data->count < MAX_DEL_RESERVE_RETRIES)
1459 	       && (rsp->data[0] == IPMI_INVALID_RESERVATION_CC))
1460     {
1461 	if (sel->sel_delete_lost_reservation)
1462 	    ipmi_domain_stat_add(sel->sel_delete_lost_reservation, 1);
1463 	/* Lost our reservation, retry the operation. */
1464 	data->count++;
1465 	rv = send_reserve_sel_for_delete(data, mc);
1466 	if (!rv)
1467 	    goto out_unlock;
1468     } else if (rsp->data[0]) {
1469 	if (rsp->data[0] == IPMI_INVALID_RESERVATION_CC) {
1470 	    if (sel->sel_fail_delete_lost_reservation)
1471 		ipmi_domain_stat_add(sel->sel_fail_delete_lost_reservation, 1);
1472 	} else {
1473 	    if (sel->sel_delete_errors)
1474 		ipmi_domain_stat_add(sel->sel_delete_errors, 1);
1475 	}
1476 	ipmi_log(IPMI_LOG_ERR_INFO,
1477 		 "%ssel.c(handle_sel_delete): "
1478 		 "IPMI error from SEL delete: %x", sel->name, rsp->data[0]);
1479 	rv = IPMI_IPMI_ERR_VAL(rsp->data[0]);
1480     } else {
1481 	/* We deleted the entry, so remove it from our database. */
1482 	sel_event_holder_t *real_holder;
1483 	ilist_iter_t       iter;
1484 
1485 	ilist_init_iter(&iter, sel->events);
1486 	ilist_unpositioned(&iter);
1487 	real_holder = ilist_search_iter(&iter, recid_search_cmp,
1488 					&(data->record_id));
1489 	if (real_holder) {
1490 	    ilist_delete(&iter);
1491 	    sel_event_holder_put(real_holder);
1492 	    sel->del_sels--;
1493 	}
1494     }
1495 
1496     sel_op_done(data, rv, 1);
1497 
1498  out:
1499     return;
1500 
1501  out_unlock:
1502     sel_unlock(sel);
1503 }
1504 
1505 static int
send_del_sel(sel_del_handler_data_t * data,ipmi_mc_t * mc)1506 send_del_sel(sel_del_handler_data_t *data, ipmi_mc_t *mc)
1507 {
1508     unsigned char   cmd_data[MAX_IPMI_DATA_SIZE];
1509     ipmi_msg_t      cmd_msg;
1510     int             rv;
1511 
1512     cmd_msg.data = cmd_data;
1513     cmd_msg.netfn = IPMI_STORAGE_NETFN;
1514     cmd_msg.cmd = IPMI_DELETE_SEL_ENTRY_CMD;
1515     cmd_msg.data_len = 4;
1516     ipmi_set_uint16(cmd_msg.data, data->reservation);
1517     ipmi_set_uint16(cmd_msg.data+2, data->record_id);
1518     rv = ipmi_mc_send_command(mc, data->lun,
1519 			      &cmd_msg, handle_sel_delete, data);
1520 
1521     return rv;
1522 }
1523 
1524 static void
handle_sel_check(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)1525 handle_sel_check(ipmi_mc_t  *mc,
1526 		 ipmi_msg_t *rsp,
1527 		 void       *rsp_data)
1528 {
1529     sel_del_handler_data_t *data = rsp_data;
1530     ipmi_sel_info_t        *sel = data->sel;
1531     int                    rv = 0;
1532 
1533     sel_lock(sel);
1534     if (sel->destroyed) {
1535 	ipmi_log(IPMI_LOG_ERR_INFO,
1536 		 "%ssel.c(handle_sel_check): "
1537 		 "SEL info was destroyed while SEL delete element was in"
1538 		 " progress",
1539 		 sel->name);
1540 	sel_op_done(data, ECANCELED, 1);
1541 	goto out;
1542     }
1543 
1544     if (!mc) {
1545 	ipmi_log(IPMI_LOG_ERR_INFO,
1546 		 "%ssel.c(handle_sel_check): "
1547 		 "MC went away while SEL delete element was in progress",
1548 		 sel->name);
1549 	sel_op_done(data, ECANCELED, 1);
1550 	goto out;
1551     }
1552 
1553     /* Special return codes. */
1554     if (rsp->data[0] == IPMI_NOT_PRESENT_CC) {
1555 	/* The entry is already gone, so just return no error. */
1556 	sel_op_done(data, 0, 1);
1557 	goto out;
1558     } else if (rsp->data[0]) {
1559 	if (sel->sel_delete_errors)
1560 	    ipmi_domain_stat_add(sel->sel_delete_errors, 1);
1561 	ipmi_log(IPMI_LOG_ERR_INFO,
1562 		 "%ssel.c(handle_sel_check): IPMI error from SEL check: %x",
1563 		 sel->name, rsp->data[0]);
1564 	sel_op_done(data, IPMI_IPMI_ERR_VAL(rsp->data[0]), 1);
1565 	goto out;
1566     } else {
1567 	ipmi_event_t *ch_event;
1568 	ipmi_time_t  timestamp;
1569 
1570 	if (rsp->data[5] < 0xe0)
1571 	    timestamp = ipmi_get_uint32(rsp->data+6);
1572 	else
1573 	    timestamp = -1;
1574 	ch_event = ipmi_event_alloc(ipmi_mc_convert_to_id(mc),
1575 				    ipmi_get_uint16(rsp->data+3),
1576 				    rsp->data[5],
1577 				    timestamp,
1578 				    rsp->data+6,
1579 				    13);
1580 	if (!ch_event) {
1581 	    ipmi_log(IPMI_LOG_ERR_INFO,
1582 		     "%ssel.c(handle_sel_check): Could not allocate memory",
1583 		     sel->name);
1584 	    sel_op_done(data, ENOMEM, 1);
1585 	    goto out;
1586 	}
1587 
1588 	if (data->event && (event_cmp(ch_event, data->event) != 0)) {
1589 	    /* The event's don't match, so just finish. */
1590 	    ipmi_event_free(ch_event);
1591 	    sel_op_done(data, 0, 1);
1592 	    goto out;
1593 	}
1594 	ipmi_event_free(ch_event);
1595 
1596 	if (data->do_clear) {
1597 	    /* Make sure that there is no next event. */
1598 	    uint16_t next_ev = ipmi_get_uint16(rsp->data+1);
1599 
1600 	    if (next_ev != 0xffff) {
1601 		/* A new event was added after this one.  Fail the op. */
1602 		ipmi_log(IPMI_LOG_ERR_INFO,
1603 			 "%ssel.c(handle_sel_check): "
1604 			 "Clear SEL failed, new events in SEL",
1605 			 sel->name);
1606 		sel_op_done(data, EAGAIN, 1);
1607 		goto out;
1608 	    }
1609 
1610 	    rv = send_del_clear(data, mc);
1611 	    if (rv) {
1612 		ipmi_log(IPMI_LOG_ERR_INFO,
1613 			 "%ssel.c(handle_sel_check): "
1614 			 "Could not send SEL clear command: %x",
1615 			 sel->name, rv);
1616 		sel_op_done(data, rv, 1);
1617 		goto out;
1618 	    }
1619 	} else {
1620 	    rv = send_del_sel(data, mc);
1621 	    if (rv) {
1622 		ipmi_log(IPMI_LOG_ERR_INFO,
1623 			 "%ssel.c(handle_sel_check): "
1624 			 "Could not send SEL delete command: %x",
1625 			 sel->name, rv);
1626 		sel_op_done(data, rv, 1);
1627 		goto out;
1628 	    } else if (data->record_id == sel->start_rec_id)
1629 		/* We are deleting our "current" record (used for finding
1630 		   the next record), make sure we start again from
1631 		   scratch on the next fetch. */
1632 		sel->start_rec_id = 0;
1633 	}
1634     }
1635 
1636 
1637     sel_unlock(sel);
1638 
1639  out:
1640     return;
1641 }
1642 
1643 /* First get the entry, to make sure we are deleting the right one. */
1644 static int
send_check_sel(sel_del_handler_data_t * data,ipmi_mc_t * mc)1645 send_check_sel(sel_del_handler_data_t *data, ipmi_mc_t *mc)
1646 {
1647     unsigned char   cmd_data[MAX_IPMI_DATA_SIZE];
1648     ipmi_msg_t      cmd_msg;
1649     int             rv;
1650 
1651     cmd_msg.data = cmd_data;
1652     cmd_msg.netfn = IPMI_STORAGE_NETFN;
1653     cmd_msg.cmd = IPMI_GET_SEL_ENTRY_CMD;
1654     cmd_msg.data_len = 6;
1655     ipmi_set_uint16(cmd_msg.data, 0);
1656     ipmi_set_uint16(cmd_msg.data+2, data->record_id);
1657     cmd_msg.data[4] = 0;
1658     cmd_msg.data[5] = 0xff;
1659     rv = ipmi_mc_send_command(mc, data->lun,
1660 			      &cmd_msg, handle_sel_check, data);
1661 
1662     return rv;
1663 }
1664 
1665 static void
sel_reserved_for_delete(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)1666 sel_reserved_for_delete(ipmi_mc_t  *mc,
1667 			ipmi_msg_t *rsp,
1668 			void       *rsp_data)
1669 {
1670     sel_del_handler_data_t *data = rsp_data;
1671     ipmi_sel_info_t        *sel = data->sel;
1672     int                    rv;
1673 
1674     sel_lock(sel);
1675     if (sel->destroyed) {
1676 	ipmi_log(IPMI_LOG_ERR_INFO,
1677 		 "%ssel.c(sel_reserved_for_delete): "
1678 		 "SEL info was destroyed while SEL delete element was in"
1679 		 " progress",
1680 		 sel->name);
1681 	sel_op_done(data, ECANCELED, 1);
1682 	goto out;
1683     }
1684     if (!mc) {
1685 	ipmi_log(IPMI_LOG_ERR_INFO,
1686 		 "%ssel.c(sel_reserved_for_delete): "
1687 		 "MC went away while SEL delete element was in progress",
1688 		 sel->name);
1689 	sel_op_done(data, ECANCELED, 1);
1690 	goto out;
1691     }
1692 
1693     if (rsp->data[0] != 0) {
1694 	if (sel->sel_delete_errors)
1695 	    ipmi_domain_stat_add(sel->sel_delete_errors, 1);
1696 	ipmi_log(IPMI_LOG_ERR_INFO,
1697 		 "%ssel.c(sel_reserved_for_delete): "
1698 		 "IPMI error from SEL delete reservation: %x",
1699 		 sel->name, rsp->data[0]);
1700 	sel_op_done(data, IPMI_IPMI_ERR_VAL(rsp->data[0]), 1);
1701 	goto out;
1702     }
1703 
1704     data->reservation = ipmi_get_uint16(rsp->data+1);
1705     if (!data->do_clear || data->event) {
1706 	rv = send_check_sel(data, mc);
1707 	if (rv) {
1708 	    ipmi_log(IPMI_LOG_ERR_INFO,
1709 		     "%ssel.c(sel_reserved_for_delete): "
1710 		     "Could not send SEL get command: %x", sel->name, rv);
1711 	    sel_op_done(data, rv, 1);
1712 	    goto out;
1713 	}
1714     } else {
1715 	/* We are clearing the SEL and the user didn't supply an
1716 	   event.  Don't worry about checking anything. */
1717 	rv = send_del_clear(data, mc);
1718 	if (rv) {
1719 	    ipmi_log(IPMI_LOG_ERR_INFO,
1720 		     "%ssel.c(sel_reserved_for_delete): "
1721 		     "Could not send SEL clear command: %x", sel->name, rv);
1722 	    sel_op_done(data, rv, 1);
1723 	    goto out;
1724 	}
1725     }
1726 
1727     sel_unlock(sel);
1728  out:
1729     return;
1730 }
1731 
1732 static int
send_reserve_sel_for_delete(sel_del_handler_data_t * data,ipmi_mc_t * mc)1733 send_reserve_sel_for_delete(sel_del_handler_data_t *data, ipmi_mc_t *mc)
1734 {
1735     unsigned char   cmd_data[MAX_IPMI_DATA_SIZE];
1736     ipmi_msg_t      cmd_msg;
1737     int             rv;
1738 
1739     cmd_msg.data = cmd_data;
1740     cmd_msg.netfn = IPMI_STORAGE_NETFN;
1741     cmd_msg.cmd = IPMI_RESERVE_SEL_CMD;
1742     cmd_msg.data_len = 0;
1743     rv = ipmi_mc_send_command_sideeff(mc, data->lun,
1744 				      &cmd_msg, sel_reserved_for_delete, data);
1745 
1746     return rv;
1747 }
1748 
1749 static void
start_del_sel_cb(ipmi_mc_t * mc,void * cb_data)1750 start_del_sel_cb(ipmi_mc_t *mc, void *cb_data)
1751 {
1752     sel_del_handler_data_t *data = cb_data;
1753     ipmi_sel_info_t        *sel = data->sel;
1754     int                    rv;
1755 
1756     /* Called with SEL lock held. */
1757     if (sel->destroyed) {
1758 	ipmi_log(IPMI_LOG_ERR_INFO,
1759 		 "%ssel.c(start_del_sel_cb): "
1760 		 "SEL info was destroyed while an operation was in progress",
1761 		 sel->name);
1762 	sel_op_done(data, ECANCELED, 1);
1763 	goto out;
1764     }
1765 
1766     if (data->sel->supports_reserve_sel)
1767 	rv = send_reserve_sel_for_delete(data, mc);
1768     else
1769 	rv = send_check_sel(data, mc);
1770 
1771     if (rv) {
1772 	ipmi_log(IPMI_LOG_ERR_INFO,
1773 		 "%ssel.c(start_del_sel_cb): could not send cmd: %x",
1774 		 sel->name, rv);
1775 	sel_op_done(data, rv, 1);
1776 	goto out;
1777     }
1778 
1779     sel_unlock(sel);
1780  out:
1781     return;
1782 }
1783 
1784 static int
start_del_sel(void * cb_data,int shutdown)1785 start_del_sel(void *cb_data, int shutdown)
1786 {
1787     sel_del_handler_data_t *data = cb_data;
1788     ipmi_sel_info_t        *sel = data->sel;
1789     int                    rv;
1790 
1791     sel_lock(sel);
1792     if (shutdown) {
1793 	ipmi_log(IPMI_LOG_ERR_INFO,
1794 		 "%ssel.c(start_del_sel): "
1795 		 "SEL info was destroyed while an operation was in progress",
1796 		 sel->name);
1797 	sel_op_done(data, ECANCELED, 0);
1798 	return OPQ_HANDLER_ABORTED;
1799     }
1800 
1801     if (data->holder && data->holder->cancelled) {
1802 	/* Deleted by a clear, everything is ok. */
1803 	sel_op_done(data, 0, 0);
1804 	return OPQ_HANDLER_ABORTED;
1805     }
1806 
1807     rv = ipmi_mc_pointer_cb(sel->mc, start_del_sel_cb, data);
1808     if (rv) {
1809 	ipmi_log(IPMI_LOG_ERR_INFO,
1810 		 "%ssel.c(start_del_sel_cb): MC went away during delete",
1811 		 sel->name);
1812 	sel_op_done(data, ECANCELED, 0);
1813 	return OPQ_HANDLER_ABORTED;
1814     }
1815 
1816     return OPQ_HANDLER_STARTED;
1817 }
1818 
1819 typedef struct sel_del_event_info_s
1820 {
1821     ipmi_sel_info_t       *sel;
1822     ipmi_event_t          *event;
1823     unsigned int          record_id;
1824     ipmi_sel_op_done_cb_t handler;
1825     void                  *cb_data;
1826     int                   cmp_event;
1827     int                   rv;
1828     int                   do_clear;
1829 } sel_del_event_info_t;
1830 
1831 static void
sel_del_event_cb(ipmi_mc_t * mc,void * cb_data)1832 sel_del_event_cb(ipmi_mc_t *mc, void *cb_data)
1833 {
1834     sel_del_event_info_t  *info = cb_data;
1835     ipmi_sel_info_t       *sel = info->sel;
1836     ipmi_event_t          *event = info->event;
1837     int                   cmp_event = info->cmp_event;
1838     sel_event_holder_t    *real_holder = NULL;
1839     ilist_iter_t          iter;
1840     int                   start_fetch = 0;
1841 
1842     sel_lock(sel);
1843     if (sel->destroyed) {
1844 	info->rv = EINVAL;
1845 	goto out_unlock;
1846     }
1847 
1848     if (event) {
1849 	ilist_init_iter(&iter, sel->events);
1850 	ilist_unpositioned(&iter);
1851 	real_holder = ilist_search_iter(&iter, recid_search_cmp,
1852 				    &info->record_id);
1853 	if (!real_holder) {
1854 	    info->rv = EINVAL;
1855 	    goto out_unlock;
1856 	}
1857 
1858 	if (cmp_event && (event_cmp(event, real_holder->event) != 0)) {
1859 	    info->rv = EINVAL;
1860 	    goto out_unlock;
1861 	}
1862 
1863 	if (! info->do_clear) {
1864 	    if (real_holder->deleted) {
1865 		info->rv = EINVAL;
1866 		goto out_unlock;
1867 	    }
1868 
1869 	    real_holder->deleted = 1;
1870 	    sel->num_sels--;
1871 	    sel->del_sels++;
1872 
1873 	    start_fetch = (sel->num_sels == 0) && (sel->del_sels > 1);
1874 	}
1875     }
1876 
1877     /* Note that at this point we cannot check num_sels to see if it
1878        is zero and do a bulk clear.  A new event might be added to the
1879        SEL after this point, thus failing the bulk clear, and that
1880        would prevent the individual delete from happening.  But we can
1881        start a fetch if the value reaches zero, which is just as
1882        good. */
1883 
1884     if (sel->supports_delete_sel || info->do_clear) {
1885 	sel_del_handler_data_t *data;
1886 	opq_elem_t             *elem;
1887 
1888 	/* We can delete the entry immediately, just do it. */
1889 	data = ipmi_mem_alloc(sizeof(*data));
1890 	elem = opq_alloc_elem();
1891 	if (!data || !elem) {
1892 	    if (! info->do_clear) {
1893 		real_holder->deleted = 0;
1894 		sel->num_sels++;
1895 		sel->del_sels--;
1896 	    }
1897 	    info->rv = ENOMEM;
1898 	    if (data)
1899 		ipmi_mem_free(data);
1900 	    if (elem)
1901 		opq_free_elem(elem);
1902 	    goto out_unlock;
1903 	}
1904 
1905 	data->sel = sel;
1906 	data->handler = info->handler;
1907 	data->cb_data = info->cb_data;
1908 	data->lun = sel->lun;
1909 	data->record_id = info->record_id;
1910 	data->count = 0;
1911 	data->event = event;
1912 	data->holder = real_holder;
1913 	if (real_holder)
1914 	    sel_event_holder_get(real_holder);
1915 	data->do_clear = info->do_clear;
1916 	event = NULL;
1917 
1918 	sel_unlock(sel);
1919 	opq_new_op_prio(sel->opq, start_del_sel, data, 0, OPQ_ADD_TAIL, elem);
1920     } else {
1921 	sel_unlock(sel);
1922 	/* Don't really delete the event, but report is as done. */
1923 	info->handler(sel, info->cb_data, 0);
1924 	ipmi_event_free(event);
1925     }
1926 
1927     if (start_fetch)
1928 	ipmi_sel_get(sel, NULL, NULL);
1929     return;
1930 
1931  out_unlock:
1932     sel_unlock(sel);
1933 }
1934 
1935 static int
sel_del_event(ipmi_sel_info_t * sel,ipmi_event_t * event,unsigned int record_id,ipmi_sel_op_done_cb_t handler,void * cb_data,int cmp_event,int do_clear)1936 sel_del_event(ipmi_sel_info_t       *sel,
1937 	      ipmi_event_t          *event,
1938 	      unsigned int          record_id,
1939 	      ipmi_sel_op_done_cb_t handler,
1940 	      void                  *cb_data,
1941 	      int                   cmp_event,
1942 	      int                   do_clear)
1943 {
1944     sel_del_event_info_t info;
1945     int                  rv;
1946 
1947     info.sel = sel;
1948     info.event = ipmi_event_dup(event);
1949     info.record_id = record_id;
1950     info.handler = handler;
1951     info.cb_data = cb_data;
1952     info.cmp_event = cmp_event;
1953     info.rv = 0;
1954     info.do_clear = do_clear;
1955     rv = ipmi_mc_pointer_cb(sel->mc, sel_del_event_cb, &info);
1956     if (!rv)
1957 	rv = info.rv;
1958     if (rv)
1959 	ipmi_event_free(info.event);
1960     return rv;
1961 }
1962 
1963 int
ipmi_sel_del_event(ipmi_sel_info_t * sel,ipmi_event_t * event,ipmi_sel_op_done_cb_t handler,void * cb_data)1964 ipmi_sel_del_event(ipmi_sel_info_t       *sel,
1965 		   ipmi_event_t          *event,
1966 		   ipmi_sel_op_done_cb_t handler,
1967 		   void                  *cb_data)
1968 {
1969     return sel_del_event(sel, event, ipmi_event_get_record_id(event),
1970 			 handler, cb_data, 1, 0);
1971 }
1972 
1973 int
ipmi_sel_del_event_by_recid(ipmi_sel_info_t * sel,unsigned int record_id,ipmi_sel_op_done_cb_t handler,void * cb_data)1974 ipmi_sel_del_event_by_recid(ipmi_sel_info_t       *sel,
1975 			    unsigned int          record_id,
1976 			    ipmi_sel_op_done_cb_t handler,
1977 			    void                  *cb_data)
1978 {
1979     return sel_del_event(sel, NULL, record_id, handler, cb_data, 0, 0);
1980 }
1981 
1982 int
ipmi_sel_clear(ipmi_sel_info_t * sel,ipmi_event_t * last_event,ipmi_sel_op_done_cb_t handler,void * cb_data)1983 ipmi_sel_clear(ipmi_sel_info_t       *sel,
1984 	       ipmi_event_t          *last_event,
1985 	       ipmi_sel_op_done_cb_t handler,
1986 	       void                  *cb_data)
1987 {
1988     int cmp_event = (last_event != NULL);
1989     unsigned int record_id = 0;
1990     if (last_event)
1991 	record_id = ipmi_event_get_record_id(last_event);
1992     return sel_del_event(sel, last_event, record_id, handler, cb_data,
1993 			 cmp_event, 1);
1994 }
1995 
1996 int
ipmi_get_sel_count(ipmi_sel_info_t * sel,unsigned int * count)1997 ipmi_get_sel_count(ipmi_sel_info_t *sel,
1998 		   unsigned int    *count)
1999 {
2000     sel_lock(sel);
2001     if (sel->destroyed) {
2002 	sel_unlock(sel);
2003 	return EINVAL;
2004     }
2005 
2006     *count = sel->num_sels;
2007 
2008     sel_unlock(sel);
2009     return 0;
2010 }
2011 
2012 int
ipmi_get_sel_entries_used(ipmi_sel_info_t * sel,unsigned int * count)2013 ipmi_get_sel_entries_used(ipmi_sel_info_t *sel,
2014 			  unsigned int    *count)
2015 {
2016     sel_lock(sel);
2017     if (sel->destroyed) {
2018 	sel_unlock(sel);
2019 	return EINVAL;
2020     }
2021 
2022     *count = sel->num_sels + sel->del_sels;
2023 
2024     sel_unlock(sel);
2025     return 0;
2026 }
2027 
2028 ipmi_event_t *
ipmi_sel_get_first_event(ipmi_sel_info_t * sel)2029 ipmi_sel_get_first_event(ipmi_sel_info_t *sel)
2030 {
2031     ilist_iter_t iter;
2032     ipmi_event_t *rv = NULL;
2033 
2034     sel_lock(sel);
2035     if (sel->destroyed) {
2036 	sel_unlock(sel);
2037 	return NULL;
2038     }
2039     ilist_init_iter(&iter, sel->events);
2040     if (ilist_first(&iter)) {
2041 	sel_event_holder_t *holder = ilist_get(&iter);
2042 
2043 	while (holder->deleted) {
2044 	    if (! ilist_next(&iter))
2045 		goto out;
2046 	    holder = ilist_get(&iter);
2047 	}
2048 	rv = ipmi_event_dup(holder->event);
2049     }
2050  out:
2051     sel_unlock(sel);
2052     return rv;
2053 }
2054 
2055 ipmi_event_t *
ipmi_sel_get_last_event(ipmi_sel_info_t * sel)2056 ipmi_sel_get_last_event(ipmi_sel_info_t *sel)
2057 {
2058     ilist_iter_t iter;
2059     ipmi_event_t *rv = NULL;
2060 
2061     sel_lock(sel);
2062     if (sel->destroyed) {
2063 	sel_unlock(sel);
2064 	return NULL;
2065     }
2066     ilist_init_iter(&iter, sel->events);
2067     if (ilist_last(&iter)) {
2068 	sel_event_holder_t *holder = ilist_get(&iter);
2069 
2070 	while (holder->deleted) {
2071 	    if (! ilist_prev(&iter))
2072 		goto out;
2073 	    holder = ilist_get(&iter);
2074 	}
2075 	rv = ipmi_event_dup(holder->event);
2076     }
2077  out:
2078     sel_unlock(sel);
2079     return rv;
2080 }
2081 
2082 ipmi_event_t *
ipmi_sel_get_next_event(ipmi_sel_info_t * sel,const ipmi_event_t * event)2083 ipmi_sel_get_next_event(ipmi_sel_info_t *sel, const ipmi_event_t *event)
2084 {
2085     ilist_iter_t iter;
2086     ipmi_event_t *rv = NULL;
2087     unsigned int record_id;
2088 
2089     sel_lock(sel);
2090     if (sel->destroyed) {
2091 	sel_unlock(sel);
2092 	return NULL;
2093     }
2094     ilist_init_iter(&iter, sel->events);
2095     ilist_unpositioned(&iter);
2096     record_id = ipmi_event_get_record_id(event);
2097     if (ilist_search_iter(&iter, recid_search_cmp, &record_id)) {
2098 	if (ilist_next(&iter)) {
2099 	    sel_event_holder_t *holder = ilist_get(&iter);
2100 
2101 	    while (holder->deleted) {
2102 		if (! ilist_next(&iter))
2103 		    goto out;
2104 		holder = ilist_get(&iter);
2105 	    }
2106 	    rv = ipmi_event_dup(holder->event);
2107 	}
2108     }
2109  out:
2110     sel_unlock(sel);
2111     return rv;
2112 }
2113 
2114 ipmi_event_t *
ipmi_sel_get_prev_event(ipmi_sel_info_t * sel,const ipmi_event_t * event)2115 ipmi_sel_get_prev_event(ipmi_sel_info_t *sel, const ipmi_event_t *event)
2116 {
2117     ilist_iter_t iter;
2118     ipmi_event_t *rv = NULL;
2119     unsigned int record_id;
2120 
2121     sel_lock(sel);
2122     if (sel->destroyed) {
2123 	sel_unlock(sel);
2124 	return NULL;
2125     }
2126     ilist_init_iter(&iter, sel->events);
2127     ilist_unpositioned(&iter);
2128     record_id = ipmi_event_get_record_id(event);
2129     if (ilist_search_iter(&iter, recid_search_cmp, &record_id)) {
2130 	if (ilist_prev(&iter)) {
2131 	    sel_event_holder_t *holder = ilist_get(&iter);
2132 
2133 	    while (holder->deleted) {
2134 		if (! ilist_prev(&iter))
2135 		    goto out;
2136 		holder = ilist_get(&iter);
2137 	    }
2138 	    rv = ipmi_event_dup(holder->event);
2139 	}
2140     }
2141  out:
2142     sel_unlock(sel);
2143     return rv;
2144 }
2145 
2146 ipmi_event_t *
ipmi_sel_get_event_by_recid(ipmi_sel_info_t * sel,unsigned int record_id)2147 ipmi_sel_get_event_by_recid(ipmi_sel_info_t *sel,
2148                             unsigned int    record_id)
2149 {
2150     ipmi_event_t       *rv = NULL;
2151     sel_event_holder_t *holder;
2152 
2153     sel_lock(sel);
2154     if (sel->destroyed) {
2155 	sel_unlock(sel);
2156 	return NULL;
2157     }
2158 
2159     holder = find_event(sel->events, record_id);
2160     if (!holder)
2161 	goto out_unlock;
2162 
2163     if (holder->deleted)
2164         goto out_unlock;
2165 
2166     rv = ipmi_event_dup(holder->event);
2167 
2168  out_unlock:
2169     sel_unlock(sel);
2170     return rv;
2171 }
2172 
2173 int
ipmi_get_all_sels(ipmi_sel_info_t * sel,int * array_size,ipmi_event_t ** array)2174 ipmi_get_all_sels(ipmi_sel_info_t *sel,
2175 		  int             *array_size,
2176 		  ipmi_event_t    **array)
2177 {
2178     unsigned int i;
2179     int          rv = 0;
2180 
2181     sel_lock(sel);
2182     if (sel->destroyed) {
2183 	sel_unlock(sel);
2184 	return EINVAL;
2185     }
2186 
2187     if (*array_size < (int) sel->num_sels) {
2188 	rv = E2BIG;
2189     } else if (sel->num_sels == 0) {
2190 	rv = 0;
2191     } else {
2192 	ilist_iter_t iter;
2193 
2194 	ilist_init_iter(&iter, sel->events);
2195 	if (! ilist_first(&iter)) {
2196 	    rv = EINVAL;
2197 	    goto out_unlock;
2198 	}
2199 	for (i = 0; ; ) {
2200 	    sel_event_holder_t *holder = ilist_get(&iter);
2201 
2202 	    if (!holder->deleted)
2203 		array[i++] = ipmi_event_dup(holder->event);
2204 	    if (i < sel->num_sels) {
2205 		if (! ilist_next(&iter)) {
2206 		    rv = EINVAL;
2207 		    while (i > 0)
2208 			ipmi_event_free(array[--i]);
2209 		    goto out_unlock;
2210 		}
2211 	    } else
2212 		break;
2213 	}
2214 	*array_size = i;
2215     }
2216 
2217  out_unlock:
2218     sel_unlock(sel);
2219     return rv;
2220 }
2221 
2222 int
ipmi_sel_get_major_version(ipmi_sel_info_t * sel,int * val)2223 ipmi_sel_get_major_version(ipmi_sel_info_t *sel, int *val)
2224 {
2225     sel_lock(sel);
2226     if (sel->destroyed) {
2227 	sel_unlock(sel);
2228 	return EINVAL;
2229     }
2230 
2231     *val = sel->major_version;
2232 
2233     sel_unlock(sel);
2234     return 0;
2235 }
2236 
2237 int
ipmi_sel_get_minor_version(ipmi_sel_info_t * sel,int * val)2238 ipmi_sel_get_minor_version(ipmi_sel_info_t *sel, int *val)
2239 {
2240     sel_lock(sel);
2241     if (sel->destroyed) {
2242 	sel_unlock(sel);
2243 	return EINVAL;
2244     }
2245 
2246     *val = sel->minor_version;
2247 
2248     sel_unlock(sel);
2249     return 0;
2250 }
2251 
2252 int
ipmi_sel_get_num_entries(ipmi_sel_info_t * sel,int * val)2253 ipmi_sel_get_num_entries(ipmi_sel_info_t *sel, int *val)
2254 {
2255     sel_lock(sel);
2256     if (sel->destroyed) {
2257 	sel_unlock(sel);
2258 	return EINVAL;
2259     }
2260 
2261     *val = sel->entries;
2262 
2263     sel_unlock(sel);
2264     return 0;
2265 }
2266 
2267 int
ipmi_sel_get_free_bytes(ipmi_sel_info_t * sel,int * val)2268 ipmi_sel_get_free_bytes(ipmi_sel_info_t *sel, int *val)
2269 {
2270     sel_lock(sel);
2271     if (sel->destroyed) {
2272 	sel_unlock(sel);
2273 	return EINVAL;
2274     }
2275 
2276     *val = sel->free_bytes;
2277 
2278     sel_unlock(sel);
2279     return 0;
2280 }
2281 
2282 int
ipmi_sel_get_overflow(ipmi_sel_info_t * sel,int * val)2283 ipmi_sel_get_overflow(ipmi_sel_info_t *sel, int *val)
2284 {
2285     sel_lock(sel);
2286     if (sel->destroyed) {
2287 	sel_unlock(sel);
2288 	return EINVAL;
2289     }
2290 
2291     *val = sel->overflow;
2292 
2293     sel_unlock(sel);
2294     return 0;
2295 }
2296 
2297 int
ipmi_sel_get_supports_delete_sel(ipmi_sel_info_t * sel,int * val)2298 ipmi_sel_get_supports_delete_sel(ipmi_sel_info_t *sel, int *val)
2299 {
2300     sel_lock(sel);
2301     if (sel->destroyed) {
2302 	sel_unlock(sel);
2303 	return EINVAL;
2304     }
2305 
2306     *val = sel->supports_delete_sel;
2307 
2308     sel_unlock(sel);
2309     return 0;
2310 }
2311 
2312 int
ipmi_sel_get_supports_partial_add_sel(ipmi_sel_info_t * sel,int * val)2313 ipmi_sel_get_supports_partial_add_sel(ipmi_sel_info_t *sel, int *val)
2314 {
2315     sel_lock(sel);
2316     if (sel->destroyed) {
2317 	sel_unlock(sel);
2318 	return EINVAL;
2319     }
2320 
2321     *val = sel->supports_partial_add_sel;
2322 
2323     sel_unlock(sel);
2324     return 0;
2325 }
2326 
2327 int
ipmi_sel_get_supports_reserve_sel(ipmi_sel_info_t * sel,int * val)2328 ipmi_sel_get_supports_reserve_sel(ipmi_sel_info_t *sel, int *val)
2329 {
2330     sel_lock(sel);
2331     if (sel->destroyed) {
2332 	sel_unlock(sel);
2333 	return EINVAL;
2334     }
2335 
2336     *val = sel->supports_reserve_sel;
2337 
2338     sel_unlock(sel);
2339     return 0;
2340 }
2341 
2342 int
ipmi_sel_get_supports_get_sel_allocation(ipmi_sel_info_t * sel,int * val)2343 ipmi_sel_get_supports_get_sel_allocation(ipmi_sel_info_t *sel,
2344 					 int             *val)
2345 {
2346     sel_lock(sel);
2347     if (sel->destroyed) {
2348 	sel_unlock(sel);
2349 	return EINVAL;
2350     }
2351 
2352     *val = sel->supports_get_sel_allocation;
2353 
2354     sel_unlock(sel);
2355     return 0;
2356 }
2357 
2358 int
ipmi_sel_get_last_addition_timestamp(ipmi_sel_info_t * sel,int * val)2359 ipmi_sel_get_last_addition_timestamp(ipmi_sel_info_t *sel,
2360                                      int             *val)
2361 {
2362     sel_lock(sel);
2363     if (sel->destroyed) {
2364 	sel_unlock(sel);
2365 	return EINVAL;
2366     }
2367 
2368     *val = sel->last_addition_timestamp;
2369 
2370     sel_unlock(sel);
2371     return 0;
2372 }
2373 
2374 int
ipmi_sel_set_new_event_handler(ipmi_sel_info_t * sel,ipmi_sel_new_event_handler_cb handler,void * cb_data)2375 ipmi_sel_set_new_event_handler(ipmi_sel_info_t               *sel,
2376 			       ipmi_sel_new_event_handler_cb handler,
2377 			       void                          *cb_data)
2378 {
2379     sel_lock(sel);
2380     sel->new_event_handler = handler;
2381     sel->new_event_cb_data = cb_data;
2382     sel_unlock(sel);
2383     return 0;
2384 }
2385 
2386 int
ipmi_sel_event_add(ipmi_sel_info_t * sel,ipmi_event_t * new_event)2387 ipmi_sel_event_add(ipmi_sel_info_t *sel,
2388 		   ipmi_event_t    *new_event)
2389 {
2390     int                rv = 0;
2391     sel_event_holder_t *holder;
2392     unsigned int       record_id;
2393 
2394     sel_lock(sel);
2395     if (sel->destroyed) {
2396 	sel_unlock(sel);
2397 	return EINVAL;
2398     }
2399 
2400     record_id = ipmi_event_get_record_id(new_event);
2401     holder = find_event(sel->events, record_id);
2402     if (!holder) {
2403 	holder = sel_event_holder_alloc();
2404 	if (!holder) {
2405 	    rv = ENOMEM;
2406 	    goto out_unlock;
2407 	}
2408 	if (!ilist_add_tail(sel->events, holder, NULL)) {
2409 	    rv = ENOMEM;
2410 	    goto out_unlock;
2411 	}
2412 	holder->event = ipmi_event_dup(new_event);
2413 	sel->num_sels++;
2414     } else if (event_cmp(holder->event, new_event) == 0) {
2415 	/* A duplicate event, just ignore it and return the right
2416 	   error. */
2417 	rv = EEXIST;
2418     } else {
2419 	ipmi_event_free(holder->event);
2420 	holder->event = ipmi_event_dup(new_event);
2421 	if (holder->deleted) {
2422 	    holder->deleted = 0;
2423 	    sel->num_sels++;
2424 	    sel->del_sels--;
2425 	}
2426     }
2427 
2428  out_unlock:
2429     sel_unlock(sel);
2430     return rv;
2431 }
2432 
2433 typedef struct sel_add_cb_handler_data_s
2434 {
2435     ipmi_sel_info_t           *sel;
2436     ipmi_sel_add_op_done_cb_t handler;
2437     void                      *cb_data;
2438     unsigned int              record_id;
2439     ipmi_event_t              *event;
2440     int                       rv;
2441 } sel_add_cb_handler_data_t;
2442 
2443 static void
sel_add_op_done(sel_add_cb_handler_data_t * data,int rv)2444 sel_add_op_done(sel_add_cb_handler_data_t *data,
2445 		int                       rv)
2446 {
2447     ipmi_sel_info_t *sel = data->sel;
2448 
2449     if (data->handler)
2450 	data->handler(sel, data->cb_data, rv, data->record_id);
2451 
2452     if (sel->in_destroy) {
2453 	/* Nothing to do */
2454 	sel_unlock(sel);
2455     } else if (sel->destroyed) {
2456 	/* This will unlock the lock. */
2457 	internal_destroy_sel(sel);
2458     } else {
2459 	sel_unlock(sel);
2460 	opq_op_done(sel->opq);
2461     }
2462     if (data->event)
2463 	ipmi_event_free(data->event);
2464     ipmi_mem_free(data);
2465 }
2466 
2467 static void
sel_add_event_done(ipmi_mc_t * mc,ipmi_msg_t * rsp,void * rsp_data)2468 sel_add_event_done(ipmi_mc_t  *mc,
2469 		   ipmi_msg_t *rsp,
2470 		   void       *rsp_data)
2471 {
2472     sel_add_cb_handler_data_t *info = rsp_data;
2473     ipmi_sel_info_t           *sel = info->sel;
2474 
2475     sel_lock(sel);
2476     if (sel->destroyed) {
2477 	ipmi_log(IPMI_LOG_ERR_INFO,
2478 		 "%ssel.c(sel_add_event_done): "
2479 		 "SEL info was destroyed while an operation was in progress",
2480 		 sel->name);
2481 	sel_add_op_done(info, ECANCELED);
2482 	goto out;
2483     }
2484 
2485     if (!mc) {
2486 	ipmi_log(IPMI_LOG_ERR_INFO,
2487 		 "%ssel.c(sel_add_event_done): "
2488 		 "MC went away while SEL op was in progress", sel->name);
2489         sel_add_op_done(info, ECANCELED);
2490 	goto out;
2491     }
2492 
2493     if (rsp->data[0] != 0) {
2494 	ipmi_log(IPMI_LOG_ERR_INFO,
2495 		 "%ssel.c(sel_add_event_done): "
2496 		 "IPMI error from SEL info fetch: %x",
2497 		 sel->name, rsp->data[0]);
2498 	sel_add_op_done(info, IPMI_IPMI_ERR_VAL(rsp->data[0]));
2499 	goto out;
2500     }
2501 
2502     if (rsp->data_len < 3) {
2503 	ipmi_log(IPMI_LOG_ERR_INFO,
2504 		 "%ssel.c(sel_add_event_done): SEL add response too short",
2505 		 sel->name);
2506 	sel_add_op_done(info, EINVAL);
2507 	goto out;
2508     }
2509 
2510     info->record_id = ipmi_get_uint16(rsp->data+1);
2511     sel_add_op_done(info, 0);
2512  out:
2513     return;
2514 }
2515 
2516 static void
sel_add_event_cb(ipmi_mc_t * mc,void * cb_data)2517 sel_add_event_cb(ipmi_mc_t *mc, void *cb_data)
2518 {
2519     sel_add_cb_handler_data_t *info = cb_data;
2520     ipmi_sel_info_t           *sel = info->sel;
2521     ipmi_event_t              *event = info->event;
2522     unsigned char             data[16];
2523     ipmi_msg_t                msg;
2524 
2525     msg.netfn = IPMI_STORAGE_NETFN;
2526     msg.cmd = IPMI_ADD_SEL_ENTRY_CMD;
2527     msg.data = data;
2528     msg.data_len = 16;
2529 
2530     ipmi_set_uint16(data, ipmi_event_get_record_id(event));
2531     data[2] = ipmi_event_get_type(event);
2532     memcpy(data+3, ipmi_event_get_data_ptr(event), 13);
2533 
2534     info->rv = ipmi_mc_send_command(mc, sel->lun, &msg, sel_add_event_done,
2535 				    info);
2536 }
2537 
2538 static int
sel_add_event_op(void * cb_data,int shutdown)2539 sel_add_event_op(void *cb_data, int shutdown)
2540 {
2541     sel_add_cb_handler_data_t *info = cb_data;
2542     ipmi_sel_info_t           *sel = info->sel;
2543     int                       rv;
2544 
2545     sel_lock(sel);
2546     if (shutdown) {
2547 	ipmi_log(IPMI_LOG_ERR_INFO,
2548 		 "%ssel.c(sel_add_event_op): "
2549 		 "SEL info was destroyed while an operation was in progress",
2550 		 sel->name);
2551 	sel_add_op_done(info, ECANCELED);
2552 	goto out;
2553     }
2554 
2555     rv = ipmi_mc_pointer_cb(sel->mc, sel_add_event_cb, info);
2556     if (rv) {
2557 	ipmi_log(IPMI_LOG_ERR_INFO,
2558 		 "%ssel.c(sel_add_event_op): MC went away during delete",
2559 		 sel->name);
2560 	sel_add_op_done(info, ECANCELED);
2561 	goto out;
2562     } else if (info->rv) {
2563 	ipmi_log(IPMI_LOG_ERR_INFO,
2564 		 "%ssel.c(sel_add_event_cb): could not send cmd: %x",
2565 		 sel->name, rv);
2566 	sel_add_op_done(info, info->rv);
2567 	goto out;
2568     }
2569 
2570     sel_unlock(sel);
2571  out:
2572     return OPQ_HANDLER_STARTED;
2573 }
2574 
2575 int
ipmi_sel_add_event_to_sel(ipmi_sel_info_t * sel,ipmi_event_t * event_to_add,ipmi_sel_add_op_done_cb_t done,void * cb_data)2576 ipmi_sel_add_event_to_sel(ipmi_sel_info_t           *sel,
2577 			  ipmi_event_t              *event_to_add,
2578 			  ipmi_sel_add_op_done_cb_t done,
2579 			  void                      *cb_data)
2580 {
2581     sel_add_cb_handler_data_t *info = cb_data;
2582     int                       rv = 0;
2583 
2584     info = ipmi_mem_alloc(sizeof(*info));
2585     if (!info)
2586 	return ENOMEM;
2587 
2588     info->sel = sel;
2589     info->event = ipmi_event_dup(event_to_add);
2590     info->handler = done;
2591     info->cb_data = cb_data;
2592     info->record_id = 0;
2593 
2594     sel_lock(sel);
2595     if (sel->destroyed) {
2596 	rv = EINVAL;
2597 	goto out_unlock;
2598     }
2599 
2600     sel_unlock(sel);
2601 
2602     /* Schedule this to run at the end of the queue. */
2603     if (!opq_new_op(sel->opq, sel_add_event_op, info, 0)) {
2604 	rv = ENOMEM;
2605 	goto out_unlock;
2606     }
2607     goto out;
2608 
2609  out_unlock:
2610     sel_unlock(sel);
2611 
2612  out:
2613     if (rv)
2614 	ipmi_mem_free(info);
2615     return rv;
2616 }
2617