1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/cmn_err.h>
  29 #include <sys/mman.h>
  30 #include <sys/systm.h>
  31 #include <vm/xhat.h>
  32 #include <vm/page.h>
  33 #include <vm/as.h>
  34 
  35 int xhat_debug = 0;
  36 
  37 krwlock_t xhat_provider_rwlock;
  38 xhat_provider_t *xhat_provider = NULL;
  39 
  40 void
  41 xhat_init()
  42 {
  43         rw_init(&xhat_provider_rwlock, NULL, RW_DEFAULT, NULL);
  44 }
  45 
  46 
  47 
  48 int
  49 xhat_provider_register(xhat_provider_t *provider)
  50 {
  51         /* strlen("_cache") = 7 */
  52         char    cache_name[XHAT_CACHE_NAMELEN + 7];
  53 
  54 
  55         if (provider->xhat_provider_version != XHAT_PROVIDER_VERSION) {
  56                 cmn_err(CE_WARN, "XHAT provider version mismatch");
  57                 return (-1);
  58         }
  59 
  60         if ((XHAT_POPS(provider)->xhat_alloc == NULL) ||
  61             (XHAT_POPS(provider)->xhat_free == NULL)) {
  62                 cmn_err(CE_WARN, "Malformed XHAT provider");
  63                 return (-1);
  64         }
  65 
  66         /* Allocate kmem_cache which will manage xhat blocks */
  67         provider->xblkcache->free_blks = NULL;
  68         (void) strncpy(cache_name, provider->xhat_provider_name,
  69             XHAT_CACHE_NAMELEN);
  70         (void) strcat(cache_name, "_cache");
  71         provider->xblkcache->cache = kmem_cache_create(cache_name,
  72             provider->xhat_provider_blk_size, 0, NULL, NULL,
  73             provider->xblkcache->reclaim,
  74             (void *)provider, NULL, 0);
  75         if (provider->xblkcache->cache == NULL) {
  76                 cmn_err(CE_WARN, "Failed to allocate cache for %s",
  77                     provider->xhat_provider_name);
  78                 return (-1);
  79         }
  80 
  81         mutex_init(&provider->xblkcache->lock, NULL, MUTEX_DEFAULT, NULL);
  82 
  83 
  84         /* Insert provider in the global list */
  85         rw_enter(&xhat_provider_rwlock, RW_WRITER);
  86         provider->next = xhat_provider;
  87         provider->prev = NULL;
  88         if (xhat_provider)
  89                 xhat_provider->prev = provider;
  90         xhat_provider = provider;
  91         xhat_provider->xhat_provider_refcnt = 0;
  92         rw_exit(&xhat_provider_rwlock);
  93         return (0);
  94 }
  95 
  96 
  97 
  98 int
  99 xhat_provider_unregister(xhat_provider_t *provider)
 100 {
 101         if (provider->xhat_provider_version != XHAT_PROVIDER_VERSION)
 102                 return (-1);
 103 
 104         rw_enter(&xhat_provider_rwlock, RW_WRITER);
 105 
 106         if (provider->xhat_provider_refcnt) {
 107                 rw_exit(&xhat_provider_rwlock);
 108                 return (-1);
 109         }
 110 
 111         if (provider->next)
 112                 provider->next->prev = provider->prev;
 113         if (provider->prev)
 114                 provider->prev->next = provider->next;
 115         else
 116                 xhat_provider = provider->next;
 117         provider->prev = NULL;
 118         provider->next = NULL;
 119         rw_exit(&xhat_provider_rwlock);
 120 
 121         /* Free all xblks that are sitting on free_blks list */
 122         provider->xblkcache->reclaim(provider);
 123 
 124         kmem_cache_destroy(provider->xblkcache->cache);
 125 
 126         return (0);
 127 }
 128 
 129 
 130 
 131 /* Attaches an XHAT to the address space */
 132 int
 133 xhat_attach_xhat(xhat_provider_t *provider, struct as *as,
 134     struct xhat **xhatp, void *arg)
 135 {
 136         struct xhat *xh;
 137 
 138 
 139 
 140         xh = XHAT_POPS(provider)->xhat_alloc(arg);
 141         if (xh == NULL) {
 142                 *xhatp = NULL;
 143                 return (XH_PRVDR);
 144         }
 145 
 146         mutex_init(&xh->xhat_lock, NULL, MUTEX_DEFAULT, NULL);
 147         xh->xhat_provider = provider;
 148 
 149         rw_enter(&xhat_provider_rwlock, RW_WRITER);
 150         provider->xhat_provider_refcnt++;
 151         rw_exit(&xhat_provider_rwlock);
 152 
 153         mutex_enter(&as->a_contents);
 154 
 155         /* Is address space busy (being freed, dup'd or swapped)? */
 156         if (AS_ISBUSY(as)) {
 157                 mutex_exit(&as->a_contents);
 158                 XHAT_POPS(provider)->xhat_free(xh);
 159 
 160                 rw_enter(&xhat_provider_rwlock, RW_WRITER);
 161                 provider->xhat_provider_refcnt--;
 162                 rw_exit(&xhat_provider_rwlock);
 163 
 164                 *xhatp = NULL;
 165                 return (XH_ASBUSY);
 166         }
 167 
 168         xh->xhat_as = as;
 169         xh->xhat_refcnt = 0;
 170         xh->holder = NULL;
 171         xh->arg = arg;
 172         xh->next = (struct xhat *)as->a_xhat;
 173         if (xh->next)
 174                 xh->next->prev = xh;
 175         as->a_xhat = xh;
 176         mutex_exit(&as->a_contents);
 177         *xhatp = xh;
 178         return (0);
 179 }
 180 
 181 
 182 int
 183 xhat_detach_xhat(xhat_provider_t *provider, struct as *as)
 184 {
 185         struct xhat *xh;
 186 
 187 
 188         mutex_enter(&as->a_contents);
 189 
 190         for (xh = (struct xhat *)as->a_xhat; xh != NULL; xh = xh->next)
 191                 if (xh->xhat_provider == provider) {
 192 
 193 
 194                         if (xh->holder != NULL) {
 195                                 /*
 196                                  * The address space is being freed,
 197                                  * dup'd or swapped out.
 198                                  * If we are the thread which doing one
 199                                  * of those operations, we can go ahead
 200                                  * and free up the XHAT.
 201                                  * Otherwise, return.
 202                                  */
 203                                 if (xh->holder != curthread) {
 204                                         mutex_exit(&as->a_contents);
 205                                         return (XH_ASBUSY);
 206                                 } else
 207                                         xhat_hat_rele(xh);
 208                         }
 209 
 210                         if (xh->xhat_refcnt > 0) {
 211                                 /*
 212                                  * There are still "users" of the XHAT.
 213                                  * This may be either because the caller
 214                                  * forgot to free something up (which is a bug)
 215                                  * or because xhat_op_all() is in progress.
 216                                  * Since we are not allowing any of
 217                                  * xhat_op_all's ops to call xhat_detach_xhat(),
 218                                  * This can only be some other thread. It
 219                                  * may want to wait a bit and retry.
 220                                  */
 221 
 222 
 223                                 /* Restore the hold on the XHAT */
 224                                 if (xh->holder == curthread)
 225                                         xhat_hat_hold(xh);
 226 
 227                                 mutex_exit(&as->a_contents);
 228                                 return (XH_XHHELD);
 229                         }
 230 
 231                         rw_enter(&xhat_provider_rwlock, RW_WRITER);
 232                         provider->xhat_provider_refcnt--;
 233                         rw_exit(&xhat_provider_rwlock);
 234 
 235                         if (xh->next)
 236                                 xh->next->prev = xh->prev;
 237                         if (xh->prev)
 238                                 xh->prev->next = xh->next;
 239                         else
 240                                 as->a_xhat = (void *) xh->next;
 241                         mutex_exit(&as->a_contents);
 242 
 243                         XHAT_POPS(provider)->xhat_free(xh);
 244 
 245                         return (0);
 246                 }
 247         mutex_exit(&as->a_contents);
 248         return (XH_NOTATTCHD);
 249 }
 250 
 251 void
 252 xhat_hat_hold(struct xhat *xhat)
 253 {
 254         mutex_enter(&xhat->xhat_lock);
 255         xhat->xhat_refcnt++;
 256         mutex_exit(&xhat->xhat_lock);
 257 }
 258 
 259 void
 260 xhat_hat_rele(struct xhat *xhat)
 261 {
 262         mutex_enter(&xhat->xhat_lock);
 263         xhat->xhat_refcnt--;
 264         ASSERT(xhat->xhat_refcnt >= 0);
 265         mutex_exit(&xhat->xhat_lock);
 266 }
 267 
 268 
 269 int
 270 xhat_hat_holders(struct xhat *xhat)
 271 {
 272         return (xhat->xhat_refcnt);
 273 }
 274 
 275 
 276 /*
 277  * Assumes that address space is already locked
 278  * and that AS_FREE is set for as->a_flags.
 279  */
 280 void
 281 xhat_free_start_all(struct as *as)
 282 {
 283         struct xhat *xh, *xh_nxt;
 284 
 285 
 286         ASSERT(AS_ISBUSY(as));
 287 
 288         mutex_enter(&as->a_contents);
 289         xh = (struct xhat *)as->a_xhat;
 290 
 291         /*
 292          * Simply calling xhat_hat_hold() won't work because we will
 293          * not be able to succeed in xhat_detach_xhat(), which may
 294          * get called from here. We need to know _who_ the holder is.
 295          */
 296         if (xh != NULL) {
 297                 xhat_hat_hold(xh);
 298                 ASSERT(xh->holder == NULL);
 299                 xh->holder = curthread;
 300         }
 301 
 302         while (xh != NULL) {
 303 
 304                 xh_nxt = xh->next;
 305                 if (xh_nxt != NULL) {
 306                         ASSERT(xh_nxt->holder == NULL);
 307                         xhat_hat_hold(xh_nxt);
 308                         xh_nxt->holder = curthread;
 309                 }
 310 
 311                 mutex_exit(&as->a_contents);
 312 
 313                 XHAT_FREE_START(xh);
 314 
 315                 mutex_enter(&as->a_contents);
 316 
 317                 xh = xh_nxt;
 318         }
 319 
 320         mutex_exit(&as->a_contents);
 321 }
 322 
 323 
 324 
 325 /*
 326  * Assumes that address space is already locked.
 327  * Since xhat_free_start_all() must have been called
 328  * earlier, for all XHATs holder is set to curthread.
 329  * Also, since AS_BUSY is set for as->a_flags, no new
 330  * XHATs could have been added.
 331  */
 332 void
 333 xhat_free_end_all(struct as *as)
 334 {
 335 
 336         struct xhat *xh, *xh_nxt;
 337 
 338         ASSERT(AS_ISBUSY(as));
 339 
 340         mutex_enter(&as->a_contents);
 341         xh = (struct xhat *)as->a_xhat;
 342 
 343 
 344         while (xh != NULL) {
 345 
 346                 ASSERT(xh->holder == curthread);
 347 
 348                 xh_nxt = xh->next;
 349 
 350                 mutex_exit(&as->a_contents);
 351 
 352                 XHAT_FREE_END(xh);
 353 
 354                 mutex_enter(&as->a_contents);
 355 
 356                 xh = xh_nxt;
 357         }
 358 
 359         mutex_exit(&as->a_contents);
 360 }
 361 
 362 
 363 /* Assumes that address space is already locked */
 364 
 365 /* ARGSUSED */
 366 int
 367 xhat_dup_all(struct as *as, struct as *newas, caddr_t addr, size_t len,
 368     uint_t flag)
 369 {
 370         /* This is not supported. Should we return some sort of error? */
 371 
 372         ASSERT(AS_ISBUSY(as));
 373 
 374         return (0);
 375 }
 376 
 377 
 378 /*
 379  * In the following routines, the appropriate xhat_op
 380  * should never attempt to call xhat_detach_xhat(): it will
 381  * never succeed since the XHAT is held.
 382  */
 383 
 384 
 385 #define XHAT_UNLOAD_CALLBACK_OP (0)
 386 #define XHAT_SETATTR_OP         (1)
 387 #define XHAT_CLRATTR_OP         (2)
 388 #define XHAT_CHGATTR_OP         (3)
 389 #define XHAT_CHGPROT_OP         (4)
 390 #define XHAT_UNSHARE_OP         (5)
 391 
 392 
 393 static void
 394 xhat_op_all(int op, struct as *as, caddr_t addr,
 395     size_t len, uint_t flags, void *ptr)
 396 {
 397         struct xhat *xh, *xh_nxt;
 398 
 399         mutex_enter(&as->a_contents);
 400         xh = (struct xhat *)as->a_xhat;
 401 
 402         while (xh != NULL) {
 403 
 404                 xhat_hat_hold(xh);
 405 
 406                 xh_nxt = xh->next;
 407                 if (xh_nxt != NULL)
 408                         xhat_hat_hold(xh_nxt);
 409 
 410                 mutex_exit(&as->a_contents);
 411 
 412                 switch (op) {
 413                 case XHAT_UNLOAD_CALLBACK_OP:
 414                         XHAT_UNLOAD_CALLBACK(xh, addr,
 415                             len, flags, (hat_callback_t *)ptr);
 416                         break;
 417                 case XHAT_SETATTR_OP:
 418                         XHAT_SETATTR(xh, addr, len, flags);
 419                         break;
 420                 case XHAT_CLRATTR_OP:
 421                         XHAT_CLRATTR(xh, addr, len, flags);
 422                         break;
 423                 case XHAT_CHGATTR_OP:
 424                         XHAT_CHGATTR(xh, addr, len, flags);
 425                         break;
 426                 case XHAT_CHGPROT_OP:
 427                         XHAT_CHGPROT(xh, addr, len, flags);
 428                         break;
 429                 case XHAT_UNSHARE_OP:
 430                         XHAT_UNSHARE(xh, addr, len);
 431                         break;
 432                 default:
 433                         panic("Unknown op %d in xhat_op_all", op);
 434                 }
 435 
 436                 mutex_enter(&as->a_contents);
 437 
 438                 /*
 439                  * Both pointers are still valid because both
 440                  * XHATs are held.
 441                  */
 442                 xhat_hat_rele(xh);
 443                 if (xh_nxt != NULL)
 444                         xhat_hat_rele(xh_nxt);
 445                 xh = xh_nxt;
 446         }
 447 
 448         mutex_exit(&as->a_contents);
 449 }
 450 
 451 
 452 
 453 void
 454 xhat_unload_callback_all(struct as *as, caddr_t addr, size_t len, uint_t flags,
 455     hat_callback_t *callback)
 456 {
 457         xhat_op_all(XHAT_UNLOAD_CALLBACK_OP, as, addr, len, flags, callback);
 458 }
 459 
 460 
 461 void
 462 xhat_setattr_all(struct as *as, caddr_t addr, size_t len, uint_t attr)
 463 {
 464         xhat_op_all(XHAT_SETATTR_OP, as, addr, len, attr, NULL);
 465 }
 466 
 467 
 468 
 469 void
 470 xhat_clrattr_all(struct as *as, caddr_t addr, size_t len, uint_t attr)
 471 {
 472         xhat_op_all(XHAT_CLRATTR_OP, as, addr, len, attr, NULL);
 473 }
 474 
 475 
 476 void
 477 xhat_chgattr_all(struct as *as, caddr_t addr, size_t len, uint_t attr)
 478 {
 479         xhat_op_all(XHAT_CHGATTR_OP, as, addr, len, attr, NULL);
 480 }
 481 
 482 
 483 void
 484 xhat_chgprot_all(struct as *as, caddr_t addr, size_t len, uint_t prot)
 485 {
 486         xhat_op_all(XHAT_CHGPROT_OP, as, addr, len, prot, NULL);
 487 }
 488 
 489 
 490 void
 491 xhat_unshare_all(struct as *as, caddr_t addr, size_t len)
 492 {
 493         xhat_op_all(XHAT_UNSHARE_OP, as, addr, len, 0, NULL);
 494 }