diff --git a/gdb/NEWS b/gdb/NEWS
index fd42864c692..9f3a8c783be 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -78,6 +78,13 @@
   functionality is also available for dprintf when dprintf-style is
   'gdb'.
 
+* Breakpoints can now be inferior-specific.  This is similar to the
+  existing thread-specific breakpoint support.  Breakpoint conditions
+  can include the 'inferior' keyword followed by an inferior id (as
+  displayed in the 'info inferiors' output).  It is invalid to use the
+  'inferior' keyword with either the 'thread' or 'task' keywords when
+  creating a breakpoint.
+
 * New commands
 
 maintenance print record-instruction [ N ]
@@ -138,6 +145,14 @@ info main
    considered simple.)  Support for this feature can be verified by using the
    '-list-features' command, which should contain "simple-values-ref-types".
 
+** The -break-insert command now accepts a '-g thread-group-id' option
+   to allow for the creation of inferior-specific breakpoints.
+
+** The bkpt tuple, which appears in breakpoint-created notifications,
+   and in the result of the -break-insert command can now include an
+   optional 'inferior' field for both the main breakpoint, and each
+   location, when the breakpoint is inferior-specific.
+
 * Python API
 
   ** gdb.ThreadExitedEvent added.  Emits a ThreadEvent.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index da6c8de9d14..804871ac5c7 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -98,7 +98,7 @@ static void create_breakpoints_sal (struct gdbarch *,
 				    gdb::unique_xmalloc_ptr<char>,
 				    gdb::unique_xmalloc_ptr<char>,
 				    enum bptype,
-				    enum bpdisp, int, int,
+				    enum bpdisp, int, int, int,
 				    int,
 				    int, int, int, unsigned);
 
@@ -322,6 +322,9 @@ struct momentary_breakpoint : public code_breakpoint
     disposition = disp_donttouch;
     frame_id = frame_id_;
     thread = thread_;
+
+    /* The inferior should have been set by the parent constructor.  */
+    gdb_assert (inferior == -1);
   }
 
   void re_set () override;
@@ -1462,13 +1465,15 @@ breakpoint_set_silent (struct breakpoint *b, int silent)
 void
 breakpoint_set_thread (struct breakpoint *b, int thread)
 {
-  /* It is invalid to set the thread field to anything other than -1 (which
-     means no thread restriction) if a task restriction is already in
-     place.  */
-  gdb_assert (thread == -1 || b->task == -1);
+  /* THREAD should be -1, meaning no thread restriction, or it should be a
+     valid global thread-id, which are greater than zero.  */
+  gdb_assert (thread == -1 || thread > 0);
 
-  int old_thread = b->thread;
+  /* It is not valid to set a thread restriction for a breakpoint that
+     already has task or inferior restriction.  */
+  gdb_assert (thread == -1 || (b->task == -1 && b->inferior == -1));
 
+  int old_thread = b->thread;
   b->thread = thread;
   if (old_thread != thread)
     notify_breakpoint_modified (b);
@@ -1476,16 +1481,37 @@ breakpoint_set_thread (struct breakpoint *b, int thread)
 
 /* See breakpoint.h.  */
 
+void
+breakpoint_set_inferior (struct breakpoint *b, int inferior)
+{
+  /* INFERIOR should be -1, meaning no inferior restriction, or it should
+     be a valid inferior number, which are greater than zero.  */
+  gdb_assert (inferior == -1 || inferior > 0);
+
+  /* It is not valid to set an inferior restriction for a breakpoint that
+     already has a task or thread restriction.  */
+  gdb_assert (inferior == -1 || (b->task == -1 && b->thread == -1));
+
+  int old_inferior = b->inferior;
+  b->inferior = inferior;
+  if (old_inferior != inferior)
+    gdb::observers::breakpoint_modified.notify (b);
+}
+
+/* See breakpoint.h.  */
+
 void
 breakpoint_set_task (struct breakpoint *b, int task)
 {
-  /* It is invalid to set the task field to anything other than -1 (which
-     means no task restriction) if a thread restriction is already in
-     place.  */
-  gdb_assert (task == -1 || b->thread == -1);
+  /* TASK should be -1, meaning no task restriction, or it should be a
+     valid task-id, which are greater than zero.  */
+  gdb_assert (task == -1 || task > 0);
 
-  int old_task = b->task;
+  /* It is not valid to set a task restriction for a breakpoint that
+     already has a thread or inferior restriction.  */
+  gdb_assert (task == -1 || (b->thread == -1 && b->inferior == -1));
 
+  int old_task = b->task;
   b->task = task;
   if (old_task != task)
     notify_breakpoint_modified (b);
@@ -3163,6 +3189,12 @@ insert_breakpoint_locations (void)
 	  && !valid_global_thread_id (bl->owner->thread))
 	continue;
 
+      /* Or inferior specific breakpoints if the inferior no longer
+	 exists.  */
+      if (bl->owner->inferior != -1
+	  && !valid_global_inferior_id (bl->owner->inferior))
+	continue;
+
       switch_to_program_space_and_thread (bl->pspace);
 
       /* For targets that support global breakpoints, there's no need
@@ -3263,6 +3295,29 @@ Thread-specific breakpoint %d deleted - thread %s no longer in the thread list.\
     }
 }
 
+/* Called when inferior INF has been removed from GDB.  Remove associated
+   per-inferior breakpoints.  */
+
+static void
+remove_inferior_breakpoints (struct inferior *inf)
+{
+  for (breakpoint &b : all_breakpoints_safe ())
+    {
+      if (b.inferior == inf->num && user_breakpoint_p (&b))
+	{
+	  /* Tell the user the breakpoint has been deleted.  But only for
+	     breakpoints that would not normally have been deleted at the
+	     next stop anyway.  */
+	  if (b.disposition != disp_del
+	      && b.disposition != disp_del_at_next_stop)
+	    gdb_printf (_("\
+Inferior-specific breakpoint %d deleted - inferior %d has been removed.\n"),
+			b.number, inf->num);
+	  delete_breakpoint (&b);
+       }
+    }
+}
+
 /* See breakpoint.h.  */
 
 void
@@ -5467,6 +5522,7 @@ bpstat_check_breakpoint_conditions (bpstat *bs, thread_info *thread)
      evaluating the condition if this isn't the specified
      thread/task.  */
   if ((b->thread != -1 && b->thread != thread->global_num)
+      || (b->inferior != -1 && b->inferior != thread->inf->num)
       || (b->task != -1 && b->task != ada_get_task_number (thread)))
     {
       infrun_debug_printf ("incorrect thread or task, not stopping");
@@ -6498,6 +6554,8 @@ print_one_breakpoint_location (struct breakpoint *b,
 	uiout->field_signed ("thread", b->thread);
       else if (b->task != -1)
 	uiout->field_signed ("task", b->task);
+      else if (b->inferior != -1)
+	uiout->field_signed ("inferior", b->inferior);
     }
 
   uiout->text ("\n");
@@ -6560,6 +6618,13 @@ print_one_breakpoint_location (struct breakpoint *b,
       uiout->text ("\n");
     }
 
+  if (!part_of_multiple && b->inferior != -1)
+    {
+      uiout->text ("\tstop only in inferior ");
+      uiout->field_signed ("inferior", b->inferior);
+      uiout->text ("\n");
+    }
+
   if (!part_of_multiple)
     {
       if (b->hit_count)
@@ -7535,7 +7600,10 @@ delete_longjmp_breakpoint (int thread)
     if (b.type == bp_longjmp || b.type == bp_exception)
       {
 	if (b.thread == thread)
-	  delete_breakpoint (&b);
+	  {
+	    gdb_assert (b.inferior == -1);
+	    delete_breakpoint (&b);
+	  }
       }
 }
 
@@ -7546,7 +7614,10 @@ delete_longjmp_breakpoint_at_next_stop (int thread)
     if (b.type == bp_longjmp || b.type == bp_exception)
       {
 	if (b.thread == thread)
-	  b.disposition = disp_del_at_next_stop;
+	  {
+	    gdb_assert (b.inferior == -1);
+	    b.disposition = disp_del_at_next_stop;
+	  }
       }
 }
 
@@ -7603,6 +7674,7 @@ check_longjmp_breakpoint_for_call_dummy (struct thread_info *tp)
     {
       if (b.type == bp_longjmp_call_dummy && b.thread == tp->global_num)
 	{
+	  gdb_assert (b.inferior == -1);
 	  struct breakpoint *dummy_b = b.related_breakpoint;
 
 	  /* Find the bp_call_dummy breakpoint in the list of breakpoints
@@ -8448,7 +8520,8 @@ code_breakpoint::code_breakpoint (struct gdbarch *gdbarch_,
 				  gdb::unique_xmalloc_ptr<char> cond_string_,
 				  gdb::unique_xmalloc_ptr<char> extra_string_,
 				  enum bpdisp disposition_,
-				  int thread_, int task_, int ignore_count_,
+				  int thread_, int task_, int inferior_,
+				  int ignore_count_,
 				  int from_tty,
 				  int enabled_, unsigned flags,
 				  int display_canonical_)
@@ -8472,10 +8545,14 @@ code_breakpoint::code_breakpoint (struct gdbarch *gdbarch_,
 
   gdb_assert (!sals.empty ());
 
-  /* At most one of thread or task can be set on any breakpoint.  */
-  gdb_assert (thread == -1 || task == -1);
+  /* At most one of thread, task, or inferior can be set on any breakpoint.  */
+  gdb_assert (((thread == -1 ? 0 : 1)
+	       + (task == -1 ? 0 : 1)
+	       + (inferior == -1 ? 0 : 1)) <= 1);
+
   thread = thread_;
   task = task_;
+  inferior = inferior_;
 
   cond_string = std::move (cond_string_);
   extra_string = std::move (extra_string_);
@@ -8577,7 +8654,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
 		       gdb::unique_xmalloc_ptr<char> cond_string,
 		       gdb::unique_xmalloc_ptr<char> extra_string,
 		       enum bptype type, enum bpdisp disposition,
-		       int thread, int task, int ignore_count,
+		       int thread, int task, int inferior, int ignore_count,
 		       int from_tty,
 		       int enabled, int internal, unsigned flags,
 		       int display_canonical)
@@ -8591,7 +8668,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
 				std::move (cond_string),
 				std::move (extra_string),
 				disposition,
-				thread, task, ignore_count,
+				thread, task, inferior, ignore_count,
 				from_tty,
 				enabled, flags,
 				display_canonical);
@@ -8620,7 +8697,8 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 			gdb::unique_xmalloc_ptr<char> cond_string,
 			gdb::unique_xmalloc_ptr<char> extra_string,
 			enum bptype type, enum bpdisp disposition,
-			int thread, int task, int ignore_count,
+			int thread, int task, int inferior,
+			int ignore_count,
 			int from_tty,
 			int enabled, int internal, unsigned flags)
 {
@@ -8644,7 +8722,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 			     std::move (cond_string),
 			     std::move (extra_string),
 			     type, disposition,
-			     thread, task, ignore_count,
+			     thread, task, inferior, ignore_count,
 			     from_tty, enabled, internal, flags,
 			     canonical->special_display);
     }
