106eeb2aek/*
206eeb2aek * CDDL HEADER START
306eeb2aek *
406eeb2aek * The contents of this file are subject to the terms of the
506eeb2aek * Common Development and Distribution License (the "License").
606eeb2aek * You may not use this file except in compliance with the License.
706eeb2aek *
806eeb2aek * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
906eeb2aek * or http://www.opensolaris.org/os/licensing.
1006eeb2aek * See the License for the specific language governing permissions
1106eeb2aek * and limitations under the License.
1206eeb2aek *
1306eeb2aek * When distributing Covered Code, include this CDDL HEADER in each
1406eeb2aek * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1506eeb2aek * If applicable, add the following below this CDDL HEADER, with the
1606eeb2aek * fields enclosed by brackets "[]" replaced with your own identifying
1706eeb2aek * information: Portions Copyright [yyyy] [name of copyright owner]
1806eeb2aek *
1906eeb2aek * CDDL HEADER END
2006eeb2aek */
2106eeb2aek
2206eeb2aek/*
233f9d6adLin Ling * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
249adfa60Matthew Ahrens * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
25c3d26abMatthew Ahrens * Copyright (c) 2014 Integros [integros.com]
26ce1577bDave Eddy * Copyright 2017 Joyent, Inc.
2706eeb2aek */
2806eeb2aek
29ecd6cf8marks#include <sys/spa.h>
3006eeb2aek#include <sys/spa_impl.h>
3106eeb2aek#include <sys/zap.h>
3206eeb2aek#include <sys/dsl_synctask.h>
33ecd6cf8marks#include <sys/dmu_tx.h>
34ecd6cf8marks#include <sys/dmu_objset.h>
354445fffMatthew Ahrens#include <sys/dsl_dataset.h>
364445fffMatthew Ahrens#include <sys/dsl_dir.h>
37ecd6cf8marks#include <sys/utsname.h>
38ecd6cf8marks#include <sys/cmn_err.h>
39ecd6cf8marks#include <sys/sunddi.h>
404445fffMatthew Ahrens#include <sys/cred.h>
413f9d6adLin Ling#include "zfs_comutil.h"
42ecd6cf8marks#ifdef _KERNEL
43ecd6cf8marks#include <sys/zone.h>
44ecd6cf8marks#endif
4506eeb2aek
4606eeb2aek/*
4706eeb2aek * Routines to manage the on-disk history log.
4806eeb2aek *
4906eeb2aek * The history log is stored as a dmu object containing
5006eeb2aek * <packed record length, record nvlist> tuples.
5106eeb2aek *
5206eeb2aek * Where "record nvlist" is a nvlist containing uint64_ts and strings, and
5306eeb2aek * "packed record length" is the packed length of the "record nvlist" stored
5406eeb2aek * as a little endian uint64_t.
5506eeb2aek *
5606eeb2aek * The log is implemented as a ring buffer, though the original creation
5706eeb2aek * of the pool ('zpool create') is never overwritten.
5806eeb2aek *
5906eeb2aek * The history log is tracked as object 'spa_t::spa_history'.  The bonus buffer
6006eeb2aek * of 'spa_history' stores the offsets for logging/retrieving history as
6106eeb2aek * 'spa_history_phys_t'.  'sh_pool_create_len' is the ending offset in bytes of
6206eeb2aek * where the 'zpool create' record is stored.  This allows us to never
6306eeb2aek * overwrite the original creation of the pool.  'sh_phys_max_off' is the
6406eeb2aek * physical ending offset in bytes of the log.  This tells you the length of
6506eeb2aek * the buffer. 'sh_eof' is the logical EOF (in bytes).  Whenever a record
6606eeb2aek * is added, 'sh_eof' is incremented by the the size of the record.
6706eeb2aek * 'sh_eof' is never decremented.  'sh_bof' is the logical BOF (in bytes).
6806eeb2aek * This is where the consumer should start reading from after reading in
6906eeb2aek * the 'zpool create' portion of the log.
7006eeb2aek *
7106eeb2aek * 'sh_records_lost' keeps track of how many records have been overwritten
7206eeb2aek * and permanently lost.
7306eeb2aek */
7406eeb2aek
7506eeb2aek/* convert a logical offset to physical */
7606eeb2aekstatic uint64_t
7706eeb2aekspa_history_log_to_phys(uint64_t log_off, spa_history_phys_t *shpp)
7806eeb2aek{
7906eeb2aek	uint64_t phys_len;
8006eeb2aek
8106eeb2aek	phys_len = shpp->sh_phys_max_off - shpp->sh_pool_create_len;
8206eeb2aek	return ((log_off - shpp->sh_pool_create_len) % phys_len
8306eeb2aek	    + shpp->sh_pool_create_len);
8406eeb2aek}
8506eeb2aek
8606eeb2aekvoid
8706eeb2aekspa_history_create_obj(spa_t *spa, dmu_tx_t *tx)
8806eeb2aek{
8906eeb2aek	dmu_buf_t *dbp;
9006eeb2aek	spa_history_phys_t *shpp;
9106eeb2aek	objset_t *mos = spa->spa_meta_objset;
9206eeb2aek
9306eeb2aek	ASSERT(spa->spa_history == 0);
9406eeb2aek	spa->spa_history = dmu_object_alloc(mos, DMU_OT_SPA_HISTORY,
95b515258Matthew Ahrens	    SPA_OLD_MAXBLOCKSIZE, DMU_OT_SPA_HISTORY_OFFSETS,
9606eeb2aek	    sizeof (spa_history_phys_t), tx);
9706eeb2aek
9806eeb2aek	VERIFY(zap_add(mos, DMU_POOL_DIRECTORY_OBJECT,
9906eeb2aek	    DMU_POOL_HISTORY, sizeof (uint64_t), 1,
10006eeb2aek	    &spa->spa_history, tx) == 0);
10106eeb2aek
10206eeb2aek	VERIFY(0 == dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp));
10306eeb2aek	ASSERT(dbp->db_size >= sizeof (spa_history_phys_t));
10406eeb2aek
10506eeb2aek	shpp = dbp->db_data;
10606eeb2aek	dmu_buf_will_dirty(dbp, tx);
10706eeb2aek
10806eeb2aek	/*
10906eeb2aek	 * Figure out maximum size of history log.  We set it at
11019b94dfMatthew Ahrens	 * 0.1% of pool size, with a max of 1G and min of 128KB.
11106eeb2aek	 */
112b24ab67Jeff Bonwick	shpp->sh_phys_max_off =
11319b94dfMatthew Ahrens	    metaslab_class_get_dspace(spa_normal_class(spa)) / 1000;
11419b94dfMatthew Ahrens	shpp->sh_phys_max_off = MIN(shpp->sh_phys_max_off, 1<<30);
11506eeb2aek	shpp->sh_phys_max_off = MAX(shpp->sh_phys_max_off, 128<<10);
11606eeb2aek
11706eeb2aek	dmu_buf_rele(dbp, FTAG);
11806eeb2aek}
11906eeb2aek
12006eeb2aek/*
12106eeb2aek * Change 'sh_bof' to the beginning of the next record.
12206eeb2aek */
12306eeb2aekstatic int
12406eeb2aekspa_history_advance_bof(spa_t *spa, spa_history_phys_t *shpp)
12506eeb2aek{
12606eeb2aek	objset_t *mos = spa->spa_meta_objset;
12706eeb2aek	uint64_t firstread, reclen, phys_bof;
12806eeb2aek	char buf[sizeof (reclen)];
12906eeb2aek	int err;
13006eeb2aek
13106eeb2aek	phys_bof = spa_history_log_to_phys(shpp->sh_bof, shpp);
13206eeb2aek	firstread = MIN(sizeof (reclen), shpp->sh_phys_max_off - phys_bof);
13306eeb2aek
13406eeb2aek	if ((err = dmu_read(mos, spa->spa_history, phys_bof, firstread,
1357bfdf01Neil Perrin	    buf, DMU_READ_PREFETCH)) != 0)
13606eeb2aek		return (err);
13706eeb2aek	if (firstread != sizeof (reclen)) {
13806eeb2aek		if ((err = dmu_read(mos, spa->spa_history,
13906eeb2aek		    shpp->sh_pool_create_len, sizeof (reclen) - firstread,
1407bfdf01Neil Perrin		    buf + firstread, DMU_READ_PREFETCH)) != 0)
14106eeb2aek			return (err);
14206eeb2aek	}
14306eeb2aek
14406eeb2aek	reclen = LE_64(*((uint64_t *)buf));
14506eeb2aek	shpp->sh_bof += reclen + sizeof (reclen);
14606eeb2aek	shpp->sh_records_lost++;
14706eeb2aek	return (0);
14806eeb2aek}
14906eeb2aek
15006eeb2aekstatic int
15106eeb2aekspa_history_write(spa_t *spa, void *buf, uint64_t len, spa_history_phys_t *shpp,
15206eeb2aek    dmu_tx_t *tx)
15306eeb2aek{
15406eeb2aek	uint64_t firstwrite, phys_eof;
15506eeb2aek	objset_t *mos = spa->spa_meta_objset;
15606eeb2aek	int err;
15706eeb2aek
15806eeb2aek	ASSERT(MUTEX_HELD(&spa->spa_history_lock));
15906eeb2aek
16006eeb2aek	/* see if we need to reset logical BOF */
16106eeb2aek	while (shpp->sh_phys_max_off - shpp->sh_pool_create_len -
16206eeb2aek	    (shpp->sh_eof - shpp->sh_bof) <= len) {
163ecd6cf8marks		if ((err = spa_history_advance_bof(spa, shpp)) != 0) {
16406eeb2aek			return (err);
165ecd6cf8marks		}
16606eeb2aek	}
16706eeb2aek
16806eeb2aek	phys_eof = spa_history_log_to_phys(shpp->sh_eof, shpp);
16906eeb2aek	firstwrite = MIN(len, shpp->sh_phys_max_off - phys_eof);
17006eeb2aek	shpp->sh_eof += len;
17106eeb2aek	dmu_write(mos, spa->spa_history, phys_eof, firstwrite, buf, tx);
17206eeb2aek
17306eeb2aek	len -= firstwrite;
17406eeb2aek	if (len > 0) {
17506eeb2aek		/* write out the rest at the beginning of physical file */
17606eeb2aek		dmu_write(mos, spa->spa_history, shpp->sh_pool_create_len,
17706eeb2aek		    len, (char *)buf + firstwrite, tx);
17806eeb2aek	}
17906eeb2aek
18006eeb2aek	return (0);
18106eeb2aek}
18206eeb2aek
183ecd6cf8marksstatic char *
1844445fffMatthew Ahrensspa_history_zone(void)
185ecd6cf8marks{
186ecd6cf8marks#ifdef _KERNEL
1874445fffMatthew Ahrens	if (INGLOBALZONE(curproc))
1884445fffMatthew Ahrens		return (NULL);
189ecd6cf8marks	return (curproc->p_zone->zone_name);
190ecd6cf8marks#else
1914445fffMatthew Ahrens	return (NULL);
192ecd6cf8marks#endif
193ecd6cf8marks}
194ecd6cf8marks
19506eeb2aek/*
196ce1577bDave Eddy * Post a history sysevent.
197ce1577bDave Eddy *
198ce1577bDave Eddy * The nvlist_t* passed into this function will be transformed into a new
199ce1577bDave Eddy * nvlist where:
200ce1577bDave Eddy *
201ce1577bDave Eddy * 1. Nested nvlists will be flattened to a single level
202ce1577bDave Eddy * 2. Keys will have their names normalized (to remove any problematic
203ce1577bDave Eddy * characters, such as whitespace)
204ce1577bDave Eddy *
205ce1577bDave Eddy * The nvlist_t passed into this function will duplicated and should be freed
206ce1577bDave Eddy * by caller.
207ce1577bDave Eddy *
208ce1577bDave Eddy */
209ce1577bDave Eddystatic void
210ce1577bDave Eddyspa_history_log_notify(spa_t *spa, nvlist_t *nvl)
211ce1577bDave Eddy{
212ce1577bDave Eddy	nvlist_t *hist_nvl = fnvlist_alloc();
213ce1577bDave Eddy	uint64_t uint64;
214ce1577bDave Eddy	char *string;
215ce1577bDave Eddy
216ce1577bDave Eddy	if (nvlist_lookup_string(nvl, ZPOOL_HIST_CMD, &string) == 0)
217ce1577bDave Eddy		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_CMD, string);
218ce1577bDave Eddy
219ce1577bDave Eddy	if (nvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME, &string) == 0)
220ce1577bDave Eddy		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_INT_NAME, string);
221ce1577bDave Eddy
222ce1577bDave Eddy	if (nvlist_lookup_string(nvl, ZPOOL_HIST_ZONE, &string) == 0)
223ce1577bDave Eddy		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_ZONE, string);
224ce1577bDave Eddy
225ce1577bDave Eddy	if (nvlist_lookup_string(nvl, ZPOOL_HIST_HOST, &string) == 0)
226ce1577bDave Eddy		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_HOST, string);
227ce1577bDave Eddy
228ce1577bDave Eddy	if (nvlist_lookup_string(nvl, ZPOOL_HIST_DSNAME, &string) == 0)
229ce1577bDave Eddy		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_DSNAME, string);
230ce1577bDave Eddy
231ce1577bDave Eddy	if (nvlist_lookup_string(nvl, ZPOOL_HIST_INT_STR, &string) == 0)
232ce1577bDave Eddy		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_INT_STR, string);
233ce1577bDave Eddy
234ce1577bDave Eddy	if (nvlist_lookup_string(nvl, ZPOOL_HIST_IOCTL, &string) == 0)
235ce1577bDave Eddy		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_IOCTL, string);
236ce1577bDave Eddy
237ce1577bDave Eddy	if (nvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME, &string) == 0)
238ce1577bDave Eddy		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_INT_NAME, string);
239ce1577bDave Eddy
240ce1577bDave Eddy	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_DSID, &uint64) == 0)
241ce1577bDave Eddy		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_DSID, uint64);
242ce1577bDave Eddy
243ce1577bDave Eddy	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_TXG, &uint64) == 0)
244ce1577bDave Eddy		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_TXG, uint64);
245ce1577bDave Eddy
246ce1577bDave Eddy	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_TIME, &uint64) == 0)
247ce1577bDave Eddy		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_TIME, uint64);
248ce1577bDave Eddy
249ce1577bDave Eddy	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_WHO, &uint64) == 0)
250ce1577bDave Eddy		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_WHO, uint64);
251ce1577bDave Eddy
252ce1577bDave Eddy	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_INT_EVENT, &uint64) == 0)
253ce1577bDave Eddy		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_INT_EVENT, uint64);
254ce1577bDave Eddy
255ce1577bDave Eddy	spa_event_notify(spa, NULL, hist_nvl, ESC_ZFS_HISTORY_EVENT);
256ce1577bDave Eddy
257ce1577bDave Eddy	nvlist_free(hist_nvl);
258ce1577bDave Eddy}
259ce1577bDave Eddy
260ce1577bDave Eddy/*
26106eeb2aek * Write out a history event.
26206eeb2aek */
263495807dMatthew Ahrens/*ARGSUSED*/
264e743726ahrensstatic void
2653b2aab1Matthew Ahrensspa_history_log_sync(void *arg, dmu_tx_t *tx)
26606eeb2aek{
2673b2aab1Matthew Ahrens	nvlist_t	*nvl = arg;
2683b2aab1Matthew Ahrens	spa_t		*spa = dmu_tx_pool(tx)->dp_spa;
26906eeb2aek	objset_t	*mos = spa->spa_meta_objset;
27006eeb2aek	dmu_buf_t	*dbp;
27106eeb2aek	spa_history_phys_t *shpp;
27206eeb2aek	size_t		reclen;
27306eeb2aek	uint64_t	le_len;
27406eeb2aek	char		*record_packed = NULL;
27506eeb2aek	int		ret;
27606eeb2aek
27706eeb2aek	/*
27806eeb2aek	 * If we have an older pool that doesn't have a command
27906eeb2aek	 * history object, create it now.
28006eeb2aek	 */
28106eeb2aek	mutex_enter(&spa->spa_history_lock);
28206eeb2aek	if (!spa->spa_history)
28306eeb2aek		spa_history_create_obj(spa, tx);
28406eeb2aek	mutex_exit(&spa->spa_history_lock);
28506eeb2aek
28606eeb2aek	/*
28706eeb2aek	 * Get the offset of where we need to write via the bonus buffer.
28806eeb2aek	 * Update the offset when the write completes.
28906eeb2aek	 */
2903b2aab1Matthew Ahrens	VERIFY0(dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp));
29106eeb2aek	shpp = dbp->db_data;
29206eeb2aek
29306eeb2aek	dmu_buf_will_dirty(dbp, tx);
29406eeb2aek
29506eeb2aek#ifdef ZFS_DEBUG
29606eeb2aek	{
29706eeb2aek		dmu_object_info_t doi;
29806eeb2aek		dmu_object_info_from_db(dbp, &doi);
29906eeb2aek		ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_SPA_HISTORY_OFFSETS);
30006eeb2aek	}
30106eeb2aek#endif
30206eeb2aek
3034445fffMatthew Ahrens	fnvlist_add_uint64(nvl, ZPOOL_HIST_TIME, gethrestime_sec());
304ecd6cf8marks#ifdef _KERNEL
3054445fffMatthew Ahrens	fnvlist_add_string(nvl, ZPOOL_HIST_HOST, utsname.nodename);
306ecd6cf8marks#endif
3074445fffMatthew Ahrens	if (nvlist_exists(nvl, ZPOOL_HIST_CMD)) {
3084445fffMatthew Ahrens		zfs_dbgmsg("command: %s",
3094445fffMatthew Ahrens		    fnvlist_lookup_string(nvl, ZPOOL_HIST_CMD));
3104445fffMatthew Ahrens	} else if (nvlist_exists(nvl, ZPOOL_HIST_INT_NAME)) {
3114445fffMatthew Ahrens		if (nvlist_exists(nvl, ZPOOL_HIST_DSNAME)) {
3124445fffMatthew Ahrens			zfs_dbgmsg("txg %lld %s %s (id %llu) %s",
3134445fffMatthew Ahrens			    fnvlist_lookup_uint64(nvl, ZPOOL_HIST_TXG),
3144445fffMatthew Ahrens			    fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME),
3154445fffMatthew Ahrens			    fnvlist_lookup_string(nvl, ZPOOL_HIST_DSNAME),
3164445fffMatthew Ahrens			    fnvlist_lookup_uint64(nvl, ZPOOL_HIST_DSID),
3174445fffMatthew Ahrens			    fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_STR));
3184445fffMatthew Ahrens		} else {
3194445fffMatthew Ahrens			zfs_dbgmsg("txg %lld %s %s",
3204445fffMatthew Ahrens			    fnvlist_lookup_uint64(nvl, ZPOOL_HIST_TXG),
3214445fffMatthew Ahrens			    fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME),
3224445fffMatthew Ahrens			    fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_STR));
3234445fffMatthew Ahrens		}
324ce1577bDave Eddy		/*
325ce1577bDave Eddy		 * The history sysevent is posted only for internal history
326ce1577bDave Eddy		 * messages to show what has happened, not how it happened. For
327ce1577bDave Eddy		 * example, the following command:
328ce1577bDave Eddy		 *
329ce1577bDave Eddy		 * # zfs destroy -r tank/foo
330ce1577bDave Eddy		 *
331ce1577bDave Eddy		 * will result in one sysevent posted per dataset that is
332ce1577bDave Eddy		 * destroyed as a result of the command - which could be more
333ce1577bDave Eddy		 * than one event in total.  By contrast, if the sysevent was
334ce1577bDave Eddy		 * posted as a result of the ZPOOL_HIST_CMD key being present
335ce1577bDave Eddy		 * it would result in only one sysevent being posted with the
336ce1577bDave Eddy		 * full command line arguments, requiring the consumer to know
337ce1577bDave Eddy		 * how to parse and understand zfs(1M) command invocations.
338ce1577bDave Eddy		 */
339ce1577bDave Eddy		spa_history_log_notify(spa, nvl);
3404445fffMatthew Ahrens	} else if (nvlist_exists(nvl, ZPOOL_HIST_IOCTL)) {
3414445fffMatthew Ahrens		zfs_dbgmsg("ioctl %s",
3424445fffMatthew Ahrens		    fnvlist_lookup_string(nvl, ZPOOL_HIST_IOCTL));
343ecd6cf8marks	}
344ecd6cf8marks
3454445fffMatthew Ahrens	record_packed = fnvlist_pack(nvl, &reclen);
34606eeb2aek
34706eeb2aek	mutex_enter(&spa->spa_history_lock);
34806eeb2aek
34906eeb2aek	/* write out the packed length as little endian */
35055434c7ek	le_len = LE_64((uint64_t)reclen);
35106eeb2aek	ret = spa_history_write(spa, &le_len, sizeof (le_len), shpp, tx);
35206eeb2aek	if (!ret)
35306eeb2aek		ret = spa_history_write(spa, record_packed, reclen, shpp, tx);
35406eeb2aek
3554445fffMatthew Ahrens	/* The first command is the create, which we keep forever */
3564445fffMatthew Ahrens	if (ret == 0 && shpp->sh_pool_create_len == 0 &&
3574445fffMatthew Ahrens	    nvlist_exists(nvl, ZPOOL_HIST_CMD)) {
3584445fffMatthew Ahrens		shpp->sh_pool_create_len = shpp->sh_bof = shpp->sh_eof;
35906eeb2aek	}
36006eeb2aek
36106eeb2aek	mutex_exit(&spa->spa_history_lock);
3624445fffMatthew Ahrens	fnvlist_pack_free(record_packed, reclen);
36306eeb2aek	dmu_buf_rele(dbp, FTAG);
3644445fffMatthew Ahrens	fnvlist_free(nvl);
36506eeb2aek}
36606eeb2aek
36706eeb2aek/*
36806eeb2aek * Write out a history event.
36906eeb2aek */
37006eeb2aekint
3714445fffMatthew Ahrensspa_history_log(spa_t *spa, const char *msg)
3724445fffMatthew Ahrens{
3734445fffMatthew Ahrens	int err;
3744445fffMatthew Ahrens	nvlist_t *nvl = fnvlist_alloc();
3754445fffMatthew Ahrens
3764445fffMatthew Ahrens	fnvlist_add_string(nvl, ZPOOL_HIST_CMD, msg);
3774445fffMatthew Ahrens	err = spa_history_log_nvl(spa, nvl);
3784445fffMatthew Ahrens	fnvlist_free(nvl);
3794445fffMatthew Ahrens	return (err);
3804445fffMatthew Ahrens}
3814445fffMatthew Ahrens
3824445fffMatthew Ahrensint
3834445fffMatthew Ahrensspa_history_log_nvl(spa_t *spa, nvlist_t *nvl)
38406eeb2aek{
385495807dMatthew Ahrens	int err = 0;
386495807dMatthew Ahrens	dmu_tx_t *tx;
387eb63303Tom Caputi	nvlist_t *nvarg, *in_nvl = NULL;
38806eeb2aek
389cd1c8b8Matthew Ahrens	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY || !spa_writeable(spa))
390be6fd75Matthew Ahrens		return (SET_ERROR(EINVAL));
391e743726ahrens
392eb63303Tom Caputi	err = nvlist_lookup_nvlist(nvl, ZPOOL_HIST_INPUT_NVL, &in_nvl);
393eb63303Tom Caputi	if (err == 0) {
394eb63303Tom Caputi		(void) nvlist_remove_all(in_nvl, ZPOOL_HIDDEN_ARGS);
395eb63303Tom Caputi	}
396eb63303Tom Caputi
397495807dMatthew Ahrens	tx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
398495807dMatthew Ahrens	err = dmu_tx_assign(tx, TXG_WAIT);
399495807dMatthew Ahrens	if (err) {
400495807dMatthew Ahrens		dmu_tx_abort(tx);
401495807dMatthew Ahrens		return (err);
402495807dMatthew Ahrens	}
403495807dMatthew Ahrens
4044445fffMatthew Ahrens	nvarg = fnvlist_dup(nvl);
4054445fffMatthew Ahrens	if (spa_history_zone() != NULL) {
4064445fffMatthew Ahrens		fnvlist_add_string(nvarg, ZPOOL_HIST_ZONE,
4074445fffMatthew Ahrens		    spa_history_zone());
4084445fffMatthew Ahrens	}
4094445fffMatthew Ahrens	fnvlist_add_uint64(nvarg, ZPOOL_HIST_WHO, crgetruid(CRED()));
410495807dMatthew Ahrens
411495807dMatthew Ahrens	/* Kick this off asynchronously; errors are ignored. */
4123b2aab1Matthew Ahrens	dsl_sync_task_nowait(spa_get_dsl(spa), spa_history_log_sync,
4137d46dc6Matthew Ahrens	    nvarg, 0, ZFS_SPACE_CHECK_NONE, tx);
414495807dMatthew Ahrens	dmu_tx_commit(tx);
415495807dMatthew Ahrens
4164445fffMatthew Ahrens	/* spa_history_log_sync will free nvl */
417495807dMatthew Ahrens	return (err);
4184445fffMatthew Ahrens
41906eeb2aek}
42006eeb2aek
42106eeb2aek/*
42206eeb2aek * Read out the command history.
42306eeb2aek */
42406eeb2aekint
42506eeb2aekspa_history_get(spa_t *spa, uint64_t *offp, uint64_t *len, char *buf)
42606eeb2aek{
42706eeb2aek	objset_t *mos = spa->spa_meta_objset;
42806eeb2aek	dmu_buf_t *dbp;
42906eeb2aek	uint64_t read_len, phys_read_off, phys_eof;
43006eeb2aek	uint64_t leftover = 0;
43106eeb2aek	spa_history_phys_t *shpp;
43206eeb2aek	int err;
43306eeb2aek
43406eeb2aek	/*
4354445fffMatthew Ahrens	 * If the command history doesn't exist (older pool),
43606eeb2aek	 * that's ok, just return ENOENT.
43706eeb2aek	 */
43806eeb2aek	if (!spa->spa_history)
439be6fd75Matthew Ahrens		return (SET_ERROR(ENOENT));
44006eeb2aek
441495807dMatthew Ahrens	/*
442495807dMatthew Ahrens	 * The history is logged asynchronously, so when they request
443495807dMatthew Ahrens	 * the first chunk of history, make sure everything has been
444495807dMatthew Ahrens	 * synced to disk so that we get it.
445495807dMatthew Ahrens	 */
4463c70851Mark J Musante	if (*offp == 0 && spa_writeable(spa))
447495807dMatthew Ahrens		txg_wait_synced(spa_get_dsl(spa), 0);
448495807dMatthew Ahrens
44906eeb2aek	if ((err = dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp)) != 0)
45006eeb2aek		return (err);
45106eeb2aek	shpp = dbp->db_data;
45206eeb2aek
45306eeb2aek#ifdef ZFS_DEBUG
45406eeb2aek	{
45506eeb2aek		dmu_object_info_t doi;
45606eeb2aek		dmu_object_info_from_db(dbp, &doi);
45706eeb2aek		ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_SPA_HISTORY_OFFSETS);
45806eeb2aek	}
45906eeb2aek#endif
46006eeb2aek
46106eeb2aek	mutex_enter(&spa->spa_history_lock);
46206eeb2aek	phys_eof = spa_history_log_to_phys(shpp->sh_eof, shpp);
46306eeb2aek
46406eeb2aek	if (*offp < shpp->sh_pool_create_len) {
46506eeb2aek		/* read in just the zpool create history */
46606eeb2aek		phys_read_off = *offp;
46706eeb2aek		read_len = MIN(*len, shpp->sh_pool_create_len -
46806eeb2aek		    phys_read_off);
46906eeb2aek	} else {
47006eeb2aek		/*
47106eeb2aek		 * Need to reset passed in offset to BOF if the passed in
47206eeb2aek		 * offset has since been overwritten.
47306eeb2aek		 */
47406eeb2aek		*offp = MAX(*offp, shpp->sh_bof);
47506eeb2aek		phys_read_off = spa_history_log_to_phys(*offp, shpp);
47606eeb2aek
47706eeb2aek		/*
47806eeb2aek		 * Read up to the minimum of what the user passed down or
47906eeb2aek		 * the EOF (physical or logical).  If we hit physical EOF,
48006eeb2aek		 * use 'leftover' to read from the physical BOF.
48106eeb2aek		 */
48206eeb2aek		if (phys_read_off <= phys_eof) {
48306eeb2aek			read_len = MIN(*len, phys_eof - phys_read_off);
48406eeb2aek		} else {
48506eeb2aek			read_len = MIN(*len,
48606eeb2aek			    shpp->sh_phys_max_off - phys_read_off);
48706eeb2aek			if (phys_read_off + *len > shpp->sh_phys_max_off) {
48806eeb2aek				leftover = MIN(*len - read_len,
48906eeb2aek				    phys_eof - shpp->sh_pool_create_len);
49006eeb2aek			}
49106eeb2aek		}
49206eeb2aek	}
49306eeb2aek
49406eeb2aek	/* offset for consumer to use next */
49506eeb2aek	*offp += read_len + leftover;
49606eeb2aek
49706eeb2aek	/* tell the consumer how much you actually read */
49806eeb2aek	*len = read_len + leftover;
49906eeb2aek
50006eeb2aek	if (read_len == 0) {
50106eeb2aek		mutex_exit(&spa->spa_history_lock);
50206eeb2aek		dmu_buf_rele(dbp, FTAG);
50306eeb2aek		return (0);
50406eeb2aek	}
50506eeb2aek
5067bfdf01Neil Perrin	err = dmu_read(mos, spa->spa_history, phys_read_off, read_len, buf,
5077bfdf01Neil Perrin	    DMU_READ_PREFETCH);
50806eeb2aek	if (leftover && err == 0) {
50906eeb2aek		err = dmu_read(mos, spa->spa_history, shpp->sh_pool_create_len,
5107bfdf01Neil Perrin		    leftover, buf + read_len, DMU_READ_PREFETCH);
51106eeb2aek	}
51206eeb2aek	mutex_exit(&spa->spa_history_lock);
51306eeb2aek
51406eeb2aek	dmu_buf_rele(dbp, FTAG);
51506eeb2aek	return (err);
51606eeb2aek}
517ecd6cf8marks
5184445fffMatthew Ahrens/*
5194445fffMatthew Ahrens * The nvlist will be consumed by this call.
5204445fffMatthew Ahrens */
521c8e1f6dMark J Musantestatic void
5224445fffMatthew Ahrenslog_internal(nvlist_t *nvl, const char *operation, spa_t *spa,
5233f9d6adLin Ling    dmu_tx_t *tx, const char *fmt, va_list adx)
524ecd6cf8marks{
5254445fffMatthew Ahrens	char *msg;
526ecd6cf8marks
527088f389ahrens	/*
528088f389ahrens	 * If this is part of creating a pool, not everything is
529088f389ahrens	 * initialized yet, so don't bother logging the internal events.
530cd1c8b8Matthew Ahrens	 * Likewise if the pool is not writeable.
531088f389ahrens	 */
532cd1c8b8Matthew Ahrens	if (tx->tx_txg == TXG_INITIAL || !spa_writeable(spa)) {
533347eec8Christopher Siden		fnvlist_free(nvl);
534088f389ahrens		return;
535347eec8Christopher Siden	}
536088f389ahrens
5374445fffMatthew Ahrens	msg = kmem_alloc(vsnprintf(NULL, 0, fmt, adx) + 1, KM_SLEEP);
5384445fffMatthew Ahrens	(void) vsprintf(msg, fmt, adx);
5394445fffMatthew Ahrens	fnvlist_add_string(nvl, ZPOOL_HIST_INT_STR, msg);
5404445fffMatthew Ahrens	strfree(msg);
541ecd6cf8marks
5424445fffMatthew Ahrens	fnvlist_add_string(nvl, ZPOOL_HIST_INT_NAME, operation);
5434445fffMatthew Ahrens	fnvlist_add_uint64(nvl, ZPOOL_HIST_TXG, tx->tx_txg);
544e743726ahrens
545e743726ahrens	if (dmu_tx_is_syncing(tx)) {
5463b2aab1Matthew Ahrens		spa_history_log_sync(nvl, tx);
547e743726ahrens	} else {
5483b2aab1Matthew Ahrens		dsl_sync_task_nowait(spa_get_dsl(spa),
5497d46dc6Matthew Ahrens		    spa_history_log_sync, nvl, 0, ZFS_SPACE_CHECK_NONE, tx);
550e743726ahrens	}
5514445fffMatthew Ahrens	/* spa_history_log_sync() will free nvl */
552ecd6cf8marks}
553c8e1f6dMark J Musante
554c8e1f6dMark J Musantevoid
5554445fffMatthew Ahrensspa_history_log_internal(spa_t *spa, const char *operation,
5563f9d6adLin Ling    dmu_tx_t *tx, const char *fmt, ...)
557c8e1f6dMark J Musante{
558c8e1f6dMark J Musante	dmu_tx_t *htx = tx;
559c8e1f6dMark J Musante	va_list adx;
560c8e1f6dMark J Musante
561c8e1f6dMark J Musante	/* create a tx if we didn't get one */
562c8e1f6dMark J Musante	if (tx == NULL) {
563c8e1f6dMark J Musante		htx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
564c8e1f6dMark J Musante		if (dmu_tx_assign(htx, TXG_WAIT) != 0) {
565c8e1f6dMark J Musante			dmu_tx_abort(htx);
566c8e1f6dMark J Musante			return;
567c8e1f6dMark J Musante		}
568c8e1f6dMark J Musante	}
569c8e1f6dMark J Musante
570c8e1f6dMark J Musante	va_start(adx, fmt);
5714445fffMatthew Ahrens	log_internal(fnvlist_alloc(), operation, spa, htx, fmt, adx);
572c8e1f6dMark J Musante	va_end(adx);
573c8e1f6dMark J Musante
574c8e1f6dMark J Musante	/* if we didn't get a tx from the caller, commit the one we made */
575c8e1f6dMark J Musante	if (tx == NULL)
576c8e1f6dMark J Musante		dmu_tx_commit(htx);
577c8e1f6dMark J Musante}
578c8e1f6dMark J Musante
579c8e1f6dMark J Musantevoid
5804445fffMatthew Ahrensspa_history_log_internal_ds(dsl_dataset_t *ds, const char *operation,
5814445fffMatthew Ahrens    dmu_tx_t *tx, const char *fmt, ...)
5824445fffMatthew Ahrens{
5834445fffMatthew Ahrens	va_list adx;
5849adfa60Matthew Ahrens	char namebuf[ZFS_MAX_DATASET_NAME_LEN];
5854445fffMatthew Ahrens	nvlist_t *nvl = fnvlist_alloc();
5864445fffMatthew Ahrens
5874445fffMatthew Ahrens	ASSERT(tx != NULL);
5884445fffMatthew Ahrens
5894445fffMatthew Ahrens	dsl_dataset_name(ds, namebuf);
5904445fffMatthew Ahrens	fnvlist_add_string(nvl, ZPOOL_HIST_DSNAME, namebuf);
5914445fffMatthew Ahrens	fnvlist_add_uint64(nvl, ZPOOL_HIST_DSID, ds->ds_object);
5924445fffMatthew Ahrens
5934445fffMatthew Ahrens	va_start(adx, fmt);
5944445fffMatthew Ahrens	log_internal(nvl, operation, dsl_dataset_get_spa(ds), tx, fmt, adx);
5954445fffMatthew Ahrens	va_end(adx);
5964445fffMatthew Ahrens}
5974445fffMatthew Ahrens
5984445fffMatthew Ahrensvoid
5994445fffMatthew Ahrensspa_history_log_internal_dd(dsl_dir_t *dd, const char *operation,
6004445fffMatthew Ahrens    dmu_tx_t *tx, const char *fmt, ...)
6014445fffMatthew Ahrens{
6024445fffMatthew Ahrens	va_list adx;
6039adfa60Matthew Ahrens	char namebuf[ZFS_MAX_DATASET_NAME_LEN];
6044445fffMatthew Ahrens	nvlist_t *nvl = fnvlist_alloc();
6054445fffMatthew Ahrens
6064445fffMatthew Ahrens	ASSERT(tx != NULL);
6074445fffMatthew Ahrens
6084445fffMatthew Ahrens	dsl_dir_name(dd, namebuf);
6094445fffMatthew Ahrens	fnvlist_add_string(nvl, ZPOOL_HIST_DSNAME, namebuf);
6104445fffMatthew Ahrens	fnvlist_add_uint64(nvl, ZPOOL_HIST_DSID,
611c137962Justin T. Gibbs	    dsl_dir_phys(dd)->dd_head_dataset_obj);
6124445fffMatthew Ahrens
6134445fffMatthew Ahrens	va_start(adx, fmt);
6144445fffMatthew Ahrens	log_internal(nvl, operation, dd->dd_pool->dp_spa, tx, fmt, adx);
6154445fffMatthew Ahrens	va_end(adx);
6164445fffMatthew Ahrens}
6174445fffMatthew Ahrens
6184445fffMatthew Ahrensvoid
6194445fffMatthew Ahrensspa_history_log_version(spa_t *spa, const char *operation)
620c8e1f6dMark J Musante{
6214445fffMatthew Ahrens	spa_history_log_internal(spa, operation, NULL,
622b11fe8cJustin Hibbits	    "pool version %llu; software version %llu/%llu; uts %s %s %s %s",
6233b2aab1Matthew Ahrens	    (u_longlong_t)spa_version(spa), SPA_VERSION, ZPL_VERSION,
6244445fffMatthew Ahrens	    utsname.nodename, utsname.release, utsname.version,
6254445fffMatthew Ahrens	    utsname.machine);
626c8e1f6dMark J Musante}
627