@@ -8774,21 +8852,26 @@ check_fast_tracepoint_sals (struct gdbarch *gdbarch,
     }
 }
 
-/* Given TOK, a string specification of condition and thread, as
-   accepted by the 'break' command, extract the condition
-   string and thread number and set *COND_STRING and *THREAD.
-   PC identifies the context at which the condition should be parsed.
-   If no condition is found, *COND_STRING is set to NULL.
-   If no thread is found, *THREAD is set to -1.  */
+/* Given TOK, a string specification of condition and thread, as accepted
+   by the 'break' command, extract the condition string into *COND_STRING.
+   If no condition string is found then *COND_STRING is set to nullptr.
+
+   If the breakpoint specification has an associated thread, task, or
+   inferior, these are extracted into *THREAD, *TASK, and *INFERIOR
+   respectively, otherwise these arguments are set to -1 (for THREAD and
+   INFERIOR) or 0 (for TASK).
+
+   PC identifies the context at which the condition should be parsed.  */
 
 static void
 find_condition_and_thread (const char *tok, CORE_ADDR pc,
 			   gdb::unique_xmalloc_ptr<char> *cond_string,
-			   int *thread, int *task,
+			   int *thread, int *inferior, int *task,
 			   gdb::unique_xmalloc_ptr<char> *rest)
 {
   cond_string->reset ();
   *thread = -1;
+  *inferior = -1;
   *task = -1;
   rest->reset ();
   bool force = false;
@@ -8805,7 +8888,7 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc,
       if ((*tok == '"' || *tok == ',') && rest)
 	{
 	  rest->reset (savestring (tok, strlen (tok)));
-	  return;
+	  break;
 	}
 
       end_tok = skip_to_space (tok);
@@ -8845,6 +8928,9 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc,
 	  if (*task != -1)
 	    error (_("You can specify only one of thread or task."));
 
+	  if (*inferior != -1)
+	    error (_("You can specify only one of inferior or thread."));
+
 	  tok = end_tok + 1;
 	  thr = parse_thread_id (tok, &tmptok);
 	  if (tok == tmptok)
@@ -8852,6 +8938,26 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc,
 	  *thread = thr->global_num;
 	  tok = tmptok;
 	}
+      else if (toklen >= 1 && strncmp (tok, "inferior", toklen) == 0)
+	{
+	  if (*inferior != -1)
+	    error(_("You can specify only one inferior."));
+
+	  if (*task != -1)
+	    error (_("You can specify only one of inferior or task."));
+
+	  if (*thread != -1)
+	    error (_("You can specify only one of inferior or thread."));
+
+	  char *tmptok;
+	  tok = end_tok + 1;
+	  *inferior = strtol (tok, &tmptok, 0);
+	  if (tok == tmptok)
+	    error (_("Junk after inferior keyword."));
+	  if (!valid_global_inferior_id (*inferior))
+	    error (_("Unknown inferior number %d."), *inferior);
+	  tok = tmptok;
+	}
       else if (toklen >= 1 && strncmp (tok, "task", toklen) == 0)
 	{
 	  char *tmptok;
@@ -8862,6 +8968,9 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc,
 	  if (*thread != -1)
 	    error (_("You can specify only one of thread or task."));
 
+	  if (*inferior != -1)
+	    error (_("You can specify only one of inferior or task."));
+
 	  tok = end_tok + 1;
 	  *task = strtol (tok, &tmptok, 0);
 	  if (tok == tmptok)
@@ -8873,7 +8982,7 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc,
       else if (rest)
 	{
 	  rest->reset (savestring (tok, strlen (tok)));
-	  return;
+	  break;
 	}
       else
 	error (_("Junk at end of arguments."));
@@ -8889,7 +8998,7 @@ static void
 find_condition_and_thread_for_sals (const std::vector<symtab_and_line> &sals,
 				    const char *input,
 				    gdb::unique_xmalloc_ptr<char> *cond_string,
-				    int *thread, int *task,
+				    int *thread, int *inferior, int *task,
 				    gdb::unique_xmalloc_ptr<char> *rest)
 {
   int num_failures = 0;
@@ -8897,6 +9006,7 @@ find_condition_and_thread_for_sals (const std::vector<symtab_and_line> &sals,
     {
       gdb::unique_xmalloc_ptr<char> cond;
       int thread_id = -1;
+      int inferior_id = -1;
       int task_id = -1;
       gdb::unique_xmalloc_ptr<char> remaining;
 
@@ -8909,11 +9019,16 @@ find_condition_and_thread_for_sals (const std::vector<symtab_and_line> &sals,
       try
 	{
 	  find_condition_and_thread (input, sal.pc, &cond, &thread_id,
-				     &task_id, &remaining);
+				     &inferior_id, &task_id, &remaining);
 	  *cond_string = std::move (cond);
-	  /* At most one of thread or task can be set.  */
-	  gdb_assert (thread_id == -1 || task_id == -1);
+	  /* A value of -1 indicates that these fields are unset.  At most
+	     one of these fields should be set (to a value other than -1)
+	     at this point.  */
+	  gdb_assert (((thread_id == -1 ? 1 : 0)
+		       + (task_id == -1 ? 1 : 0)
+		       + (inferior_id == -1 ? 1 : 0)) >= 2);
 	  *thread = thread_id;
+	  *inferior = inferior_id;
 	  *task = task_id;
 	  *rest = std::move (remaining);
 	  break;
@@ -9003,7 +9118,8 @@ int
 create_breakpoint (struct gdbarch *gdbarch,
 		   location_spec *locspec,
 		   const char *cond_string,
-		   int thread, const char *extra_string,
+		   int thread, int inferior,
+		   const char *extra_string,
 		   bool force_condition, int parse_extra,
 		   int tempflag, enum bptype type_wanted,
 		   int ignore_count,
@@ -9017,6 +9133,10 @@ create_breakpoint (struct gdbarch *gdbarch,
   int task = -1;
   int prev_bkpt_count = breakpoint_count;
 
+  gdb_assert (thread == -1 || thread > 0);
+  gdb_assert (inferior == -1 || inferior > 0);
+  gdb_assert (thread == -1 || inferior == -1);
+
   gdb_assert (ops != NULL);
 
   /* If extra_string isn't useful, set it to NULL.  */
@@ -9092,7 +9212,8 @@ create_breakpoint (struct gdbarch *gdbarch,
 	  const linespec_sals &lsal = canonical.lsals[0];
 
 	  find_condition_and_thread_for_sals (lsal.sals, extra_string,
-					      &cond, &thread, &task, &rest);
+					      &cond, &thread, &inferior,
+					      &task, &rest);
 	  cond_string_copy = std::move (cond);
 	  extra_string_copy = std::move (rest);
 	}
@@ -9142,7 +9263,7 @@ create_breakpoint (struct gdbarch *gdbarch,
 				   std::move (extra_string_copy),
 				   type_wanted,
 				   tempflag ? disp_del : disp_donttouch,
-				   thread, task, ignore_count,
+				   thread, task, inferior, ignore_count,
 				   from_tty, enabled, internal, flags);
     }
   else
@@ -9211,7 +9332,9 @@ break_command_1 (const char *arg, int flag, int from_tty)
 
   create_breakpoint (get_current_arch (),
 		     locspec.get (),
-		     NULL, 0, arg, false, 1 /* parse arg */,
+		     NULL,
+		     -1 /* thread */, -1 /* inferior */,
+		     arg, false, 1 /* parse arg */,
 		     tempflag, type_wanted,
 		     0 /* Ignore count */,
 		     pending_break_support,
@@ -9323,7 +9446,8 @@ dprintf_command (const char *arg, int from_tty)
 
   create_breakpoint (get_current_arch (),
 		     locspec.get (),
-		     NULL, 0, arg, false, 1 /* parse arg */,
+		     NULL, -1, -1,
+		     arg, false, 1 /* parse arg */,
 		     0, bp_dprintf,
 		     0 /* Ignore count */,
 		     pending_break_support,
@@ -10068,6 +10192,7 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
   const char *cond_end = NULL;
   enum bptype bp_type;
   int thread = -1;
+  int inferior = -1;
   /* Flag to indicate whether we are going to use masks for
      the hardware watchpoint.  */
   bool use_mask = false;
@@ -10122,12 +10247,13 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
 	      if (task != -1)
 		error (_("You can specify only one of thread or task."));
 
+	      if (inferior != -1)
+		error (_("You can specify only one of inferior or thread."));
+
 	      /* Extract the thread ID from the next token.  */
 	      thr = parse_thread_id (value_start, &endp);
-
-	      /* Check if the user provided a valid thread ID.  */
-	      if (*endp != ' ' && *endp != '\t' && *endp != '\0')
-		invalid_thread_id_error (value_start);
+	      if (value_start == endp)
+		error (_("Junk after thread keyword."));
 
 	      thread = thr->global_num;
 	    }
@@ -10141,12 +10267,20 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
 	      if (thread != -1)
 		error (_("You can specify only one of thread or task."));
 
+	      if (inferior != -1)
+		error (_("You can specify only one of inferior or task."));
+
 	      task = strtol (value_start, &tmp, 0);
 	      if (tmp == value_start)
 		error (_("Junk after task keyword."));
 	      if (!valid_task_id (task))
 		error (_("Unknown task %d."), task);
 	    }
+	  else if (toklen == 8 && startswith (tok, "inferior"))
+	    {
+	      /* Support for watchpoints will be added in a later commit.  */
+	      error (_("Cannot use 'inferior' keyword with watchpoints"));
+	    }
 	  else if (toklen == 4 && startswith (tok, "mask"))
 	    {
 	      /* We've found a "mask" token, which means the user wants to
@@ -10319,6 +10453,7 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
   /* At most one of thread or task can be set on a watchpoint.  */
   gdb_assert (thread == -1 || task == -1);
   w->thread = thread;
+  w->inferior = inferior;
   w->task = task;
   w->disposition = disp_donttouch;
   w->pspace = current_program_space;
@@ -12253,7 +12388,8 @@ strace_marker_create_breakpoints_sal (struct gdbarch *gdbarch,
 				      enum bptype type_wanted,
 				      enum bpdisp disposition,
 				      int thread,
-				      int task, int ignore_count,
+				      int task, int inferior,
+				      int ignore_count,
 				      int from_tty, int enabled,
 				      int internal, unsigned flags)
 {
@@ -12279,7 +12415,7 @@ strace_marker_create_breakpoints_sal (struct gdbarch *gdbarch,
 			 std::move (cond_string),
 			 std::move (extra_string),
 			 disposition,
-			 thread, task, ignore_count,
+			 thread, task, inferior, ignore_count,
 			 from_tty, enabled, flags,
 			 canonical->special_display));
 
@@ -12898,10 +13034,11 @@ code_breakpoint::location_spec_to_sals (location_spec *locspec,
       if (condition_not_parsed && extra_string != NULL)
 	{
 	  gdb::unique_xmalloc_ptr<char> local_cond, local_extra;
-	  int local_thread, local_task;
+	  int local_thread, local_task, local_inferior;
 
 	  find_condition_and_thread_for_sals (sals, extra_string.get (),
 					      &local_cond, &local_thread,
+					      &local_inferior,
 					      &local_task, &local_extra);
 	  gdb_assert (cond_string == nullptr);
 	  if (local_cond != nullptr)
@@ -13775,7 +13912,7 @@ trace_command (const char *arg, int from_tty)
 
   create_breakpoint (get_current_arch (),
 		     locspec.get (),
-		     NULL, 0, arg, false, 1 /* parse arg */,
+		     NULL, -1, -1, arg, false, 1 /* parse arg */,
 		     0 /* tempflag */,
 		     bp_tracepoint /* type_wanted */,
 		     0 /* Ignore count */,
@@ -13793,7 +13930,7 @@ ftrace_command (const char *arg, int from_tty)
 						      current_language);
   create_breakpoint (get_current_arch (),
 		     locspec.get (),
-		     NULL, 0, arg, false, 1 /* parse arg */,
+		     NULL, -1, -1, arg, false, 1 /* parse arg */,
 		     0 /* tempflag */,
 		     bp_fast_tracepoint /* type_wanted */,
 		     0 /* Ignore count */,
@@ -13831,7 +13968,7 @@ strace_command (const char *arg, int from_tty)
 
   create_breakpoint (get_current_arch (),
 		     locspec.get (),
-		     NULL, 0, arg, false, 1 /* parse arg */,
+		     NULL, -1, -1, arg, false, 1 /* parse arg */,
 		     0 /* tempflag */,
 		     type /* type_wanted */,
 		     0 /* Ignore count */,
@@ -13900,7 +14037,7 @@ create_tracepoint_from_upload (struct uploaded_tp *utp)
 						      current_language);
   if (!create_breakpoint (get_current_arch (),
 			  locspec.get (),
-			  utp->cond_string.get (), -1, addr_str,
+			  utp->cond_string.get (), -1, -1, addr_str,
 			  false /* force_condition */,
 			  0 /* parse cond/thread */,
 			  0 /* tempflag */,
@@ -14988,4 +15125,6 @@ This is useful for formatted output in user-defined commands."));
 					   "breakpoint");
   gdb::observers::thread_exit.attach (remove_threaded_breakpoints,
 				      "breakpoint");
+  gdb::observers::inferior_removed.attach (remove_inferior_breakpoints,
+					   "breakpoint");
 }
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index da150585f73..18fd90341c7 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -579,7 +579,7 @@ struct breakpoint_ops
 				  struct linespec_result *,
 				  gdb::unique_xmalloc_ptr<char>,
 				  gdb::unique_xmalloc_ptr<char>,
-				  enum bptype, enum bpdisp, int, int,
+				  enum bptype, enum bpdisp, int, int, int,
 				  int, int, int, int, unsigned);
 };
 
@@ -859,6 +859,10 @@ struct breakpoint : public intrusive_list_node<breakpoint>
      care.  */
   int thread = -1;
 
+  /* Inferior number for inferior-specific breakpoint, or -1 if this
+     breakpoint is for all inferiors.  */
+  int inferior = -1;
+
   /* Ada task number for task-specific breakpoint, or -1 if don't
      care.  */
   int task = -1;
@@ -917,7 +921,7 @@ struct code_breakpoint : public breakpoint
 		   gdb::unique_xmalloc_ptr<char> cond_string,
 		   gdb::unique_xmalloc_ptr<char> extra_string,
 		   enum bpdisp disposition,
-		   int thread, int task, int ignore_count,
+		   int thread, int task, int inferior, int ignore_count,
 		   int from_tty,
 		   int enabled, unsigned flags,
 		   int display_canonical);
@@ -1597,6 +1601,7 @@ enum breakpoint_create_flags
 extern int create_breakpoint (struct gdbarch *gdbarch,
 			      struct location_spec *locspec,
 			      const char *cond_string, int thread,
+			      int inferior,
 			      const char *extra_string,
 			      bool force_condition,
 			      int parse_extra,
@@ -1740,6 +1745,11 @@ extern void breakpoint_set_silent (struct breakpoint *b, int silent);
 
 extern void breakpoint_set_thread (struct breakpoint *b, int thread);
 
+/* Set the inferior for breakpoint B to INFERIOR.  If INFERIOR is -1, make
+   the breakpoint work for any inferior.  */
+
+extern void breakpoint_set_inferior (struct breakpoint *b, int inferior);
+
 /* Set the task for this breakpoint.  If TASK is -1, make the breakpoint
    work for any task.  Passing a value other than -1 for TASK should only
    be done if b->thread is -1; it is not valid to try and set both a thread
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b10c06ae91f..d8679181215 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3517,6 +3517,57 @@
 space as a result of inferior 1 having executed a @code{vfork} call.
 @end table
 
+@menu
+* Inferior-Specific Breakpoints::	Controlling breakpoints
+@end menu
+
+@node Inferior-Specific Breakpoints
+@subsection Inferior-Specific Breakpoints
+
+When debugging multiple inferiors, you can choose whether to set
+breakpoints for all inferiors, or for a particular inferior.
+
+@table @code
+@cindex breakpoints and inferiors
+@cindex inferior-specific breakpoints
+@kindex break @dots{} inferior @var{inferior-id}
+@item break @var{locspec} inferior @var{inferior-id}
+@itemx break @var{locspec} inferior @var{inferior-id} if @dots{}
+@var{locspec} specifies a code location or locations in your program.
+@xref{Location Specifications}, for details.
+
+Use the qualifier @samp{inferior @var{inferior-id}} with a breakpoint
+command to specify that you only want @value{GDBN} to stop when a
+particular inferior reaches this breakpoint.  The @var{inferior-id}
+specifier is one of the inferior identifiers assigned by @value{GDBN},
+shown in the first column of the @samp{info inferiors} output.
+
+If you do not specify @samp{inferior @var{inferior-id}} when you set a
+breakpoint, the breakpoint applies to @emph{all} inferiors of your
+program.
+
+You can use the @code{inferior} qualifier on conditional breakpoints as
+well; in this case, place @samp{inferior @var{inferior-id}} before or
+after the breakpoint condition, like this:
+
+@smallexample
+(@value{GDBP}) break frik.c:13 inferior 2 if bartab > lim
+@end smallexample
+@end table
+
+Inferior-specific breakpoints are automatically deleted when the
+corresponding inferior is removed from @value{GDBN}.  For example:
+
+@smallexample
+(@value{GDBP}) remove-inferiors 2
+Inferior-specific breakpoint 3 deleted - inferior 2 has been removed.
+@end smallexample
+
+A breakpoint can't be both inferior-specific and thread-specific
+(@pxref{Thread-Specific Breakpoints}), or task-specific (@pxref{Ada
+Tasks}); using more than one of the @code{inferior}, @code{thread}, or
+@code{task} keywords when creating a breakpoint will give an error.
+
 @node Threads
 @section Debugging Programs with Multiple Threads
 
@@ -4480,8 +4531,9 @@
 situation.
 
 It is also possible to insert a breakpoint that will stop the program
-only if a specific thread (@pxref{Thread-Specific Breakpoints})
-or a specific task (@pxref{Ada Tasks}) hits that breakpoint.
+only if a specific thread (@pxref{Thread-Specific Breakpoints}),
+specific inferior (@pxref{Inferior-Specific Breakpoints}), or a
+specific task (@pxref{Ada Tasks}) hits that breakpoint.
 
 @item break
 When called without any arguments, @code{break} sets a breakpoint at
@@ -7344,9 +7396,14 @@
 Process}), or if @value{GDBN} loses the remote connection
 (@pxref{Remote Debugging}), etc.  Note that with some targets,
 @value{GDBN} is only able to detect a thread has exited when the user
-explictly asks for the thread list with the @code{info threads}
+explicitly asks for the thread list with the @code{info threads}
 command.
 
+A breakpoint can't be both thread-specific and inferior-specific
+(@pxref{Inferior-Specific Breakpoints}), or task-specific (@pxref{Ada
+Tasks}); using more than one of the @code{thread}, @code{inferior}, or
+@code{task} keywords when creating a breakpoint will give an error.
+
 @node Interrupted System Calls
 @subsection Interrupted System Calls 
 
@@ -31612,6 +31669,10 @@
 If this is a thread-specific breakpoint, then this identifies the
 thread in which the breakpoint can trigger.
 
+@item inferior
+If this is an inferior-specific breakpoint, this this identifies the
+inferior in which the breakpoint can trigger.
+
 @item task
 If this breakpoint is restricted to a particular Ada task, then this
 field will hold the task identifier.
@@ -32203,7 +32264,7 @@
 @smallexample
  -break-insert [ -t ] [ -h ] [ -f ] [ -d ] [ -a ] [ --qualified ]
     [ -c @var{condition} ] [ --force-condition ] [ -i @var{ignore-count} ]
-    [ -p @var{thread-id} ] [ @var{locspec} ]
+    [ -p @var{thread-id} ] [ -g @var{thread-group-id} ] [ @var{locspec} ]
 @end smallexample
 
 @noindent
@@ -32269,6 +32330,9 @@
 time the breakpoint is requested.  Breakpoints created with a
 @var{thread-id} will automatically be deleted when the corresponding
 thread exits.
+@item -g @var{thread-group-id}
+Restrict the breakpoint to the thread group with the specified
+@var{thread-group-id}.
 @item --qualified
 This option makes @value{GDBN} interpret a function name specified as
 a complete fully-qualified name.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 9a342f34bf0..3cb93a69463 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -3430,7 +3430,10 @@
 A @code{gdb.Inferior} object has the following attributes:
 
 @defvar Inferior.num
-ID of inferior, as assigned by GDB.
+ID of inferior, as assigned by @value{GDBN}.  You can use this to make
+Python breakpoints inferior-specific, for example
+(@pxref{python_breakpoint_inferior,,The Breakpoint.inferior
+attribute}).
 @end defvar
 
 @anchor{gdbpy_inferior_connection}
@@ -6316,9 +6319,24 @@
 
 @anchor{python_breakpoint_thread}
 @defvar Breakpoint.thread
-If the breakpoint is thread-specific, this attribute holds the
-thread's global id.  If the breakpoint is not thread-specific, this
-attribute is @code{None}.  This attribute is writable.
+If the breakpoint is thread-specific (@pxref{Thread-Specific
+Breakpoints}), this attribute holds the thread's global id.  If the
+breakpoint is not thread-specific, this attribute is @code{None}.
+This attribute is writable.
+
+Only one of @code{Breakpoint.thread} or @code{Breakpoint.inferior} can
+be set to a valid id at any time, that is, a breakpoint can be thread
+specific, or inferior specific, but not both.
+@end defvar
+
+@anchor{python_breakpoint_inferior}
+@defvar Breakpoint.inferior
+If the breakpoint is inferior-specific (@pxref{Inferior-Specific
+Breakpoints}), this attribute holds the inferior's id.  If the
+breakpoint is not inferior-specific, this attribute is @code{None}.
+
+This attribute can be written for breakpoints of type
+@code{gdb.BP_BREAKPOINT} and @code{gdb.BP_HARDWARE_BREAKPOINT}.
 @end defvar
 
 @defvar Breakpoint.task
diff --git a/gdb/guile/scm-breakpoint.c b/gdb/guile/scm-breakpoint.c
index 6c6dacb3883..59254646bcc 100644
--- a/gdb/guile/scm-breakpoint.c
+++ b/gdb/guile/scm-breakpoint.c
@@ -465,7 +465,7 @@ gdbscm_register_breakpoint_x (SCM self)
 	    const breakpoint_ops *ops =
 	      breakpoint_ops_for_location_spec (locspec.get (), false);
 	    create_breakpoint (get_current_arch (),
-			       locspec.get (), NULL, -1, NULL, false,
+			       locspec.get (), NULL, -1, -1, NULL, false,
 			       0,
 			       temporary, bp_breakpoint,
 			       0,
@@ -784,6 +784,11 @@ gdbscm_set_breakpoint_thread_x (SCM self, SCM newvalue)
   else
     SCM_ASSERT_TYPE (0, newvalue, SCM_ARG2, FUNC_NAME, _("integer or #f"));
 
+  if (bp_smob->bp->inferior != -1 && id != -1)
+    scm_misc_error (FUNC_NAME,
+		    _("Cannot have both 'thread' and 'inferior' "
+		      "conditions on a breakpoint"), SCM_EOL);
+
   breakpoint_set_thread (bp_smob->bp, id);
 
   return SCM_UNSPECIFIED;
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 15702f84894..8144331bfe6 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -416,17 +416,10 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how)
   if (run_how == RUN_STOP_AT_MAIN)
     {
       /* To avoid other inferiors hitting this breakpoint, make it
-	 inferior-specific using a condition.  A better solution would be to
-	 have proper inferior-specific breakpoint support, in the breakpoint
-	 machinery.  We could then avoid inserting a breakpoint in the program
-	 spaces unrelated to this inferior.  */
-      const char *op
-	= ((current_language->la_language == language_ada
-	    || current_language->la_language == language_pascal
-	    || current_language->la_language == language_m2) ? "=" : "==");
-      std::string arg = string_printf
-	("-qualified %s if $_inferior %s %d", main_name (), op,
-	 current_inferior ()->num);
+	 inferior-specific.  */
+      std::string arg = string_printf ("-qualified %s inferior %d",
+				       main_name (),
+				       current_inferior ()->num);
       tbreak_command (arg.c_str (), 0);
     }
 
diff --git a/gdb/inferior.h b/gdb/inferior.h
index caa8e4d494a..9af0e367c6f 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -826,4 +826,15 @@ extern void print_selected_inferior (struct ui_out *uiout);
 extern void switch_to_inferior_and_push_target
   (inferior *new_inf, bool no_connection, inferior *org_inf);
 
+/* Return true if ID is a valid global inferior number.  */
+
+inline bool
+valid_global_inferior_id (int id)
+{
+  for (inferior *inf : all_inferiors ())
+    if (inf->num == id)
+      return true;
+  return false;
+}
+
 #endif /* !defined (INFERIOR_H) */
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 7d969f37fbf..695dc6515ee 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -254,9 +254,9 @@ enum linespec_token_type
 
 /* List of keywords.  This is NULL-terminated so that it can be used
    as enum completer.  */
-const char * const linespec_keywords[] = { "if", "thread", "task", "-force-condition", NULL };
+const char * const linespec_keywords[] = { "if", "thread", "task", "inferior", "-force-condition", NULL };
 #define IF_KEYWORD_INDEX 0
-#define FORCE_KEYWORD_INDEX 3
+#define FORCE_KEYWORD_INDEX 4
 
 /* A token of the linespec lexer  */
 
diff --git a/gdb/mi/mi-cmd-break.c b/gdb/mi/mi-cmd-break.c
index 0777fcbd35e..975bc1c5dde 100644
--- a/gdb/mi/mi-cmd-break.c
+++ b/gdb/mi/mi-cmd-break.c
@@ -173,6 +173,7 @@ mi_cmd_break_insert_1 (int dprintf, const char *command,
   int hardware = 0;
   int temp_p = 0;
   int thread = -1;
+  int thread_group = -1;
   int ignore_count = 0;
   const char *condition = NULL;
   int pending = 0;
@@ -191,7 +192,8 @@ mi_cmd_break_insert_1 (int dprintf, const char *command,
   enum opt
     {
       HARDWARE_OPT, TEMP_OPT, CONDITION_OPT,
-      IGNORE_COUNT_OPT, THREAD_OPT, PENDING_OPT, DISABLE_OPT,
+      IGNORE_COUNT_OPT, THREAD_OPT, THREAD_GROUP_OPT,
+      PENDING_OPT, DISABLE_OPT,
       TRACEPOINT_OPT,
       FORCE_CONDITION_OPT,
       QUALIFIED_OPT,
@@ -205,6 +207,7 @@ mi_cmd_break_insert_1 (int dprintf, const char *command,
     {"c", CONDITION_OPT, 1},
     {"i", IGNORE_COUNT_OPT, 1},
     {"p", THREAD_OPT, 1},
+    {"g", THREAD_GROUP_OPT, 1},
     {"f", PENDING_OPT, 0},
     {"d", DISABLE_OPT, 0},
     {"a", TRACEPOINT_OPT, 0},
@@ -247,6 +250,9 @@ mi_cmd_break_insert_1 (int dprintf, const char *command,
 	  if (!valid_global_thread_id (thread))
 	    error (_("Unknown thread %d."), thread);
 	  break;
+	case THREAD_GROUP_OPT:
+	  thread_group = mi_parse_thread_group_id (oarg);
+	  break;
 	case PENDING_OPT:
 	  pending = 1;
 	  break;
@@ -360,7 +366,8 @@ mi_cmd_break_insert_1 (int dprintf, const char *command,
 	error (_("Garbage '%s' at end of location"), address);
     }
 
-  create_breakpoint (get_current_arch (), locspec.get (), condition, thread,
+  create_breakpoint (get_current_arch (), locspec.get (), condition,
+		     thread, thread_group,
 		     extra_string.c_str (),
 		     force_condition,
 		     0 /* condition and thread are valid.  */,
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 9108cf505c7..402b5a7212f 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -1755,8 +1755,7 @@ mi_cmd_remove_inferior (const char *command, const char *const *argv, int argc)
   if (argc != 1)
     error (_("-remove-inferior should be passed a single argument"));
 
-  if (sscanf (argv[0], "i%d", &id) != 1)
-    error (_("the thread group id is syntactically invalid"));
+  id = mi_parse_thread_group_id (argv[0]);
 
   inf_to_remove = find_inferior_id (id);
   if (inf_to_remove == NULL)
@@ -2784,6 +2783,21 @@ mi_cmd_complete (const char *command, const char *const *argv, int argc)
 		       result.number_matches == max_completions ? "1" : "0");
 }
 
+/* See mi-main.h.  */
+int
+mi_parse_thread_group_id (const char *id)
+{
+  if (*id != 'i')
+    error (_("thread group id should start with an 'i'"));
+
+  char *end;
+  long num = strtol (id + 1, &end, 10);
+
+  if (*end != '\0' || num > INT_MAX)
+    error (_("invalid thread group id '%s'"), id);
+
+  return (int) num;
+}
 
 void _initialize_mi_main ();
 void
diff --git a/gdb/mi/mi-main.h b/gdb/mi/mi-main.h
index cb17921c150..b35544baef1 100644
--- a/gdb/mi/mi-main.h
+++ b/gdb/mi/mi-main.h
@@ -75,4 +75,10 @@ extern void mi_cmd_fix_breakpoint_script_output (const char *command,
 						 const char *const *argv,
 						 int argc);
 
+/* Parse a thread-group-id from ID, and return the integer part of the
+   ID.  A valid thread-group-id is the character 'i' followed by an
+   integer that is greater than zero.  */
+
+extern int mi_parse_thread_group_id (const char *id);
+
 #endif /* MI_MI_MAIN_H */
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index fa1570e1a04..cb064515690 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -288,11 +288,86 @@ bppy_set_thread (PyObject *self, PyObject *newvalue, void *closure)
       return -1;
     }
 
+  if (self_bp->bp->inferior != -1 && id != -1)
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Cannot have both 'thread' and 'inferior' "
+			 "conditions on a breakpoint"));
+      return -1;
+    }
+
   breakpoint_set_thread (self_bp->bp, id);
 
   return 0;
 }
 
+/* Python function to set the inferior of a breakpoint.  */
+
+static int
+bppy_set_inferior (PyObject *self, PyObject *newvalue, void *closure)
+{
+  gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
+  long id;
+
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Cannot delete 'inferior' attribute."));
+      return -1;
+    }
+  else if (PyLong_Check (newvalue))
+    {
+      if (!gdb_py_int_as_long (newvalue, &id))
+	return -1;
+
+      if (!valid_global_inferior_id (id))
+	{
+	  PyErr_SetString (PyExc_RuntimeError,
+			   _("Invalid inferior ID."));
+	  return -1;
+	}
+    }
+  else if (newvalue == Py_None)
+    id = -1;
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("The value of 'inferior' must be an integer or None."));
+      return -1;
+    }
+
+  if (self_bp->bp->type != bp_breakpoint
+      && self_bp->bp->type != bp_hardware_breakpoint)
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Cannot set 'inferior' attribute on a gdb.Breakpoint "
+			 "of this type"));
+      return -1;
+    }
+
+  if (self_bp->bp->thread != -1 && id != -1)
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Cannot have both 'thread' and 'inferior' conditions "
+			 "on a breakpoint"));
+      return -1;
+    }
+
+  if (self_bp->bp->task != -1 && id != -1)
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Cannot have both 'task' and 'inferior' conditions "
+			 "on a breakpoint"));
+      return -1;
+    }
+
+  breakpoint_set_inferior (self_bp->bp, id);
+
+  return 0;
+}
+
 /* Python function to set the (Ada) task of a breakpoint.  */
 static int
 bppy_set_task (PyObject *self, PyObject *newvalue, void *closure)
@@ -704,6 +779,20 @@ bppy_get_thread (PyObject *self, void *closure)
   return gdb_py_object_from_longest (self_bp->bp->thread).release ();
 }
 
+/* Python function to get the breakpoint's inferior ID.  */
+static PyObject *
+bppy_get_inferior (PyObject *self, void *closure)
+{
+  gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  if (self_bp->bp->inferior == -1)
+    Py_RETURN_NONE;
+
+  return gdb_py_object_from_longest (self_bp->bp->inferior).release ();
+}
+
 /* Python function to get the breakpoint's task ID (in Ada).  */
 static PyObject *
 bppy_get_task (PyObject *self, void *closure)
@@ -942,7 +1031,7 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 	      = breakpoint_ops_for_location_spec (locspec.get (), false);
 
 	    create_breakpoint (gdbpy_enter::get_gdbarch (),
-			       locspec.get (), NULL, -1, NULL, false,
+			       locspec.get (), NULL, -1, -1, NULL, false,
 			       0,
 			       temporary_bp, type,
 			       0,
@@ -1376,6 +1465,11 @@ static gdb_PyGetSetDef breakpoint_object_getset[] = {
 If the value is a thread ID (integer), then this is a thread-specific breakpoint.\n\
 If the value is None, then this breakpoint is not thread-specific.\n\
 No other type of value can be used.", NULL },
+  { "inferior", bppy_get_inferior, bppy_set_inferior,
+    "Inferior ID for the breakpoint.\n\
+If the value is an inferior ID (integer), then this is an inferior-specific\n\
+breakpoint.  If the value is None, then this breakpoint is not\n\
+inferior-specific.  No other type of value can be used.", NULL },
   { "task", bppy_get_task, bppy_set_task,
     "Thread ID for the breakpoint.\n\
 If the value is a task ID (integer), then this is an Ada task-specific breakpoint.\n\
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index b71e5fafc46..fa2139ba5d2 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -307,7 +307,7 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
       location_spec_up locspec
 	= new_address_location_spec (get_frame_pc (prev_frame), NULL, 0);
       create_breakpoint (gdbpy_enter::get_gdbarch (),
-			 locspec.get (), NULL, thread, NULL, false,
+			 locspec.get (), NULL, thread, -1, NULL, false,
 			 0,
 			 1 /*temp_flag*/,
 			 bp_breakpoint,
diff --git a/gdb/testsuite/gdb.ada/tasks.exp b/gdb/testsuite/gdb.ada/tasks.exp
index eb7ee5c9951..603d43f7c36 100644
--- a/gdb/testsuite/gdb.ada/tasks.exp
+++ b/gdb/testsuite/gdb.ada/tasks.exp
@@ -58,6 +58,8 @@ gdb_test "break break_me task 1 thread 1" \
     "You can specify only one of thread or task\\."
 gdb_test "break break_me thread 1 task 1" \
     "You can specify only one of thread or task\\."
+gdb_test "break break_me inferior 1 task 1" \
+    "You can specify only one of inferior or task\\."
 gdb_test "watch j task 1 thread 1" \
     "You can specify only one of thread or task\\."
 gdb_test "watch j thread 1 task 1" \
diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp
index fbe19b49d43..f005707b9da 100644
--- a/gdb/testsuite/gdb.linespec/cpcompletion.exp
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp
@@ -1259,8 +1259,8 @@ proc_with_prefix function-labels {} {
 }
 
 # Test that completion after a function name offers keyword
-# (if/task/thread/-force-condition) matches in linespec mode, and also
-# the explicit location options in explicit locations mode.
+# (if/inferior/task/thread/-force-condition) matches in linespec mode,
+# and also the explicit location options in explicit locations mode.
 
 proc_with_prefix keywords-after-function {} {
     set explicit_list \
diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp
index b08d65953d2..668002d9038 100644
--- a/gdb/testsuite/gdb.linespec/explicit.exp
+++ b/gdb/testsuite/gdb.linespec/explicit.exp
@@ -412,6 +412,7 @@ namespace eval $testfile {
 	    "-qualified"
 	    "-source"
 	    "if"
+	    "inferior"
 	    "task"
 	    "thread"
 	}
diff --git a/gdb/testsuite/gdb.mi/new-ui-bp-deleted.c b/gdb/testsuite/gdb.mi/new-ui-bp-deleted.c
new file mode 100644
index 00000000000..c171ef71b9d
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/new-ui-bp-deleted.c
@@ -0,0 +1,29 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+foo (void)
+{
+  return 0;
+}
+
+int
+main (void)
+{
+  int res = foo ();
+  return res;
+}
diff --git a/gdb/testsuite/gdb.mi/new-ui-bp-deleted.exp b/gdb/testsuite/gdb.mi/new-ui-bp-deleted.exp
new file mode 100644
index 00000000000..57e69ef6240
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/new-ui-bp-deleted.exp
@@ -0,0 +1,108 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Check for the delivery of '=breakpoint-deleted' notifications when
+# breakpoints are deleted.  Right now this test only covers
+# inferior-specific breakpoints, but it could be extended to cover
+# other cases too.
+
+# Multiple inferiors are needed, therefore only native gdb and
+# extended gdbserver modes are supported.
+require !use_gdb_stub
+
+# Separate UI doesn't work with GDB debug.
+require !gdb_debug_enabled
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+standard_testfile
+
+if { [build_executable "failed to prepare" $testfile $srcfile] } {
+    return -1
+}
+
+# Helper proc to create a breakpoint location regexp.  NUM is the
+# regexp to match the number field of this location.
+proc make_bp_loc { num } {
+    return [mi_make_breakpoint_loc \
+		-number "$num" \
+		-enabled "y" \
+		-func "foo" \
+		-inferior "2"]
+}
+
+foreach_mi_ui_mode mode {
+    mi_gdb_exit
+
+    if {$mode eq "separate"} {
+	set start_ops "separate-mi-tty"
+    } else {
+	set start_ops ""
+    }
+
+    if [mi_gdb_start $start_ops] {
+	return
+    }
+
+    # Load a test binary into inferior 1.
+    mi_gdb_load ${binfile}
+
+    # Setup inferior 2, including loading an exec file.
+    mi_gdb_test "-add-inferior" \
+	[multi_line "=thread-group-added,id=\"\[^\"\]+\"" \
+	     "~\"\\\[New inferior 2\\\]\\\\n\"" \
+	     "\~\"Added inferior 2\[^\r\n\]*\\\\n\"" \
+	     "\\^done,inferior=\"\[^\"\]+\"(?:,connection={.*})?" ] \
+	"mi add inferior 2"
+    mi_gdb_test "-file-exec-and-symbols --thread-group i2 $::binfile" \
+	"\\^done" \
+	"set executable of inferior 2"
+
+    # Build regexp for the two locations.
+    set loc1 [make_bp_loc "$::decimal\\.1"]
+    set loc2 [make_bp_loc "$::decimal\\.2"]
+
+    # Create the inferior-specific breakpoint.
+    mi_create_breakpoint_multi "-g i2 foo" "create breakpoint in inferior 2" \
+	-inferior "2" -locations "\\\[$loc1,$loc2\\\]"
+    set bpnum [mi_get_valueof "/d" "\$bpnum" "INVALID"]
+
+    if {$mode eq "separate"} {
+	# In 'separate' mode we delete the inferior from the CLI, and
+	# then look for the breakpoint-deleted notification on the MI.
+	with_spawn_id $gdb_main_spawn_id {
+	    gdb_test "inferior 1" ".*"
+	    gdb_test "remove-inferiors 2" \
+		"Inferior-specific breakpoint $bpnum deleted - inferior 2 has been removed\\."
+	}
+
+	gdb_test_multiple "" "check for b/p deleted notification on MI" {
+	    -re "=breakpoint-deleted,id=\"$bpnum\"" {
+		pass $gdb_test_name
+	    }
+	}
+    } else {
+	# In the non-separate mode we delete the inferior from the MI
+	# and expect to immediately see a breakpoint-deleted
+	# notification.
+	mi_gdb_test "-remove-inferior i2" \
+	    [multi_line \
+		 "=thread-group-removed,id=\"i2\"" \
+		 "~\"Inferior-specific breakpoint $bpnum deleted - inferior 2 has been removed\\.\\\\n\"" \
+		 "=breakpoint-deleted,id=\"$bpnum\"" \
+		 "\\^done"]
+    }
+}
diff --git a/gdb/testsuite/gdb.multi/inferior-specific-bp-1.c b/gdb/testsuite/gdb.multi/inferior-specific-bp-1.c
new file mode 100644
index 00000000000..8f86d8cdcea
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/inferior-specific-bp-1.c
@@ -0,0 +1,52 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+volatile int global_var = 0;
+
+static void
+stop_breakpt (void)
+{
+  /* Nothing.  */
+}
+
+static inline void __attribute__((__always_inline__))
+foo (void)
+{
+  int i;
+
+  for (i = 0; i < 10; ++i)
+    global_var = 0;
+}
+
+static void
+bar (void)
+{
+  global_var = 0;
+
+  foo ();
+}
+
+
+int
+main (void)
+{
+  global_var = 0;
+  foo ();
+  bar ();
+  stop_breakpt ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.multi/inferior-specific-bp-2.c b/gdb/testsuite/gdb.multi/inferior-specific-bp-2.c
new file mode 100644
index 00000000000..e5b20b6e7a8
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/inferior-specific-bp-2.c
@@ -0,0 +1,52 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+static int bar (void);
+static int baz (void);
+static int foo (void);
+
+static void
+stop_breakpt (void)
+{
+  /* Nothing.  */
+}
+
+int
+main (void)
+{
+  int ret = baz ();
+  stop_breakpt ();
+  return ret;
+}
+
+static int
+bar (void)
+{
+  return baz ();
+}
+
+static int
+foo (void)
+{
+  return 0;
+}
+
+static int
+baz (void)
+{
+  return foo ();
+}
diff --git a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
new file mode 100644
index 00000000000..1f6573268da
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
@@ -0,0 +1,179 @@
+# Copyright 2022-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test inferior-specific breakpoints.
+
+standard_testfile -1.c -2.c
+
+if {[use_gdb_stub]} {
+    return
+}
+
+set srcfile1 ${srcfile}
+set binfile1 ${binfile}-1
+set binfile2 ${binfile}-2
+
+if {[build_executable ${testfile}.exp ${binfile1} "${srcfile1}"] != 0} {
+    return -1
+}
+
+if {[build_executable ${testfile}.exp ${binfile2} "${srcfile2}"] != 0} {
+    return -1
+}
+
+# Start the first inferior.
+clean_restart ${binfile1}
+if {![runto_main]} {
+    return
+}
+
+# Add a second inferior, and start this one too.
+gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2"
+gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2"
+gdb_load $binfile2
+if {![runto_main]} {
+    return
+}
+
+# Try to create a breakpoint using both the 'inferior' and 'thread' keywords,
+# this should fail.  Try with the keywords in both orders just in case the
+# parser has a bug.
+gdb_test "break foo thread 1.1 inferior 1" \
+    "You can specify only one of inferior or thread\\."
+gdb_test "break foo inferior 1 thread 1.1" \
+    "You can specify only one of inferior or thread\\."
+
+# Try to create a breakpoint using the 'inferior' keyword multiple times.
+gdb_test "break foo inferior 1 inferior 2" \
+    "You can specify only one inferior\\."
+
+# Clear out any other breakpoints.
+delete_breakpoints
+
+# Use 'info breakpoint' to check that the inferior specific breakpoint is
+# present in the breakpoint list.  TESTNAME is the name used for this test,
+# BP_NUMBER is the number for the breakpoint, and EXPECTED_LOC_COUNT is the
+# number of locations we expect for that breakpoint.
+proc check_info_breakpoints { testname bp_number expected_loc_count } {
+    gdb_test_multiple "info breakpoints $bp_number" $testname {
+	-re "\r\nNum\\s+\[^\r\n\]+\r\n" {
+	    exp_continue
+	}
+
+	-re "^$bp_number\\s+breakpoint\\s+keep\\s+y\\s+<MULTIPLE>\\s*\r\n" {
+	    set saw_header true
+	    exp_continue
+	}
+
+	-re "^\\s+stop only in inferior 1\r\n" {
+	    set saw_inf_cond true
+	    exp_continue
+	}
+
+	-re "^\\s+breakpoint already hit $::decimal times\r\n" {
+	    exp_continue
+	}
+
+	-re "^$bp_number\\.\[123\]\\s+y\\s+ $::hex in foo at \[^\r\n\]+(?: inf \[12\])?\r\n" {
+	    incr location_count
+	    exp_continue
+	}
+
+	-re "^$::gdb_prompt $" {
+	    with_test_prefix $gdb_test_name {
+		gdb_assert { $saw_header \
+				 && $location_count == $expected_loc_count \
+				 && $saw_inf_cond } \
+		    $gdb_test_name
+	    }
+	}
+    }
+}
+
+# Create an inferior-specific breakpoint.  Use gdb_test instead of
+# gdb_breakpoint here as we want to check the breakpoint was placed in
+# multiple locations.
+#
+# Currently GDB still places inferior specific breakpoints into every
+# inferior, just like it does with thread specific breakpoints.
+# Hopefully this will change in the future, at which point, this test
+# will need updating.
+#
+# Two of these locations are in inferior 1, while the third is in
+# inferior 2.
+gdb_test "break foo inferior 1" \
+    "Breakpoint $decimal at $hex: foo\\. \\(3 locations\\)"
+set bp_number [get_integer_valueof "\$bpnum" "INVALID" \
+		  "get b/p number for inferior specific breakpoint"]
+
+set saw_header false
+set location_count 0
+set saw_inf_cond false
+
+check_info_breakpoints "first check for inferior specific breakpoint" \
+    $bp_number 3
+
+# Create a multi-inferior breakpoint to stop at.
+gdb_breakpoint "stop_breakpt" message
+set stop_bp_num [get_integer_valueof "\$bpnum" "INVALID" \
+		    "get b/p number for stop_breakpt"]
+
+# Now resume inferior 2, this should reach 'stop_breakpt'.
+gdb_test "continue" \
+    "hit Breakpoint $stop_bp_num\.$decimal, stop_breakpt \\(\\) .*" \
+    "continue in inferior 2"
+
+# Switch to inferior 1, and try there.
+gdb_test "inferior 1" ".*" \
+    "select inferior 1 to check the inferior-specific b/p works"
+gdb_test "continue " \
+    "Thread 1\\.${decimal}\[^\r\n\]* hit Breakpoint\
+     $bp_number\.$decimal, foo \\(\\) .*" \
+    "first continue in inferior 1"
+
+# Now back to inferior 2, let the inferior exit, and then remove the
+# inferior, the inferior-specific breakpoint should not be deleted.
+gdb_test "inferior 2" ".*" \
+    "switch back to allow inferior 2 to exit"
+gdb_test "continue" "\\\[Inferior 2 \[^\r\n\]+ exited normally\\\]" \
+    "allow inferior 2 to exit"
+gdb_test "inferior 1" ".*" \
+    "back to inferior 1 so inferior 2 can be deleted"
+gdb_test_no_output "remove-inferiors 2"
+
+gdb_test "continue " "hit Breakpoint $bp_number\.$decimal, foo \\(\\) .*" \
+    "second continue in inferior 1"
+gdb_test "continue " "hit Breakpoint $stop_bp_num, stop_breakpt \\(\\) .*" \
+    "third continue in inferior 1"
+
+# Now allow inferior 1 to exit, the inferior specific breakpoint
+# should not be deleted.
+gdb_test "continue" \
+    "\\\[Inferior 1 \[^\r\n\]+ exited normally\\\]" \
+    "allow inferior 1 to exit"
+
+check_info_breakpoints "second check for inferior specific breakpoint" \
+    $bp_number 2
+
+# Now create another new inferior, then remove inferior 1.  As a result of
+# this removal, the inferior specific breakpoint should be deleted.
+gdb_test "add-inferior" "Added inferior 3.*" "add empty inferior 3"
+gdb_test "inferior 3" "Switching to inferior 3.*" "switch to inferior 3"
+gdb_test "remove-inferiors 1" \
+    "Inferior-specific breakpoint $bp_number deleted - inferior 1 has been removed\\."
+
+# Now check 'info breakpoints' to ensure the breakpoint is gone.
+gdb_test "info breakpoints $bp_number" \
+    "No breakpoint or watchpoint matching '$bp_number'\\."
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index df17d646b28..b2c39d70c67 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -153,6 +153,8 @@ proc_with_prefix test_bkpt_basic { } {
 	"Check repr for a thread breakpoint"
     gdb_py_test_silent_cmd "python blist\[1\].thread = None" \
 	"clear breakpoint thread" 0
+    gdb_test "python print (blist\[1\].inferior)" \
+	"None" "Check breakpoint inferior"
     gdb_test "python print (blist\[1\].type == gdb.BP_BREAKPOINT)" \
 	"True" "Check breakpoint type"
     gdb_test "python print (blist\[0\].number)" \
@@ -255,6 +257,46 @@ proc_with_prefix test_bkpt_cond_and_cmds { } {
 	"check number of lines in commands"
 }
 
+# Test breakpoint thread and inferior attributes.
+proc_with_prefix test_bkpt_thread_and_inferior { } {
+    global srcfile testfile hex decimal
+
+    # Start with a fresh gdb.
+    clean_restart ${testfile}
+
+    if {![runto_main]} {
+	return 0
+    }
+
+    with_test_prefix "thread" {
+	delete_breakpoints
+	gdb_test "break multiply thread 1"
+	gdb_test "python bp = gdb.breakpoints ()\[0\]"
+	gdb_test "python print(bp.thread)" "1"
+	gdb_test "python print(bp.inferior)" "None"
+	gdb_test "python bp.inferior = 1" \
+	    "RuntimeError: Cannot have both 'thread' and 'inferior' conditions on a breakpoint.*"
+	gdb_test_no_output "python bp.thread = None"
+	gdb_test_no_output "python bp.inferior = 1" \
+	    "set the inferior now the thread has been cleared"
+	gdb_test "info breakpoints" "stop only in inferior 1\r\n.*"
+    }
+
+    with_test_prefix "inferior" {
+	delete_breakpoints
+	gdb_test "break multiply inferior 1"
+	gdb_test "python bp = gdb.breakpoints ()\[0\]"
+	gdb_test "python print(bp.thread)" "None"
+	gdb_test "python print(bp.inferior)" "1"
+	gdb_test "python bp.thread = 1" \
+	    "RuntimeError: Cannot have both 'thread' and 'inferior' conditions on a breakpoint.*"
+	gdb_test_no_output "python bp.inferior = None"
+	gdb_test_no_output "python bp.thread = 1" \
+	    "set the thread now the inferior has been cleared"
+	gdb_test "info breakpoints" "stop only in thread 1\r\n.*"
+    }
+}
+
 proc_with_prefix test_bkpt_invisible { } {
     global srcfile testfile hex decimal
 
@@ -900,6 +942,7 @@ proc_with_prefix test_bkpt_auto_disable { } {
 test_bkpt_basic
 test_bkpt_deletion
 test_bkpt_cond_and_cmds
+test_bkpt_thread_and_inferior
 test_bkpt_invisible
 test_hardware_breakpoints
 test_catchpoints
diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
index ea73c3bb367..fdc512838c3 100644
--- a/gdb/testsuite/lib/completion-support.exp
+++ b/gdb/testsuite/lib/completion-support.exp
@@ -27,7 +27,7 @@ namespace eval completion {
     # List of all quote chars, including no-quote at all.
     variable maybe_quoted_list {"" "'" "\""}
 
-    variable keyword_list {"-force-condition" "if" "task" "thread"}
+    variable keyword_list {"-force-condition" "if" "inferior" "task" "thread"}
 
     variable explicit_opts_list \
 	{"-function" "-label" "-line" "-qualified" "-source"}
diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp
index 49d5e2ef272..3cd94b03c15 100644
--- a/gdb/testsuite/lib/mi-support.exp
+++ b/gdb/testsuite/lib/mi-support.exp
@@ -2542,7 +2542,7 @@ proc mi_build_kv_pairs {attr_list {joiner ,}} {
 # locations.
 #
 # All arguments for the breakpoint location may be specified using the
-# options: number, enabled, addr, func, file, fullname, line,
+# options: number, enabled, addr, func, file, fullname, line, inferior
 # thread-groups, and thread.
 #
 # For the option -thread the corresponding output field is only added
@@ -2556,12 +2556,14 @@ proc mi_build_kv_pairs {attr_list {joiner ,}} {
 proc mi_make_breakpoint_loc {args} {
     parse_args {{number .*} {enabled .*} {addr .*}
 	{func .*} {file .*} {fullname .*} {line .*}
-	{thread-groups \\\[.*\\\]} {thread ""}}
+	{thread-groups \\\[.*\\\]} {thread ""} {inferior ""}}
 
     set attr_list {}
     foreach attr [list number enabled addr func file \
-		      fullname line thread-groups] {
-	lappend attr_list $attr [set $attr]
+		      fullname line thread-groups inferior] {
+	if {$attr ne "inferior" || [set $attr] ne ""} {
+	    lappend attr_list $attr [set $attr]
+	}
     }
 
     set result [mi_build_kv_pairs $attr_list]
@@ -2635,7 +2637,7 @@ proc mi_make_breakpoint_1 {attr_list thread cond evaluated-by times \
 # locations.
 #
 # All arguments for the breakpoint may be specified using the options:
-# number, type, disp, enabled, times, ignore, script,
+# number, type, disp, enabled, times, ignore, script, inferior,
 # original-location, cond, evaluated-by, locations, and thread.
 #
 # Only if -script and -ignore are given will they appear in the output.
@@ -2656,7 +2658,7 @@ proc mi_make_breakpoint_multi {args} {
     parse_args {{number .*} {type .*} {disp .*} {enabled .*}
 	{times .*} {ignore 0}
 	{script ""} {original-location .*} {cond ""} {evaluated-by ""}
-	{locations .*} {thread ""}}
+	{locations .*} {thread ""} {inferior ""}}
 
     set attr_list {}
     foreach attr [list number type disp enabled] {
@@ -2665,6 +2667,12 @@ proc mi_make_breakpoint_multi {args} {
 
     lappend attr_list "addr" "<MULTIPLE>"
 
+    # Only include the inferior field if it was set.  This field is
+    # optional in the MI output.
+    if {$inferior ne ""} {
+	lappend attr_list "inferior" $inferior
+    }
+
     set result [mi_make_breakpoint_1 \
 		    $attr_list $thread $cond ${evaluated-by} $times \
 		    $ignore $script ${original-location}]
